diff --git a/FroggerGame/.classpath b/FroggerGame/.classpath new file mode 100644 index 0000000..151cbe3 --- /dev/null +++ b/FroggerGame/.classpath @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/FroggerGame/.project b/FroggerGame/.project new file mode 100644 index 0000000..aa9c389 --- /dev/null +++ b/FroggerGame/.project @@ -0,0 +1,17 @@ + + + FroggerGame + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/FroggerGame/.settings/org.eclipse.jdt.core.prefs b/FroggerGame/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..838bd9d --- /dev/null +++ b/FroggerGame/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.7 diff --git a/FroggerGame/.settings/org.eclipse.ltk.core.refactoring.prefs b/FroggerGame/.settings/org.eclipse.ltk.core.refactoring.prefs new file mode 100644 index 0000000..cfcd1d3 --- /dev/null +++ b/FroggerGame/.settings/org.eclipse.ltk.core.refactoring.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false diff --git a/FroggerGame/bin/Testspiel.zip b/FroggerGame/bin/Testspiel.zip new file mode 100644 index 0000000..9f0e866 Binary files /dev/null and b/FroggerGame/bin/Testspiel.zip differ diff --git a/FroggerGame/bin/Testspiel/Auto.class b/FroggerGame/bin/Testspiel/Auto.class new file mode 100644 index 0000000..26935b4 Binary files /dev/null and b/FroggerGame/bin/Testspiel/Auto.class differ diff --git a/FroggerGame/bin/Testspiel/Frosch.class b/FroggerGame/bin/Testspiel/Frosch.class new file mode 100644 index 0000000..b0c2671 Binary files /dev/null and b/FroggerGame/bin/Testspiel/Frosch.class differ diff --git a/FroggerGame/bin/spiel/Boss.class b/FroggerGame/bin/spiel/Boss.class new file mode 100644 index 0000000..9f7e098 Binary files /dev/null and b/FroggerGame/bin/spiel/Boss.class differ diff --git a/FroggerGame/bin/spiel/NinjaFight.class b/FroggerGame/bin/spiel/NinjaFight.class new file mode 100644 index 0000000..bdda22c Binary files /dev/null and b/FroggerGame/bin/spiel/NinjaFight.class differ diff --git a/FroggerGame/bin/spiel/TestSpiel.class b/FroggerGame/bin/spiel/TestSpiel.class new file mode 100644 index 0000000..748ae01 Binary files /dev/null and b/FroggerGame/bin/spiel/TestSpiel.class differ diff --git a/GDD.docx b/GDD.docx new file mode 100644 index 0000000..87997e0 Binary files /dev/null and b/GDD.docx differ diff --git a/JnP.jar b/JnP.jar new file mode 100644 index 0000000..e1a603c Binary files /dev/null and b/JnP.jar differ diff --git a/JnP.java b/JnP.java new file mode 100644 index 0000000..5ec9102 --- /dev/null +++ b/JnP.java @@ -0,0 +1,210 @@ +package Main; + +//Importe +import org.newdawn.slick.AppGameContainer; +import org.newdawn.slick.BasicGame; +import org.newdawn.slick.GameContainer; +import org.newdawn.slick.Graphics; +import org.newdawn.slick.Input; +import org.newdawn.slick.SlickException; + +public class JnP extends BasicGame +{ + + // Main Klasse des Games + public JnP() + { + super("Jump 'n' Pong"); + } + + // Objekterstellungen + Panel panel; + + Level level; + + UserInterface hud; + + Invader invader; + + Player protector; + + State gameState; + + Menu menu; + + float waiter;// Zeit-Laufvariable + + @Override + public void init(final GameContainer gc) throws SlickException + { + // TODO Auto-generated method stub + + // Objekterstellung (außer Ball -> Erstellung in der Invader-Klasse + this.hud = new UserInterface(); + this.invader = new Invader(); + this.protector = new Player(); + this.level = new Level(); + this.gameState = new State(); + this.menu = new Menu(); + this.panel = new Panel(); + + // Initialisierung + this.gameState.init(gc); + this.menu.init(gc, this.gameState); + this.level.init(gc, this.invader, this.protector, this.hud, this.gameState, this.panel, this.waiter);// Initialisierung + // mit + // Vererbung + // aller + // Objekte + this.panel.init(gc, this.gameState, 1);// Initialisierung + Angabe des + // Startlevels + this.hud.init(gc, this.gameState); + + this.waiter = 0; // Setzung der Zeit zu Begin + } + + @Override + public void update(final GameContainer gc, final int delta) throws SlickException + { + // TODO Auto-generated method stub + final float _delta = delta / 1000f; + this.waiter += _delta; // Zeit läuft + + // Menu exit + if (gc.getInput().isKeyDown(Input.KEY_ENTER) && ((this.level.selectedString == 2) || (this.menu.selectedString == 2))) + { + gc.exit(); + } + + // Press Enter start screen + if (gc.getInput().isKeyDown(Input.KEY_ENTER) && (this.level.selectedString == 0) && (this.level.waiter > 3) && (this.gameState.stateId == 0)) + { + this.gameState.stateId = 4; + this.level.waiter = 0; + } + + // Menu startgame Option + if (gc.getInput().isKeyDown(Input.KEY_ENTER) && (this.level.waiter > 0.3) && (this.menu.selectedString == 0) && (this.gameState.stateId == 4)) + { + this.gameState.stateId = 6; + this.level.waiter = 0; + } + + // Pause Menu resume game Option + if (gc.getInput().isKeyDown(Input.KEY_ENTER) && (this.level.waiter > 3) && (this.menu.selectedString == 0) && (this.gameState.stateId == 2)) + { + this.gameState.stateId = 1; + } + + // Control Instructions Screen Timer + if ((this.gameState.stateId == 6) && (this.level.waiter > 10)) + { + this.gameState.stateId = 5; + this.level.waiter = 0; + } + + // Intro Screen Timer + if ((this.gameState.stateId == 5) && (this.level.waiter > 5)) + { + this.panel.xPos = 350; + this.gameState.stateId = 1; + this.level.waiter = 0; + + } + + // Win State Bedingung + if ((this.gameState.stateId == 1) && (this.level.currentLevel == 5) && (this.hud.sbar_width >= 100)) + { + this.level.waiter = 0; + this.gameState.stateId = 8; + } + + // Win Screen Timer + if ((this.gameState.stateId == 8) && (this.level.waiter > 5)) + { + this.gameState.stateId = 4; + this.level.currentLevel = 1; + + // Initialisierung eines "Neuen Spiels" + this.panel.init(gc, this.gameState, 1); + this.hud.init(gc, this.gameState); + this.invader.init(gc, this.gameState); + this.protector.init(gc, this.gameState, 50); + this.level.init(gc, this.invader, this.protector, this.hud, this.gameState, this.panel, _delta); + this.menu.init(gc, this.gameState); + + } + + // Pause Menu Aktivierung + if (gc.getInput().isKeyPressed(Input.KEY_P) && (this.gameState.stateId == 1)) + { + this.gameState.stateId = 2; + } + + // Game Over Bedingung + if ((this.hud.dbar_width <= 0) && (this.gameState.stateId == 1)) + { + this.level.waiter = 0; + this.gameState.stateId = 7; + + } + + // Neues Game nach Game Over + if ((this.gameState.stateId == 7) && (this.level.waiter > 10)) + { + + this.panel.init(gc, this.gameState, 1); + this.hud.init(gc, this.gameState); + this.invader.init(gc, this.gameState); + this.protector.init(gc, this.gameState, 50); + this.level.init(gc, this.invader, this.protector, this.hud, this.gameState, this.panel, _delta); + this.menu.init(gc, this.gameState); + this.level.currentLevel = 1; + this.gameState.stateId = 4; + } + // Update Aufrufe + this.gameState.update(gc, _delta); + this.level.update(gc, _delta); + this.menu.update(gc, _delta); + this.panel.update(gc, _delta); + + } + + @Override + public void render(final GameContainer gc, final Graphics g) throws SlickException + { + // TODO Auto-generated method stub + + // Renderaufrufe + this.gameState.render(gc, g); + this.level.render(gc, g); + + this.menu.render(gc, g); + this.hud.render(gc, g); + + // Titel screen Einblendung + if ((this.gameState.stateId == 0) && (this.waiter > 3)) + { + g.drawString("Press Enter", 350, 550); + } + } + + // Hauptmethode + public static void main(final String[] args) + { + try + {// Apperstellung + final AppGameContainer app = new AppGameContainer(new JnP()); + app.setDisplayMode(800, 600, false);// Fullscreen 800x600 + app.setTargetFrameRate(60);// Flüssige FPS von 60 + app.setVSync(true);// VSinc um Renderprobleme bei schneller + // Objektbewegung zu verhindern + app.setShowFPS(true); + app.start(); + } + catch (final SlickException e) + { + e.printStackTrace(); + } + } +} diff --git a/Level.java b/Level.java new file mode 100644 index 0000000..bce2005 --- /dev/null +++ b/Level.java @@ -0,0 +1,538 @@ +package Main; + +import org.newdawn.slick.Animation; +import org.newdawn.slick.Color; +import org.newdawn.slick.GameContainer; +import org.newdawn.slick.Graphics; +import org.newdawn.slick.Image; +import org.newdawn.slick.Input; +import org.newdawn.slick.SlickException; +import org.newdawn.slick.SpriteSheet; + +public class Level +{ + + private Image cyberg, title, paused, instructionScreen, missionScreen, + alert, clearedLevel; + + private Invader invader; + + private Player protector; + + private UserInterface ui; + + private State state; + + // Levelbezeichnungen + private String[] level = + { "-1- First Contact |", "-2- Two Paths to Core |", "-3- Cyberlabyrinth", "-4-", " -5- Final Fight |" }; + + // Game Over Screen Erstellung + private Image game_over_screen0, game_over_screen1, game_over_screen2, + game_over_screen3, game_over_screen4, game_over_screen5, game_over_screen6, + game_over_screen7, game_over_screen8; + + Image[] game_over_screens = + { this.game_over_screen0, this.game_over_screen1, this.game_over_screen2, this.game_over_screen3, this.game_over_screen4, this.game_over_screen5, this.game_over_screen6, this.game_over_screen7, this.game_over_screen8 }; + + int selectedString, currentLevel; + + SpriteSheet shock, arrows_sheet, walk_demo_sheet, virus_demo_sheet, + rider_demo_sheet, shock_demo_sheet; + + Animation selected, arrows, walk_demo, game_over, virus_demo, rider_demo, + shock_demo; + + Panel panel; + + float waiter;// Ingame Zeit + + boolean changedDirection = false;// Indikator für Richtungswechsel + + public void init(final GameContainer gc, final Invader invader, final Player player, final UserInterface ui, final State gameState, final Panel panel, final float waiter) throws SlickException + { + + this.game_over_screen0 = new Image("Main/cybergrid.png"); + this.game_over_screen1 = new Image("Main/game_Over1.png"); + this.game_over_screen2 = new Image("Main/game_Over2.png"); + this.game_over_screen3 = new Image("Main/game_Over3.png"); + this.game_over_screen4 = new Image("Main/game_Over4.png"); + this.game_over_screen5 = new Image("Main/game_Over5.png"); + this.game_over_screen6 = new Image("Main/game_Over6.png"); + this.game_over_screen7 = new Image("Main/game_Over7.png"); + this.game_over_screen8 = new Image("Main/game_Over8.png"); + + this.waiter = waiter; + this.panel = panel; + this.state = gameState; + this.invader = invader; + this.protector = player; + this.ui = ui; + + this.alert = new Image("Main/Hack_Alert.png"); + this.clearedLevel = new Image("Main/level_cleared_screen.png"); + this.instructionScreen = new Image("Main/key_instructions.png"); + this.missionScreen = new Image("Main/mission.png"); + this.arrows_sheet = new SpriteSheet("Main/arrows_sheet.png", 101, 101); + this.walk_demo_sheet = new SpriteSheet("Main/robot_sheet_good.png", 50, 100); + this.virus_demo_sheet = new SpriteSheet("Main/Virus_sheet.png", 72, 72); + this.rider_demo_sheet = new SpriteSheet("Main/rider.png", 75, 75); + this.shock_demo_sheet = new SpriteSheet("Main/shock.png", 75, 45); + + this.arrows = new Animation(this.arrows_sheet, 0, 0, 3, 2, true, 200, true); + this.walk_demo = new Animation(this.walk_demo_sheet, 0, 0, 5, 2, true, 200, true); + this.virus_demo = new Animation(this.virus_demo_sheet, 2, 1, 3, 4, true, 200, true); + this.rider_demo = new Animation(this.rider_demo_sheet, 0, 0, 2, 0, true, 200, true); + this.shock_demo = new Animation(this.shock_demo_sheet, 0, 0, 3, 0, true, 200, true); + + this.selectedString = 0;// Default Auswahl im Menu + this.currentLevel = 1;// Initial Level zu Begin + + this.cyberg = new Image("Main/cybergrid.png"); + this.title = new Image("Main/titel.png"); + this.paused = new Image("Main/paused_menu.png"); + this.shock = new SpriteSheet("Main/shock.png", 75, 45); + this.selected = new Animation(this.shock, 0, 0, 3, 0, true, 250, true); + + this.protector.init(gc, gameState, 100);// Roboter Initialisierung an + // YPosition 100 + invader.init(gc, gameState); + + } + + public void render(final GameContainer gc, final Graphics g) throws SlickException + { + + // Rendering abhängig von State + switch (this.state.currentState) + { + + case "win":// Sieges Screen + g.drawImage(this.clearedLevel, 0, 0); + g.setColor(Color.cyan); + g.drawLine(0, 100, 800, 100); + break; + // game over Pseudo-Animation + case "gameOver": + if (this.waiter > 5) + { + g.drawImage(this.game_over_screen8, 0, 0); + } + else if (this.waiter > 4.2) + { + g.drawImage(this.game_over_screen7, 0, 0); + } + else if (this.waiter > 3.8) + { + g.drawImage(this.game_over_screen6, 0, 0); + } + else if (this.waiter > 3.2) + { + g.drawImage(this.game_over_screen5, 0, 0); + } + else if (this.waiter > 2.8) + { + g.drawImage(this.game_over_screen4, 0, 0); + } + else if (this.waiter > 2.2) + { + g.drawImage(this.game_over_screen3, 0, 0); + } + else if (this.waiter > 1.8) + { + g.drawImage(this.game_over_screen2, 0, 0); + } + else if (this.waiter > 1.2) + { + g.drawImage(this.game_over_screen1, 0, 0); + } + else + { + g.drawImage(this.game_over_screen0, 0, 0); + } + break; + + // Steuerungs Screens + case "controlInstructions": + if (this.waiter < 5) + { + g.drawImage(this.instructionScreen, 0, 0); + this.arrows.draw(150, 200); + this.walk_demo.draw(550, 200); + + } + else if (this.waiter > 6) + {// "Missions screen" + g.drawImage(this.missionScreen, 0, 0); + this.walk_demo.draw(550, 200); + this.virus_demo.draw(550, 300); + this.rider_demo.draw(550, 430); + this.shock_demo.draw(550, 460); + } + break; + + case "level_intro": + // Level intro je nach currentLevel + switch (this.currentLevel) + { + case 1: + this.alert.draw(0, 0); + g.setColor(Color.red); + g.drawLine(0, 200, 800, 200); + g.drawLine(0, 400, 800, 400); + g.setColor(Color.white); + g.drawString("1", 300, 330); + g.drawString("-First Contact-", 330, 330); + break; + case 2: + this.alert.draw(0, 0); + g.setColor(Color.red); + g.drawLine(0, 200, 800, 200); + g.drawLine(0, 400, 800, 400); + g.setColor(Color.white); + g.drawString("2", 300, 330); + g.drawString("-Two Paths to the Core-", 330, 330); + break; + case 3: + this.alert.draw(0, 0); + g.setColor(Color.red); + g.drawLine(0, 200, 800, 200); + g.drawLine(0, 400, 800, 400); + g.setColor(Color.white); + g.drawString("3", 300, 330); + g.drawString("-The Cyberlabyrinth-", 330, 330); + break; + case 4: + this.alert.draw(0, 0); + g.setColor(Color.red); + g.drawLine(0, 200, 800, 200); + g.drawLine(0, 400, 800, 400); + g.setColor(Color.white); + g.drawString("4", 300, 330); + g.drawString("-The prelast level-", 330, 330); + break; + case 5: + this.alert.draw(0, 0); + g.setColor(Color.red); + g.drawLine(0, 200, 800, 200); + g.drawLine(0, 400, 800, 400); + g.setColor(Color.white); + g.drawString("5", 300, 330); + g.drawString("-Final Fight-", 330, 330); + break; + } + break; + + case "titel": + g.drawImage(this.title, 0, 0); + break; + case "paused": + this.cyberg.draw(0, 0, 800, 600); + this.panel.render(gc, g); + this.protector.render(gc, g); + this.invader.render(gc, g); + + g.setColor(Color.magenta); + // Pause Menü Tags + this.paused.draw(225, 250); + g.drawString("Resume Game", 425, 290); + g.drawString("Options(Not Implemented)", 425, 320); + g.drawString("Exit Game", 425, 350); + // Name des Levels in der UI + switch (this.currentLevel) + { + case 1: + g.drawString(this.level[0], 30, 555); + break; + case 2: + g.drawString(this.level[1], 30, 555); + break; + case 3: + g.drawString(this.level[2], 30, 555); + break; + case 4: + g.drawString(this.level[3], 30, 555); + break; + case 5: + g.drawString(this.level[4], 30, 555); + break; + } + + switch (this.selectedString) + { + case 0: + this.selected.draw(325, 280); + break; + case 1: + this.selected.draw(325, 310); + break; + case 2: + this.selected.draw(325, 340); + break; + } + break; + case "start": + this.cyberg.draw(0, 0, 800, 600); + this.panel.render(gc, g); + this.protector.render(gc, g); + this.invader.render(gc, g); + + g.setColor(Color.red); + g.drawLine(0, gc.getHeight() - 50, 800, gc.getHeight() - 50);// Systemlebenslinie + g.setColor(Color.magenta); + + switch (this.currentLevel) + { + case 1: + g.drawString(this.level[0], 30, 555); + break; + case 2: + g.drawString(this.level[1], 30, 555); + break; + case 3: + g.drawString(this.level[2], 30, 555); + break; + case 4: + g.drawString(this.level[3], 30, 555); + break; + case 5: + g.drawString(this.level[4], 30, 555); + break; + } + + break; + } + } + + public void update(final GameContainer gc, final float _delta) throws SlickException + { + this.waiter += _delta;// Ingame Zeit + + switch (this.state.currentState) + { + + case "paused": + // Kontrolle im Menü + if (gc.getInput().isKeyPressed(Input.KEY_UP) && (this.selectedString > 0)) + { + this.selectedString--; + } + if (gc.getInput().isKeyPressed(Input.KEY_DOWN) && (this.selectedString < 2)) + { + this.selectedString++; + } + break; + + case "start": + this.protector.update(gc, _delta); + this.invader.update(gc, _delta); + + if (!this.invader.isParalized && (this.invader.para_timer >= 5) && (this.currentLevel == 5)) + { + // invader movement in final level// + if (((this.invader.riderX + 75) > (this.panel.xPos + (this.panel.xSize * 15))) && (this.changedDirection == false)) + { + this.invader.flightSpdX *= -1; + this.invader.fly_right = false; + this.changedDirection = true; + } + + if (((this.invader.riderX - 75) < (this.panel.xPos + (this.panel.xSize * 1))) && (this.changedDirection == false)) + { + this.invader.flightSpdX *= -1; + this.invader.fly_right = true; + this.changedDirection = true; + } + + if ((this.invader.riderX < (this.panel.xPos + (this.panel.xSize * 9))) && (this.invader.riderX > (this.panel.xPos + (this.panel.xSize * 6))) && (this.changedDirection == true)) + { + this.changedDirection = false; + } + + // Ball Bewegung im letzten level + if ((this.invader.ball.xPos - 36) > (this.panel.xPos + (this.panel.xSize * 15))) + { + this.invader.ball.collisionManager("x_inverted"); + } + + if ((this.invader.ball.xPos + 36) < (this.panel.xPos + (this.panel.xSize * 1))) + { + this.invader.ball.collisionManager("x_inverted"); + } + + } + + // Level ende und wechsel zum nächsten Level + if ((this.panel.xPos <= -4350) && this.protector.isLanded) + { + this.currentLevel++; + this.waiter = 0; + this.state.stateId = 5; + this.panel.init(gc, this.state, this.currentLevel); + this.protector.init(gc, this.state, -100); + this.invader.init(gc, this.state); + this.ui.sbar_width = 0; + this.invader.isParalized = false; + this.panel.xPos = 350; + } + + // Roboter - Panel kollision + for (int i = 1; i < this.panel.collisionarea.length; i++) + { + if (this.panel.collisionarea[i].intersects(this.protector.getCollisionarea()) && !this.protector.hasFloorContact) + { + this.protector.ySpd = 0; + this.protector.setFloorContact(true); + this.protector.isLanded = true; + + } + else + { + this.protector.hasFloorContact = false; + } + + // einsetzen der "Gravitation" + if (this.panel.collisionarea[i].intersects(this.protector.getCollisionarea()) && !this.protector.hasFloorContact) + { + + this.protector.ySpd *= 0; + + } + } + + // Roboter Sprung + if (gc.getInput().isKeyDown(Input.KEY_UP) && this.protector.isLanded) + { + if (this.protector.isRunningRight) + { + + this.protector.move = 3; + } + else if (this.protector.isRunningLeft) + { + + this.protector.move = 4; + } + else + { + this.protector.move = 0; + } + this.protector.hasFloorContact = false; + this.protector.ySpd = -400; + this.protector.isLanded = false; + + } + + // Ball fliegt oben aus dem Screen + if ((this.invader.ball.yPos < 0) && !this.invader.ball.ballIsKilled) + { + this.invader.ball.killBall(gc); + this.protector.ballhit = false; + + } + // Kollision Ball - Hacker + if ((this.invader.ball.yPos < (this.invader.riderY + 75)) && (this.invader.ball.yPos > this.invader.riderY) && (this.invader.ball.xPos < (this.invader.riderX + 75)) && (this.invader.ball.xPos > this.invader.riderX)) + { + this.invader.ball.killBall(gc); + this.ui.sbar_width += 20; + this.protector.ballhit = false; + + } + + // Disconnected Erzeugung + if ((this.ui.sbar_width >= 100) && !this.invader.isParalized) + { + this.invader.isParalized = true; + this.invader.paralized(); + + } + + // ReConect des Hackers + if ((this.invader.para_timer >= 5) && (this.ui.sbar_width >= 100)) + { + this.ui.sbar_width -= 100; + this.invader.isParalized = false; + } + + // Ball Kollidiert mit Systemlebenslinie + if ((this.invader.ball.yPos >= (gc.getHeight() - 50))) + { + + this.invader.ball.killBall(gc); + this.protector.ballhit = false; + + if (this.ui.dbar_width > 0) + { + this.ui.dbar_width -= 20; + } + } + + // Ball - Roboter Kollision + if ((this.invader.ball.yPos >= (this.protector.yPos - 50)) && (this.invader.ball.xPos >= (this.protector.xPos - 25)) && (this.invader.ball.xPos <= (this.protector.xPos + 25)) && (this.invader.ball.yPos <= (this.protector.yPos + 50)) && !this.protector.ballhit) + { + + this.invader.ball.collisionManager("y_inverted"); + this.protector.ballhit = true; + } + + // roboter rechtsbewegung + if (gc.getInput().isKeyDown(Input.KEY_RIGHT)) + { + if (this.protector.isLanded) + { + this.protector.move = 1; + } + else + { + this.protector.move = 3; + } + this.panel.xSpd = -200; + this.invader.ball.xPos -= 3; + if (!this.invader.isParalized) + { + this.invader.riderX -= 3; + } + + } + else + // roboter Linksbewegung + if (gc.getInput().isKeyDown(Input.KEY_LEFT)) + { + if (this.protector.isLanded) + { + this.protector.move = 2; + } + else + { + this.protector.move = 4; + } + this.panel.xSpd = 200; + this.invader.ball.xPos += 3; + if (!this.invader.isParalized) + { + this.invader.riderX += 3; + } + + } + + else + {// roboter stand bewegung + this.protector.move = 0; + this.panel.xSpd = 0; + } + + // roboter fliegt unten durch den Screen + if (this.protector.yPos > 800) + { + this.protector.init(gc, this.state, -100); + this.panel.xPos = 350; + this.invader.init(gc, this.state); + this.invader.ball.killBall(gc); + this.ui.sbar_width = 0; + this.ui.dbar_width -= 20; + this.invader.isParalized = false; + } + break; + } + } +} diff --git a/Main/Hack_Alert.png b/Main/Hack_Alert.png new file mode 100644 index 0000000..5d728ab Binary files /dev/null and b/Main/Hack_Alert.png differ diff --git a/Main/Panel.png b/Main/Panel.png new file mode 100644 index 0000000..6aeb197 Binary files /dev/null and b/Main/Panel.png differ diff --git a/Main/Robot_sheet.png b/Main/Robot_sheet.png new file mode 100644 index 0000000..df23405 Binary files /dev/null and b/Main/Robot_sheet.png differ diff --git a/Main/Robot_sheet1.png b/Main/Robot_sheet1.png new file mode 100644 index 0000000..258c4be Binary files /dev/null and b/Main/Robot_sheet1.png differ diff --git a/Main/Virus_sheet.png b/Main/Virus_sheet.png new file mode 100644 index 0000000..eba9e0f Binary files /dev/null and b/Main/Virus_sheet.png differ diff --git a/Main/arrows_sheet.png b/Main/arrows_sheet.png new file mode 100644 index 0000000..bcb5f72 Binary files /dev/null and b/Main/arrows_sheet.png differ diff --git a/Main/cybergrid.png b/Main/cybergrid.png new file mode 100644 index 0000000..0af39f7 Binary files /dev/null and b/Main/cybergrid.png differ diff --git a/Main/destruction_bar.png b/Main/destruction_bar.png new file mode 100644 index 0000000..74dff5b Binary files /dev/null and b/Main/destruction_bar.png differ diff --git a/Main/empty_bar.png b/Main/empty_bar.png new file mode 100644 index 0000000..67358e1 Binary files /dev/null and b/Main/empty_bar.png differ diff --git a/Main/game_Over1.png b/Main/game_Over1.png new file mode 100644 index 0000000..6187bfe Binary files /dev/null and b/Main/game_Over1.png differ diff --git a/Main/game_Over2.png b/Main/game_Over2.png new file mode 100644 index 0000000..3a6d994 Binary files /dev/null and b/Main/game_Over2.png differ diff --git a/Main/game_Over3.png b/Main/game_Over3.png new file mode 100644 index 0000000..c7cadee Binary files /dev/null and b/Main/game_Over3.png differ diff --git a/Main/game_Over4.png b/Main/game_Over4.png new file mode 100644 index 0000000..c9cd823 Binary files /dev/null and b/Main/game_Over4.png differ diff --git a/Main/game_Over5.png b/Main/game_Over5.png new file mode 100644 index 0000000..6e027e1 Binary files /dev/null and b/Main/game_Over5.png differ diff --git a/Main/game_Over6.png b/Main/game_Over6.png new file mode 100644 index 0000000..f649b89 Binary files /dev/null and b/Main/game_Over6.png differ diff --git a/Main/game_Over7.png b/Main/game_Over7.png new file mode 100644 index 0000000..9ae010e Binary files /dev/null and b/Main/game_Over7.png differ diff --git a/Main/game_Over8.png b/Main/game_Over8.png new file mode 100644 index 0000000..9bad6ca Binary files /dev/null and b/Main/game_Over8.png differ diff --git a/Main/key_instructions.png b/Main/key_instructions.png new file mode 100644 index 0000000..e34dc44 Binary files /dev/null and b/Main/key_instructions.png differ diff --git a/Main/level_cleared_screen.png b/Main/level_cleared_screen.png new file mode 100644 index 0000000..721d3d2 Binary files /dev/null and b/Main/level_cleared_screen.png differ diff --git a/Main/mechrider.png b/Main/mechrider.png new file mode 100644 index 0000000..06b7ee8 Binary files /dev/null and b/Main/mechrider.png differ diff --git a/Main/menu.png b/Main/menu.png new file mode 100644 index 0000000..4c13c33 Binary files /dev/null and b/Main/menu.png differ diff --git a/Main/mission.png b/Main/mission.png new file mode 100644 index 0000000..2e51d10 Binary files /dev/null and b/Main/mission.png differ diff --git a/Main/paused_menu.png b/Main/paused_menu.png new file mode 100644 index 0000000..1c5dc9e Binary files /dev/null and b/Main/paused_menu.png differ diff --git a/Main/rider.png b/Main/rider.png new file mode 100644 index 0000000..b57c440 Binary files /dev/null and b/Main/rider.png differ diff --git a/Main/robot_sheet_good.png b/Main/robot_sheet_good.png new file mode 100644 index 0000000..ae06686 Binary files /dev/null and b/Main/robot_sheet_good.png differ diff --git a/Main/shield_bar.png b/Main/shield_bar.png new file mode 100644 index 0000000..c3c4904 Binary files /dev/null and b/Main/shield_bar.png differ diff --git a/Main/shock.png b/Main/shock.png new file mode 100644 index 0000000..6949fae Binary files /dev/null and b/Main/shock.png differ diff --git a/Main/titel.png b/Main/titel.png new file mode 100644 index 0000000..808034f Binary files /dev/null and b/Main/titel.png differ diff --git a/OpenAL32.dll b/OpenAL32.dll new file mode 100644 index 0000000..b6f6f65 Binary files /dev/null and b/OpenAL32.dll differ diff --git a/OpenAL64.dll b/OpenAL64.dll new file mode 100644 index 0000000..d835da5 Binary files /dev/null and b/OpenAL64.dll differ diff --git a/Original Package/Main/Ball.java b/Original Package/Main/Ball.java new file mode 100644 index 0000000..0c40a9c --- /dev/null +++ b/Original Package/Main/Ball.java @@ -0,0 +1,72 @@ +package Main; + +import org.newdawn.slick.Animation; +import org.newdawn.slick.GameContainer; +import org.newdawn.slick.Graphics; +import org.newdawn.slick.SlickException; +import org.newdawn.slick.SpriteSheet; + +public class Ball { + + SpriteSheet virus_sheet; + Animation virus; + + int xPos,yPos; + float xSpd,ySpd; + boolean isColliding = false,ballIsKilled = true; + //initial methode mit Koordinatenbestimmung und startrichtung + void init (GameContainer gc,int xPos, int yPos, int invaderSpd) throws SlickException{ + + this.xPos = xPos; + this.yPos = yPos; + + this.xSpd = 1.0f * invaderSpd;//initiierung mit Hacker flugrichtungsindikation + this.ySpd = 1.0f; + + virus_sheet = new SpriteSheet ("Main/Virus_sheet.png", 72, 72); + virus = new Animation(virus_sheet,2,1,3,4,true,200,true); + + + } + + + void render(GameContainer gc, Graphics g) throws SlickException{ + + virus.draw(xPos-36, yPos-36); + + } + + + void update(GameContainer gc, float _delta)throws SlickException{ + + this.xPos += xSpd; + this.yPos += ySpd; + + } + + public void collisionManager(String direction){//Verwaltet Apprallrichtung und intensität + + + switch(direction){ + + + case "x_inverted" :this.xSpd *= -1; break; + case "y_inverted" :this.ySpd *= -1; break; + case "rebounce" :this.xSpd *= -1; this.ySpd *= -1;break; + + case "left" :this.xSpd *= -1; this.ySpd *= -1;break; + case "midleft" :this.xSpd *= 2; this.ySpd *= -1;break; + case "middle" :this.ySpd *= -1; break; + case "midright" :this.xSpd *= 2; this.ySpd *= -1;break; + case "right" :this.xSpd *= -1; this.ySpd *= -1;break; + } + + } + //ball "zerstörung" + public void killBall(GameContainer gc) throws SlickException{ + init(gc, -50,350, 1); + ballIsKilled=true; + } + + +} diff --git a/Original Package/Main/Hack_Alert.png b/Original Package/Main/Hack_Alert.png new file mode 100644 index 0000000..5d728ab Binary files /dev/null and b/Original Package/Main/Hack_Alert.png differ diff --git a/Original Package/Main/Invader.java b/Original Package/Main/Invader.java new file mode 100644 index 0000000..f8574a3 --- /dev/null +++ b/Original Package/Main/Invader.java @@ -0,0 +1,140 @@ +package Main; + +import org.newdawn.slick.Animation; +import org.newdawn.slick.GameContainer; +import org.newdawn.slick.Graphics; +import org.newdawn.slick.SlickException; +import org.newdawn.slick.SpriteSheet; + +public class Invader { + + int riderX, riderY,flightSpdX,flightSpdY; + SpriteSheet rider,shocking; + Animation flight_left, flight_right,shock; + String flight_direction;//Flugrichtung + + float countDown,para_timer; + boolean readyToFire,//Check für die Abschuss automatik + ball_shoot,//Check ob Ball abgefeuert wurde + fly_right,//Check ob Flugrichtung rechts + isParalized;//Disconnected check + + Ball ball;//Definierung eines Ball objektes + private State state; + + void init(GameContainer gc, State gameState) throws SlickException{ + + + this.state = gameState; + riderX = 450; + riderY = 30; + flightSpdX = 1; + flightSpdY = 1; + flight_direction = "right"; + fly_right = true; + ball_shoot= false; + isParalized = false; + + rider = new SpriteSheet("Main/rider.png", 75, 75); + shocking = new SpriteSheet("Main/shock.png", 75, 45); + + flight_left = new Animation(rider,0,1,2,1,true,250,true); + flight_right = new Animation(rider,0,0,2,0,true,250,true); + shock = new Animation(shocking,0,0,3,0,true,250,true); + + countDown = 3.1f; + para_timer = 5.1f; + readyToFire = false; + + ball = new Ball();//erstellung des Ballobjektes + + + } + + + void render(GameContainer gc, Graphics g) throws SlickException{ + switch(state.currentState){ + case "titel":break; + case "start": case "paused": + if(fly_right)//rechtsflug animation + flight_right.draw(riderX, riderY); + else//linksflug animation + flight_left.draw(riderX, riderY); + + //ball wird nur gerendert falls er abgeschossen wurde + if(ball_shoot){ + ball.render(gc, g); + } + + //shock animation des Disconnected Status + if(para_timer < 5){ + shock.draw(riderX, riderY+20); + } + + + break; + } + } + + + void update(GameContainer gc, float _delta) throws SlickException{ + + + switch(state.currentState){ + case "paused":break; + case "start" : + + countDown += _delta; + para_timer += _delta; + + + //flug nur bei Connected Status + if(!isParalized&& para_timer >=5 ){ + + riderX += flightSpdX; + + //Abschuss automatik + if(readyToFire == true && countDown > 3 ){ + ball.init(gc, riderX+37, riderY+80, flightSpdX); + ball_shoot = true; + readyToFire= false; + } + + + } + + + //ball bewegung + if(ball_shoot&&!ball.ballIsKilled) + ball.update(gc, _delta ); + //ball wird bei zerstörung für den schuss vorbereitet + if(ball.ballIsKilled){ + prepareToFire(); + ball.ballIsKilled =false; + } + + gc.getInput().clearKeyPressedRecord();//Tastendruck löschung zur Sicherheit + + + + + + break; + } + } + + + + //Abschussmethode + void prepareToFire(){ + countDown = 0; + readyToFire = true; + ball_shoot = false; + } + //Disconnected Methode + void paralized(){ + para_timer = 0; + + } + +} diff --git a/Original Package/Main/JnP.java b/Original Package/Main/JnP.java new file mode 100644 index 0000000..7c35300 --- /dev/null +++ b/Original Package/Main/JnP.java @@ -0,0 +1,175 @@ +package Main; +//Importe +import org.newdawn.slick.AppGameContainer; +import org.newdawn.slick.BasicGame; +import org.newdawn.slick.GameContainer; +import org.newdawn.slick.Graphics; +import org.newdawn.slick.Input;//für Tastennutzung +import org.newdawn.slick.SlickException; + +public class JnP extends BasicGame { + + //Main Klasse des Games + public JnP() { + super("Jump 'n' Pong"); + } + //Objekterstellungen + Panel panel; + Level level; + UserInterface hud; + Invader invader; + Player protector; + State gameState; + Menu menu; + + float waiter;//Zeit-Laufvariable + + public void init(GameContainer gc) throws SlickException { + // TODO Auto-generated method stub + + //Objekterstellung (außer Ball -> Erstellung in der Invader-Klasse + hud = new UserInterface(); + invader = new Invader(); + protector = new Player(); + level = new Level(); + gameState = new State(); + menu = new Menu(); + panel = new Panel(); + + //Initialisierung + gameState.init(gc); + menu.init(gc, gameState); + level.init(gc,invader,protector,hud,gameState,panel,waiter);//Initialisierung mit Vererbung aller Objekte + panel.init(gc,gameState,1);// Initialisierung + Angabe des Startlevels + hud.init(gc,gameState); + + + waiter = 0; //Setzung der Zeit zu Begin + } +@Override + + public void update(GameContainer gc, int delta) throws SlickException { + // TODO Auto-generated method stub + float _delta = delta /1000f; + this.waiter += _delta; //Zeit läuft + + //Menu exit + if(gc.getInput().isKeyDown(Input.KEY_ENTER) && (level.selectedString == 2||menu.selectedString == 2)) + gc.exit(); + + // Press Enter start screen + if(gc.getInput().isKeyDown(Input.KEY_ENTER)&& (level.selectedString == 0)&& level.waiter >3 && gameState.stateId==0){ + gameState.stateId = 4;level.waiter = 0; + } + + //Menu startgame Option + if(gc.getInput().isKeyDown(Input.KEY_ENTER)&&level.waiter >0.3 && (menu.selectedString == 0) && gameState.stateId==4){ + gameState.stateId = 6;level.waiter = 0; + } + + //Pause Menu resume game Option + if(gc.getInput().isKeyDown(Input.KEY_ENTER)&&level.waiter >3 && (menu.selectedString == 0) && gameState.stateId==2){ + gameState.stateId = 1; + } + + //Control Instructions Screen Timer + if(gameState.stateId == 6 && level.waiter >10){ + gameState.stateId = 5; + level.waiter = 0; + } + + //Intro Screen Timer + if(gameState.stateId == 5 && level.waiter > 5){ + panel.xPos = 350; + gameState.stateId = 1; + level.waiter = 0; + + } + + //Win State Bedingung + if(gameState.stateId == 1 && level.currentLevel == 5 && hud.sbar_width >=100){ + level.waiter = 0 ; + gameState.stateId = 8; + } + + //Win Screen Timer + if(gameState.stateId == 8 && level.waiter > 5){ + gameState.stateId = 4; + level.currentLevel = 1; + + //Initialisierung eines "Neuen Spiels" + panel.init(gc, gameState, 1); + hud.init(gc, gameState); + invader.init(gc, gameState); + protector.init(gc, gameState, 50); + level.init(gc, invader, protector, hud, gameState, panel, _delta); + menu.init(gc, gameState); + + } + + //Pause Menu Aktivierung + if(gc.getInput().isKeyPressed(Input.KEY_P )&& (gameState.stateId == 1)) + gameState.stateId = 2; + + //Game Over Bedingung + if(hud.dbar_width<=0 && gameState.stateId==1){ + level.waiter = 0; + gameState.stateId = 7; + + } + + //Neues Game nach Game Over + if(gameState.stateId == 7 &&level.waiter > 10){ + + panel.init(gc, gameState, 1); + hud.init(gc, gameState); + invader.init(gc, gameState); + protector.init(gc, gameState, 50); + level.init(gc, invader, protector, hud, gameState, panel, _delta); + menu.init(gc, gameState); + level.currentLevel = 1; + gameState.stateId = 4; + } + //Update Aufrufe + gameState.update(gc, _delta); + level.update(gc, _delta); + menu.update(gc, _delta); + panel.update(gc, _delta); + + + } + @Override + public void render(GameContainer gc, Graphics g) throws SlickException { + // TODO Auto-generated method stub + + //Renderaufrufe + gameState.render(gc, g); + level.render(gc, g); + + menu.render(gc, g); + hud.render(gc, g); + + //Titel screen Einblendung + if(gameState.stateId == 0 && waiter >3){ + g.drawString("Press Enter", 350, 550); + } + } + + + + + //Hauptmethode + public static void main(String[] args) { + try {//Apperstellung + AppGameContainer app = new AppGameContainer(new JnP()); + app.setDisplayMode(800, 600, true);//Fullscreen 800x600 + app.setTargetFrameRate(60);//Flüssige FPS von 60 + app.setVSync(true);//VSinc um Renderprobleme bei schneller Objektbewegung zu verhindern + app.setShowFPS(false); + app.start(); + } + catch (SlickException e) { + e.printStackTrace(); + } + } +} diff --git a/Original Package/Main/Level.java b/Original Package/Main/Level.java new file mode 100644 index 0000000..64ac6ae --- /dev/null +++ b/Original Package/Main/Level.java @@ -0,0 +1,455 @@ +package Main; + + +import org.newdawn.slick.Animation; +import org.newdawn.slick.Color; +import org.newdawn.slick.GameContainer; +import org.newdawn.slick.Graphics; +import org.newdawn.slick.Image; +import org.newdawn.slick.Input; +import org.newdawn.slick.SlickException; +import org.newdawn.slick.SpriteSheet; + + + + +public class Level{ + + + private Image cyberg,title,paused,instructionScreen,missionScreen,alert,clearedLevel; + private Invader invader; + private Player protector; + private UserInterface ui; + private State state; + + //Levelbezeichnungen + private String[] level = {"-1- First Contact |", "-2- Two Paths to Core |", "-3- Cyberlabyrinth", "-4-", " -5- Final Fight |"}; + + //Game Over Screen Erstellung + private Image game_over_screen0,game_over_screen1,game_over_screen2,game_over_screen3,game_over_screen4,game_over_screen5,game_over_screen6,game_over_screen7,game_over_screen8; + Image[] game_over_screens = { game_over_screen0, + game_over_screen1, + game_over_screen2, + game_over_screen3, + game_over_screen4, + game_over_screen5, + game_over_screen6, + game_over_screen7, + game_over_screen8}; + + + int selectedString,currentLevel; + SpriteSheet shock,arrows_sheet,walk_demo_sheet,virus_demo_sheet,rider_demo_sheet,shock_demo_sheet; + Animation selected,arrows,walk_demo,game_over,virus_demo,rider_demo,shock_demo; + Panel panel; + + float waiter;//Ingame Zeit + + boolean changedDirection = false;//Indikator für Richtungswechsel + + public void init(GameContainer gc, Invader invader, Player player, UserInterface ui, State gameState, Panel panel, float waiter) throws SlickException{ + + game_over_screen0 = new Image("Main/cybergrid.png"); + game_over_screen1 = new Image("Main/game_Over1.png"); + game_over_screen2 = new Image("Main/game_Over2.png"); + game_over_screen3 = new Image("Main/game_Over3.png"); + game_over_screen4 = new Image("Main/game_Over4.png"); + game_over_screen5 = new Image("Main/game_Over5.png"); + game_over_screen6 = new Image("Main/game_Over6.png"); + game_over_screen7 = new Image("Main/game_Over7.png"); + game_over_screen8 = new Image("Main/game_Over8.png"); + + + this.waiter = waiter; + this.panel = panel; + this.state = gameState; + this.invader = invader; + this.protector = player; + this.ui = ui; + + + alert = new Image("Main/Hack_Alert.png"); + clearedLevel = new Image("Main/level_cleared_screen.png"); + instructionScreen = new Image("Main/key_instructions.png"); + missionScreen = new Image("Main/mission.png"); + arrows_sheet = new SpriteSheet("Main/arrows_sheet.png" ,101, 101); + walk_demo_sheet = new SpriteSheet("Main/robot_sheet_good.png",50,100); + virus_demo_sheet = new SpriteSheet("Main/Virus_sheet.png",72,72); + rider_demo_sheet = new SpriteSheet("Main/rider.png",75,75); + shock_demo_sheet = new SpriteSheet("Main/shock.png",75,45); + + arrows = new Animation(arrows_sheet,0,0,3,2,true,200, true); + walk_demo = new Animation(walk_demo_sheet,0,0,5,2,true,200,true); + virus_demo = new Animation(virus_demo_sheet,2,1,3,4,true,200,true); + rider_demo = new Animation(rider_demo_sheet,0,0,2,0,true,200,true); + shock_demo = new Animation(shock_demo_sheet,0,0,3,0,true,200,true); + + selectedString = 0;//Default Auswahl im Menu + currentLevel = 1;//Initial Level zu Begin + + cyberg = new Image("Main/cybergrid.png"); + title = new Image("Main/titel.png"); + paused = new Image("Main/paused_menu.png"); + shock = new SpriteSheet("Main/shock.png", 75, 45); + selected = new Animation(shock,0,0,3,0,true,250,true); + + protector.init(gc,gameState,100);//Roboter Initialisierung an YPosition 100 + invader.init(gc,gameState); + + } + + public void render (GameContainer gc, Graphics g)throws SlickException{ + + + //Rendering abhängig von State + switch(state.currentState){ + + case "win" ://Sieges Screen + g.drawImage(clearedLevel, 0, 0); + g.setColor(Color.cyan); + g.drawLine(0, 100, 800, 100); + break; + //game over Pseudo-Animation + case "gameOver": + if(waiter > 5) + g.drawImage(game_over_screen8, 0, 0); + else if ( waiter > 4.2) + g.drawImage(game_over_screen7, 0, 0); + else if(waiter > 3.8) + g.drawImage(game_over_screen6, 0, 0); + else if(waiter > 3.2) + g.drawImage(game_over_screen5, 0, 0); + else if(waiter > 2.8) + g.drawImage(game_over_screen4, 0, 0); + else if(waiter > 2.2) + g.drawImage(game_over_screen3, 0, 0); + else if(waiter > 1.8) + g.drawImage(game_over_screen2, 0, 0); + else if(waiter > 1.2) + g.drawImage(game_over_screen1, 0, 0); + else + g.drawImage(game_over_screen0, 0, 0); + break; + + //Steuerungs Screens + case "controlInstructions": + if(waiter < 5){ + g.drawImage(instructionScreen, 0, 0); + arrows.draw(150, 200); + walk_demo.draw(550, 200); + + } + else if(waiter >6){//"Missions screen" + g.drawImage(missionScreen, 0, 0); + walk_demo.draw(550, 200); + virus_demo.draw(550, 300); + rider_demo.draw(550, 430); + shock_demo.draw(550, 460); + } + break; + + case "level_intro": + //Level intro je nach currentLevel + switch(currentLevel){ + case 1:alert.draw(0, 0); + g.setColor(Color.red); + g.drawLine(0, 200, 800, 200); + g.drawLine(0, 400, 800, 400); + g.setColor(Color.white); + g.drawString("1", 300, 330); + g.drawString("-First Contact-", 330, 330); + break; + case 2:alert.draw(0, 0); + g.setColor(Color.red); + g.drawLine(0, 200, 800, 200); + g.drawLine(0, 400, 800, 400); + g.setColor(Color.white); + g.drawString("2", 300, 330); + g.drawString("-Two Paths to the Core-", 330, 330); + break; + case 3:alert.draw(0, 0); + g.setColor(Color.red); + g.drawLine(0, 200, 800, 200); + g.drawLine(0, 400, 800, 400); + g.setColor(Color.white); + g.drawString("3", 300, 330); + g.drawString("-The Cyberlabyrinth-", 330, 330); + break; + case 4:alert.draw(0, 0); + g.setColor(Color.red); + g.drawLine(0, 200, 800, 200); + g.drawLine(0, 400, 800, 400); + g.setColor(Color.white); + g.drawString("4", 300, 330); + g.drawString("-The prelast level-", 330, 330); + break; + case 5:alert.draw(0, 0); + g.setColor(Color.red); + g.drawLine(0, 200, 800, 200); + g.drawLine(0, 400, 800, 400); + g.setColor(Color.white); + g.drawString("5", 300, 330); + g.drawString("-Final Fight-", 330, 330); + break; + } + break; + + case "titel": + g.drawImage(title, 0, 0);break; + case"paused": + cyberg.draw(0, 0, 800, 600); + panel.render(gc,g); + protector.render(gc, g); + invader.render(gc, g); + + g.setColor(Color.magenta); + //Pause Menü Tags + paused.draw(225, 250); + g.drawString("Resume Game", 425, 290); + g.drawString("Options(Not Implemented)", 425, 320); + g.drawString("Exit Game", 425, 350); + //Name des Levels in der UI + switch(currentLevel){ + case 1:g.drawString(level[0], 30, 555);break; + case 2:g.drawString(level[1], 30, 555);break; + case 3:g.drawString(level[2], 30, 555);break; + case 4:g.drawString(level[3], 30, 555);break; + case 5:g.drawString(level[4], 30, 555);break; + } + + switch(selectedString){ + case 0: selected.draw(325, 280);break; + case 1: selected.draw(325, 310);break; + case 2: selected.draw(325, 340);break; + } + break; + case"start": + cyberg.draw(0, 0, 800, 600); + panel.render(gc,g); + protector.render(gc, g); + invader.render(gc, g); + + g.setColor(Color.red); + g.drawLine(0, gc.getHeight()-50, 800, gc.getHeight()-50);//Systemlebenslinie + g.setColor(Color.magenta); + + + switch(currentLevel){ + case 1:g.drawString(level[0], 30, 555);break; + case 2:g.drawString(level[1], 30, 555);break; + case 3:g.drawString(level[2], 30, 555);break; + case 4:g.drawString(level[3], 30, 555);break; + case 5:g.drawString(level[4], 30, 555);break; + } + + + break; + } + } + + public void update (GameContainer gc,float _delta)throws SlickException{ + this.waiter += _delta;//Ingame Zeit + + switch(state.currentState){ + + case "paused": + //Kontrolle im Menü + if(gc.getInput().isKeyPressed(Input.KEY_UP)&& selectedString >0) + selectedString--; + if(gc.getInput().isKeyPressed(Input.KEY_DOWN)&& selectedString <2) + selectedString++; + break; + + case "start": + protector.update(gc, _delta); + invader.update(gc, _delta); + + + + if(!invader.isParalized&& invader.para_timer >=5 && currentLevel == 5){ + //invader movement in final level// + if(invader.riderX+75 > panel.xPos+panel.xSize*15 && changedDirection == false){ + invader.flightSpdX *= -1; + invader.fly_right = false; + changedDirection = true; + } + + + if(invader.riderX -75 < panel.xPos+panel.xSize*1 && changedDirection == false){ + invader.flightSpdX *= -1; + invader.fly_right = true; + changedDirection = true; + } + + + if(invader.riderX < panel.xPos+panel.xSize*9 && + invader.riderX > panel.xPos+panel.xSize*6 && changedDirection == true) + changedDirection = false; + + //Ball Bewegung im letzten level + if(invader.ball.xPos-36 > panel.xPos+panel.xSize*15) + invader.ball.collisionManager("x_inverted"); + + if(invader.ball.xPos+36 < panel.xPos+panel.xSize*1) + invader.ball.collisionManager("x_inverted"); + + + } + + + //Level ende und wechsel zum nächsten Level + if(panel.xPos <= -4350 && protector.isLanded){ + currentLevel++; + waiter = 0; + state.stateId = 5; + panel.init(gc, state, currentLevel); + protector.init(gc, state, -100); + invader.init(gc, state); + ui.sbar_width = 0; + invader.isParalized = false; + panel.xPos = 350; + } + + + + //Roboter - Panel kollision + for(int i = 1; i< panel.collisionarea.length;i++){ + if(panel.collisionarea[i].intersects(protector.getCollisionarea())&&!protector.hasFloorContact){ + protector.ySpd = 0; + protector.setFloorContact(true); + protector.isLanded = true; + + } else + protector.hasFloorContact = false; + + //einsetzen der "Gravitation" + if(panel.collisionarea[i].intersects(protector.getCollisionarea())&& !protector.hasFloorContact){ + + protector.ySpd *=0; + + } + } + + //Roboter Sprung + if(gc.getInput().isKeyDown(Input.KEY_UP)&& protector.isLanded) + { + if(protector.isRunningRight){ + + protector.move = 3; + } + else + if(protector.isRunningLeft){ + + protector.move = 4; + } + else + protector.move = 0; + protector.hasFloorContact = false; + protector.ySpd = -400; + protector.isLanded = false; + + } + + //Ball fliegt oben aus dem Screen + if(invader.ball.yPos < 0&& !invader.ball.ballIsKilled){ + invader.ball.killBall(gc); + protector.ballhit =false; + + } + //Kollision Ball - Hacker + if(invader.ball.yPos < invader.riderY + 75 && + invader.ball.yPos > invader.riderY && + invader.ball.xPos < invader.riderX + 75 && + invader.ball.xPos > invader.riderX ){ + invader.ball.killBall(gc); + ui.sbar_width += 20; + protector.ballhit=false; + + } + + //Disconnected Erzeugung + if(ui.sbar_width >= 100 && !invader.isParalized){ + invader.isParalized = true; + invader.paralized(); + + } + + //ReConect des Hackers + if(invader.para_timer >= 5 && ui.sbar_width >= 100){ + ui.sbar_width -=100; + invader.isParalized = false; + } + + + // Ball Kollidiert mit Systemlebenslinie + if( (invader.ball.yPos >= gc.getHeight()-50)){ + + invader.ball.killBall(gc); + protector.ballhit=false; + + + if(ui.dbar_width >0) + ui.dbar_width -= 20; + } + + + + //Ball - Roboter Kollision + if(invader.ball.yPos >= protector.yPos -50 && + invader.ball.xPos >= protector.xPos -25&& + invader.ball.xPos <= protector.xPos + 25&& + invader.ball.yPos <= protector.yPos +50&& + !protector.ballhit){ + + invader.ball.collisionManager("y_inverted"); + protector.ballhit= true; + } + + //roboter rechtsbewegung + if(gc.getInput().isKeyDown(Input.KEY_RIGHT)){ + if(protector.isLanded)protector.move = 1; + else + protector.move = 3; + panel.xSpd=-200; + invader.ball.xPos-=3; + if(!invader.isParalized) + invader.riderX -=3; + + + } + else + //roboter Linksbewegung + if(gc.getInput().isKeyDown(Input.KEY_LEFT)){ + if(protector.isLanded)protector.move = 2; + else + protector.move = 4; + panel.xSpd=200; + invader.ball.xPos+=3; + if(!invader.isParalized) + invader.riderX +=3; + + } + + else + {//roboter stand bewegung + protector.move = 0; + panel.xSpd=0; + } + + //roboter fliegt unten durch den Screen + if(protector.yPos > 800){ + protector.init(gc, state,-100 ); + panel.xPos = 350; + invader.init(gc, state); + invader.ball.killBall(gc); + ui.sbar_width = 0; + ui.dbar_width -=20 ; + invader.isParalized = false; + } + break; + } + } +} + + + diff --git a/Original Package/Main/Menu.java b/Original Package/Main/Menu.java new file mode 100644 index 0000000..dbe1388 --- /dev/null +++ b/Original Package/Main/Menu.java @@ -0,0 +1,70 @@ +package Main; + +import org.newdawn.slick.Animation; +import org.newdawn.slick.Color; +import org.newdawn.slick.GameContainer; +import org.newdawn.slick.Graphics; +import org.newdawn.slick.Image; +import org.newdawn.slick.Input; +import org.newdawn.slick.SlickException; +import org.newdawn.slick.SpriteSheet; + +public class Menu { + + Image menu; + State state; + String selectedOption; + int selectedString; + SpriteSheet shocking; + Animation selected; + + + public void init(GameContainer gc, State state) throws SlickException{ + this.state= state; + + selectedString = 0; + menu = new Image ("Main/menu.png"); + shocking = new SpriteSheet("Main/shock.png", 75, 45); + selected = new Animation(shocking,0,0,3,0,true,250,true);//Auswahl im Menu + } + + public void render(GameContainer gc, Graphics g)throws SlickException{ + g.setColor(Color.magenta); + + + + if(state.currentState == "menu"){ + menu.draw(0, 0); + g.scale(2, 2); + + switch(selectedString){ + case 0: selected.draw(100, 70);break; + case 1: selected.draw(100, 130);break; + case 2: selected.draw(100, 190);break; + + }//Menu auswahl punkte + g.drawString("Start Game", 150, 80); + g.drawString("Options Game", 150, 140); + g.drawString("Exit Game", 150, 200); + g.scale(0.5f, 0.5f); + } + } + + public void update(GameContainer gc, float _delta)throws SlickException{ + + switch(state.stateId){ + case 0: case 1:break; + case 4: switch(selectedString){ + case 0: selectedOption = "Start Game";selectedString = 0;break; + case 1: selectedOption = "Options";selectedString = 1;break; + case 2: selectedOption = "Exit Game";selectedString = 2;break; + } + if(gc.getInput().isKeyPressed(Input.KEY_UP)&& selectedString >0) + selectedString--; + if(gc.getInput().isKeyPressed(Input.KEY_DOWN)&& selectedString <2) + selectedString++; + + break; + } + } +} diff --git a/Original Package/Main/Panel.java b/Original Package/Main/Panel.java new file mode 100644 index 0000000..c086ff9 --- /dev/null +++ b/Original Package/Main/Panel.java @@ -0,0 +1,238 @@ +package Main; + +import org.newdawn.slick.Color; +import org.newdawn.slick.GameContainer; +import org.newdawn.slick.Graphics; +import org.newdawn.slick.Image; +import org.newdawn.slick.SlickException; +import org.newdawn.slick.geom.Rectangle; + +public class Panel { + + Image panel; + int i ; + int xPos,xSize,yPos,xSpd; + + //Level Modelierungen + int[] yPos_level1= {0,12,12,11,11,10,10, 9, 9,-1,-1, + -1,-1, 9, 9, 9,10,10,11,11,12, + 12,-1,-1,11,11,-1,-1,10,10,-1, + -1, 9, 9,-1,-1,-1, 9, 9,-1,-1, + -1,-1, 9, 9, 9,-1,-1,-1,-1,-1, + 11,11,-1,-1,10,10,12,12, 7, 7, + 7,-1,-1,-1,-1,-1, 8, 8, 7, 7, + -1,-1,-1,-1,-1,-1,11,11,-1,-1, + -1,-1,-1,12,12,10,10,-1,-1,-1, + -1,-1,11,11,12,12,12,12,12}; + + int[] yPos_level2= {0, 9, 9, 9,-1,-1, 8,10, 5,12, 5, + 12, 9, 9, 9,-1, 5,12,-1,-1, 5, + -1,12,-1,-1,-1,-1,11,11,11,-1, + -1,-1,-1,10,10,10,-1,-1,-1,-1, + 9, 9, 9,-1,12,12,12,-1, 8, 8, + 8,-1,12,12,-1, 7, 7, 7,12,12, + 12,12, 6, 6, 6,-1,12,12,-1, 5, + 5, 5,-1,-1,-1,-1,-1,-1,-1,12, + 12,12,12,12,12,-1,-1,-1,10,10, + 10,-1,-1,-1, 9, 9,10,10,10}; + + int[] yPos_level3= {0, 7, 7, 7,11,11,11,-1,-1, 8, 8, + 8,-1,-1,-1, 5, 5, 5,-1,-1, 8, + -1,-1,-1,-1,12,-1, 6,-1,-1, 9, + -1, 5, 5, 6, 6, 8, 8, 8, 9, 9, + -1,-1,-1, 6,12,-1, 9,-1,-1, 5, + 5, 5, 9, 7, 6,12,-1,-1,-1,11, + 6,12,12, 9, 5, 5,-1,-1,-1,-1, + -1,-1,-1, 9, 9, 9, 9,-1,-1,-1, + -1,12,12,-1, 6,12, 5,10,-1,-1, + -1, 5, 7, 7, 8, 8, 8, 8, 8}; + + int[] yPos_level4= {0,12,12,11,11,10,10,11,11,12,12, + 11,12,12,11,11,12,12,10,10,19, + 19,19,10,11,11,12,12,12,12,12, + 12,11,11,11,10,10,10, 8,11, 8, + 11,10,10,10, 9, 9, 8, 4,11, 4, + 11, 4,11, 8, 8, 8, 4,11, 4,11, + 4,12, 8, 8, 9, 9, 9,10,10,10, + 11,11,11,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12}; + + int[] yPos_finalLevel= {0,12, 5,-1, 9,-1,-1,12, 5,-1,-1, 9,-1, 5,12}; + + State state; + Rectangle[] collisionarea;// Kollisionsvierecke für die Roboter-Panel Kollisionen + int currentLevel; + + public void init(GameContainer gc, State state,int currentLevel) throws SlickException { + // TODO Auto-generated method stub + this.state = state; + this.currentLevel = currentLevel; + panel = new Image ("Main/panel.png"); + //initialwerte + xSize = 50;//breite eines Panels + yPos = 40;// höhe eines Panels + xPos = 350;//Initial Position des ersten Panels + xSpd = 0; + + //initiierung der einzelnen Kollisionsvierecke der jeweiligen Levels + switch(currentLevel){ + case 1: + collisionarea = new Rectangle[yPos_level1.length]; + + for(int i = 1; i=5 ){ + + riderX += flightSpdX; + + //Abschuss automatik + if(readyToFire == true && countDown > 3 ){ + ball.init(gc, riderX+37, riderY+80, flightSpdX); + ball_shoot = true; + readyToFire= false; + } + + + } + + + //ball bewegung + if(ball_shoot&&!ball.ballIsKilled) + ball.update(gc, _delta ); + //ball wird bei zerstörung für den schuss vorbereitet + if(ball.ballIsKilled){ + prepareToFire(); + ball.ballIsKilled =false; + } + + gc.getInput().clearKeyPressedRecord();//Tastendruck löschung zur Sicherheit + + + + + + break; + } + } + + + + //Abschussmethode + void prepareToFire(){ + countDown = 0; + readyToFire = true; + ball_shoot = false; + } + //Disconnected Methode + void paralized(){ + para_timer = 0; + + } + +} diff --git a/QuellCode in txt/JnP.txt b/QuellCode in txt/JnP.txt new file mode 100644 index 0000000..7c35300 --- /dev/null +++ b/QuellCode in txt/JnP.txt @@ -0,0 +1,175 @@ +package Main; +//Importe +import org.newdawn.slick.AppGameContainer; +import org.newdawn.slick.BasicGame; +import org.newdawn.slick.GameContainer; +import org.newdawn.slick.Graphics; +import org.newdawn.slick.Input;//für Tastennutzung +import org.newdawn.slick.SlickException; + +public class JnP extends BasicGame { + + //Main Klasse des Games + public JnP() { + super("Jump 'n' Pong"); + } + //Objekterstellungen + Panel panel; + Level level; + UserInterface hud; + Invader invader; + Player protector; + State gameState; + Menu menu; + + float waiter;//Zeit-Laufvariable + + public void init(GameContainer gc) throws SlickException { + // TODO Auto-generated method stub + + //Objekterstellung (außer Ball -> Erstellung in der Invader-Klasse + hud = new UserInterface(); + invader = new Invader(); + protector = new Player(); + level = new Level(); + gameState = new State(); + menu = new Menu(); + panel = new Panel(); + + //Initialisierung + gameState.init(gc); + menu.init(gc, gameState); + level.init(gc,invader,protector,hud,gameState,panel,waiter);//Initialisierung mit Vererbung aller Objekte + panel.init(gc,gameState,1);// Initialisierung + Angabe des Startlevels + hud.init(gc,gameState); + + + waiter = 0; //Setzung der Zeit zu Begin + } +@Override + + public void update(GameContainer gc, int delta) throws SlickException { + // TODO Auto-generated method stub + float _delta = delta /1000f; + this.waiter += _delta; //Zeit läuft + + //Menu exit + if(gc.getInput().isKeyDown(Input.KEY_ENTER) && (level.selectedString == 2||menu.selectedString == 2)) + gc.exit(); + + // Press Enter start screen + if(gc.getInput().isKeyDown(Input.KEY_ENTER)&& (level.selectedString == 0)&& level.waiter >3 && gameState.stateId==0){ + gameState.stateId = 4;level.waiter = 0; + } + + //Menu startgame Option + if(gc.getInput().isKeyDown(Input.KEY_ENTER)&&level.waiter >0.3 && (menu.selectedString == 0) && gameState.stateId==4){ + gameState.stateId = 6;level.waiter = 0; + } + + //Pause Menu resume game Option + if(gc.getInput().isKeyDown(Input.KEY_ENTER)&&level.waiter >3 && (menu.selectedString == 0) && gameState.stateId==2){ + gameState.stateId = 1; + } + + //Control Instructions Screen Timer + if(gameState.stateId == 6 && level.waiter >10){ + gameState.stateId = 5; + level.waiter = 0; + } + + //Intro Screen Timer + if(gameState.stateId == 5 && level.waiter > 5){ + panel.xPos = 350; + gameState.stateId = 1; + level.waiter = 0; + + } + + //Win State Bedingung + if(gameState.stateId == 1 && level.currentLevel == 5 && hud.sbar_width >=100){ + level.waiter = 0 ; + gameState.stateId = 8; + } + + //Win Screen Timer + if(gameState.stateId == 8 && level.waiter > 5){ + gameState.stateId = 4; + level.currentLevel = 1; + + //Initialisierung eines "Neuen Spiels" + panel.init(gc, gameState, 1); + hud.init(gc, gameState); + invader.init(gc, gameState); + protector.init(gc, gameState, 50); + level.init(gc, invader, protector, hud, gameState, panel, _delta); + menu.init(gc, gameState); + + } + + //Pause Menu Aktivierung + if(gc.getInput().isKeyPressed(Input.KEY_P )&& (gameState.stateId == 1)) + gameState.stateId = 2; + + //Game Over Bedingung + if(hud.dbar_width<=0 && gameState.stateId==1){ + level.waiter = 0; + gameState.stateId = 7; + + } + + //Neues Game nach Game Over + if(gameState.stateId == 7 &&level.waiter > 10){ + + panel.init(gc, gameState, 1); + hud.init(gc, gameState); + invader.init(gc, gameState); + protector.init(gc, gameState, 50); + level.init(gc, invader, protector, hud, gameState, panel, _delta); + menu.init(gc, gameState); + level.currentLevel = 1; + gameState.stateId = 4; + } + //Update Aufrufe + gameState.update(gc, _delta); + level.update(gc, _delta); + menu.update(gc, _delta); + panel.update(gc, _delta); + + + } + @Override + public void render(GameContainer gc, Graphics g) throws SlickException { + // TODO Auto-generated method stub + + //Renderaufrufe + gameState.render(gc, g); + level.render(gc, g); + + menu.render(gc, g); + hud.render(gc, g); + + //Titel screen Einblendung + if(gameState.stateId == 0 && waiter >3){ + g.drawString("Press Enter", 350, 550); + } + } + + + + + //Hauptmethode + public static void main(String[] args) { + try {//Apperstellung + AppGameContainer app = new AppGameContainer(new JnP()); + app.setDisplayMode(800, 600, true);//Fullscreen 800x600 + app.setTargetFrameRate(60);//Flüssige FPS von 60 + app.setVSync(true);//VSinc um Renderprobleme bei schneller Objektbewegung zu verhindern + app.setShowFPS(false); + app.start(); + } + catch (SlickException e) { + e.printStackTrace(); + } + } +} diff --git a/QuellCode in txt/Level.txt b/QuellCode in txt/Level.txt new file mode 100644 index 0000000..64ac6ae --- /dev/null +++ b/QuellCode in txt/Level.txt @@ -0,0 +1,455 @@ +package Main; + + +import org.newdawn.slick.Animation; +import org.newdawn.slick.Color; +import org.newdawn.slick.GameContainer; +import org.newdawn.slick.Graphics; +import org.newdawn.slick.Image; +import org.newdawn.slick.Input; +import org.newdawn.slick.SlickException; +import org.newdawn.slick.SpriteSheet; + + + + +public class Level{ + + + private Image cyberg,title,paused,instructionScreen,missionScreen,alert,clearedLevel; + private Invader invader; + private Player protector; + private UserInterface ui; + private State state; + + //Levelbezeichnungen + private String[] level = {"-1- First Contact |", "-2- Two Paths to Core |", "-3- Cyberlabyrinth", "-4-", " -5- Final Fight |"}; + + //Game Over Screen Erstellung + private Image game_over_screen0,game_over_screen1,game_over_screen2,game_over_screen3,game_over_screen4,game_over_screen5,game_over_screen6,game_over_screen7,game_over_screen8; + Image[] game_over_screens = { game_over_screen0, + game_over_screen1, + game_over_screen2, + game_over_screen3, + game_over_screen4, + game_over_screen5, + game_over_screen6, + game_over_screen7, + game_over_screen8}; + + + int selectedString,currentLevel; + SpriteSheet shock,arrows_sheet,walk_demo_sheet,virus_demo_sheet,rider_demo_sheet,shock_demo_sheet; + Animation selected,arrows,walk_demo,game_over,virus_demo,rider_demo,shock_demo; + Panel panel; + + float waiter;//Ingame Zeit + + boolean changedDirection = false;//Indikator für Richtungswechsel + + public void init(GameContainer gc, Invader invader, Player player, UserInterface ui, State gameState, Panel panel, float waiter) throws SlickException{ + + game_over_screen0 = new Image("Main/cybergrid.png"); + game_over_screen1 = new Image("Main/game_Over1.png"); + game_over_screen2 = new Image("Main/game_Over2.png"); + game_over_screen3 = new Image("Main/game_Over3.png"); + game_over_screen4 = new Image("Main/game_Over4.png"); + game_over_screen5 = new Image("Main/game_Over5.png"); + game_over_screen6 = new Image("Main/game_Over6.png"); + game_over_screen7 = new Image("Main/game_Over7.png"); + game_over_screen8 = new Image("Main/game_Over8.png"); + + + this.waiter = waiter; + this.panel = panel; + this.state = gameState; + this.invader = invader; + this.protector = player; + this.ui = ui; + + + alert = new Image("Main/Hack_Alert.png"); + clearedLevel = new Image("Main/level_cleared_screen.png"); + instructionScreen = new Image("Main/key_instructions.png"); + missionScreen = new Image("Main/mission.png"); + arrows_sheet = new SpriteSheet("Main/arrows_sheet.png" ,101, 101); + walk_demo_sheet = new SpriteSheet("Main/robot_sheet_good.png",50,100); + virus_demo_sheet = new SpriteSheet("Main/Virus_sheet.png",72,72); + rider_demo_sheet = new SpriteSheet("Main/rider.png",75,75); + shock_demo_sheet = new SpriteSheet("Main/shock.png",75,45); + + arrows = new Animation(arrows_sheet,0,0,3,2,true,200, true); + walk_demo = new Animation(walk_demo_sheet,0,0,5,2,true,200,true); + virus_demo = new Animation(virus_demo_sheet,2,1,3,4,true,200,true); + rider_demo = new Animation(rider_demo_sheet,0,0,2,0,true,200,true); + shock_demo = new Animation(shock_demo_sheet,0,0,3,0,true,200,true); + + selectedString = 0;//Default Auswahl im Menu + currentLevel = 1;//Initial Level zu Begin + + cyberg = new Image("Main/cybergrid.png"); + title = new Image("Main/titel.png"); + paused = new Image("Main/paused_menu.png"); + shock = new SpriteSheet("Main/shock.png", 75, 45); + selected = new Animation(shock,0,0,3,0,true,250,true); + + protector.init(gc,gameState,100);//Roboter Initialisierung an YPosition 100 + invader.init(gc,gameState); + + } + + public void render (GameContainer gc, Graphics g)throws SlickException{ + + + //Rendering abhängig von State + switch(state.currentState){ + + case "win" ://Sieges Screen + g.drawImage(clearedLevel, 0, 0); + g.setColor(Color.cyan); + g.drawLine(0, 100, 800, 100); + break; + //game over Pseudo-Animation + case "gameOver": + if(waiter > 5) + g.drawImage(game_over_screen8, 0, 0); + else if ( waiter > 4.2) + g.drawImage(game_over_screen7, 0, 0); + else if(waiter > 3.8) + g.drawImage(game_over_screen6, 0, 0); + else if(waiter > 3.2) + g.drawImage(game_over_screen5, 0, 0); + else if(waiter > 2.8) + g.drawImage(game_over_screen4, 0, 0); + else if(waiter > 2.2) + g.drawImage(game_over_screen3, 0, 0); + else if(waiter > 1.8) + g.drawImage(game_over_screen2, 0, 0); + else if(waiter > 1.2) + g.drawImage(game_over_screen1, 0, 0); + else + g.drawImage(game_over_screen0, 0, 0); + break; + + //Steuerungs Screens + case "controlInstructions": + if(waiter < 5){ + g.drawImage(instructionScreen, 0, 0); + arrows.draw(150, 200); + walk_demo.draw(550, 200); + + } + else if(waiter >6){//"Missions screen" + g.drawImage(missionScreen, 0, 0); + walk_demo.draw(550, 200); + virus_demo.draw(550, 300); + rider_demo.draw(550, 430); + shock_demo.draw(550, 460); + } + break; + + case "level_intro": + //Level intro je nach currentLevel + switch(currentLevel){ + case 1:alert.draw(0, 0); + g.setColor(Color.red); + g.drawLine(0, 200, 800, 200); + g.drawLine(0, 400, 800, 400); + g.setColor(Color.white); + g.drawString("1", 300, 330); + g.drawString("-First Contact-", 330, 330); + break; + case 2:alert.draw(0, 0); + g.setColor(Color.red); + g.drawLine(0, 200, 800, 200); + g.drawLine(0, 400, 800, 400); + g.setColor(Color.white); + g.drawString("2", 300, 330); + g.drawString("-Two Paths to the Core-", 330, 330); + break; + case 3:alert.draw(0, 0); + g.setColor(Color.red); + g.drawLine(0, 200, 800, 200); + g.drawLine(0, 400, 800, 400); + g.setColor(Color.white); + g.drawString("3", 300, 330); + g.drawString("-The Cyberlabyrinth-", 330, 330); + break; + case 4:alert.draw(0, 0); + g.setColor(Color.red); + g.drawLine(0, 200, 800, 200); + g.drawLine(0, 400, 800, 400); + g.setColor(Color.white); + g.drawString("4", 300, 330); + g.drawString("-The prelast level-", 330, 330); + break; + case 5:alert.draw(0, 0); + g.setColor(Color.red); + g.drawLine(0, 200, 800, 200); + g.drawLine(0, 400, 800, 400); + g.setColor(Color.white); + g.drawString("5", 300, 330); + g.drawString("-Final Fight-", 330, 330); + break; + } + break; + + case "titel": + g.drawImage(title, 0, 0);break; + case"paused": + cyberg.draw(0, 0, 800, 600); + panel.render(gc,g); + protector.render(gc, g); + invader.render(gc, g); + + g.setColor(Color.magenta); + //Pause Menü Tags + paused.draw(225, 250); + g.drawString("Resume Game", 425, 290); + g.drawString("Options(Not Implemented)", 425, 320); + g.drawString("Exit Game", 425, 350); + //Name des Levels in der UI + switch(currentLevel){ + case 1:g.drawString(level[0], 30, 555);break; + case 2:g.drawString(level[1], 30, 555);break; + case 3:g.drawString(level[2], 30, 555);break; + case 4:g.drawString(level[3], 30, 555);break; + case 5:g.drawString(level[4], 30, 555);break; + } + + switch(selectedString){ + case 0: selected.draw(325, 280);break; + case 1: selected.draw(325, 310);break; + case 2: selected.draw(325, 340);break; + } + break; + case"start": + cyberg.draw(0, 0, 800, 600); + panel.render(gc,g); + protector.render(gc, g); + invader.render(gc, g); + + g.setColor(Color.red); + g.drawLine(0, gc.getHeight()-50, 800, gc.getHeight()-50);//Systemlebenslinie + g.setColor(Color.magenta); + + + switch(currentLevel){ + case 1:g.drawString(level[0], 30, 555);break; + case 2:g.drawString(level[1], 30, 555);break; + case 3:g.drawString(level[2], 30, 555);break; + case 4:g.drawString(level[3], 30, 555);break; + case 5:g.drawString(level[4], 30, 555);break; + } + + + break; + } + } + + public void update (GameContainer gc,float _delta)throws SlickException{ + this.waiter += _delta;//Ingame Zeit + + switch(state.currentState){ + + case "paused": + //Kontrolle im Menü + if(gc.getInput().isKeyPressed(Input.KEY_UP)&& selectedString >0) + selectedString--; + if(gc.getInput().isKeyPressed(Input.KEY_DOWN)&& selectedString <2) + selectedString++; + break; + + case "start": + protector.update(gc, _delta); + invader.update(gc, _delta); + + + + if(!invader.isParalized&& invader.para_timer >=5 && currentLevel == 5){ + //invader movement in final level// + if(invader.riderX+75 > panel.xPos+panel.xSize*15 && changedDirection == false){ + invader.flightSpdX *= -1; + invader.fly_right = false; + changedDirection = true; + } + + + if(invader.riderX -75 < panel.xPos+panel.xSize*1 && changedDirection == false){ + invader.flightSpdX *= -1; + invader.fly_right = true; + changedDirection = true; + } + + + if(invader.riderX < panel.xPos+panel.xSize*9 && + invader.riderX > panel.xPos+panel.xSize*6 && changedDirection == true) + changedDirection = false; + + //Ball Bewegung im letzten level + if(invader.ball.xPos-36 > panel.xPos+panel.xSize*15) + invader.ball.collisionManager("x_inverted"); + + if(invader.ball.xPos+36 < panel.xPos+panel.xSize*1) + invader.ball.collisionManager("x_inverted"); + + + } + + + //Level ende und wechsel zum nächsten Level + if(panel.xPos <= -4350 && protector.isLanded){ + currentLevel++; + waiter = 0; + state.stateId = 5; + panel.init(gc, state, currentLevel); + protector.init(gc, state, -100); + invader.init(gc, state); + ui.sbar_width = 0; + invader.isParalized = false; + panel.xPos = 350; + } + + + + //Roboter - Panel kollision + for(int i = 1; i< panel.collisionarea.length;i++){ + if(panel.collisionarea[i].intersects(protector.getCollisionarea())&&!protector.hasFloorContact){ + protector.ySpd = 0; + protector.setFloorContact(true); + protector.isLanded = true; + + } else + protector.hasFloorContact = false; + + //einsetzen der "Gravitation" + if(panel.collisionarea[i].intersects(protector.getCollisionarea())&& !protector.hasFloorContact){ + + protector.ySpd *=0; + + } + } + + //Roboter Sprung + if(gc.getInput().isKeyDown(Input.KEY_UP)&& protector.isLanded) + { + if(protector.isRunningRight){ + + protector.move = 3; + } + else + if(protector.isRunningLeft){ + + protector.move = 4; + } + else + protector.move = 0; + protector.hasFloorContact = false; + protector.ySpd = -400; + protector.isLanded = false; + + } + + //Ball fliegt oben aus dem Screen + if(invader.ball.yPos < 0&& !invader.ball.ballIsKilled){ + invader.ball.killBall(gc); + protector.ballhit =false; + + } + //Kollision Ball - Hacker + if(invader.ball.yPos < invader.riderY + 75 && + invader.ball.yPos > invader.riderY && + invader.ball.xPos < invader.riderX + 75 && + invader.ball.xPos > invader.riderX ){ + invader.ball.killBall(gc); + ui.sbar_width += 20; + protector.ballhit=false; + + } + + //Disconnected Erzeugung + if(ui.sbar_width >= 100 && !invader.isParalized){ + invader.isParalized = true; + invader.paralized(); + + } + + //ReConect des Hackers + if(invader.para_timer >= 5 && ui.sbar_width >= 100){ + ui.sbar_width -=100; + invader.isParalized = false; + } + + + // Ball Kollidiert mit Systemlebenslinie + if( (invader.ball.yPos >= gc.getHeight()-50)){ + + invader.ball.killBall(gc); + protector.ballhit=false; + + + if(ui.dbar_width >0) + ui.dbar_width -= 20; + } + + + + //Ball - Roboter Kollision + if(invader.ball.yPos >= protector.yPos -50 && + invader.ball.xPos >= protector.xPos -25&& + invader.ball.xPos <= protector.xPos + 25&& + invader.ball.yPos <= protector.yPos +50&& + !protector.ballhit){ + + invader.ball.collisionManager("y_inverted"); + protector.ballhit= true; + } + + //roboter rechtsbewegung + if(gc.getInput().isKeyDown(Input.KEY_RIGHT)){ + if(protector.isLanded)protector.move = 1; + else + protector.move = 3; + panel.xSpd=-200; + invader.ball.xPos-=3; + if(!invader.isParalized) + invader.riderX -=3; + + + } + else + //roboter Linksbewegung + if(gc.getInput().isKeyDown(Input.KEY_LEFT)){ + if(protector.isLanded)protector.move = 2; + else + protector.move = 4; + panel.xSpd=200; + invader.ball.xPos+=3; + if(!invader.isParalized) + invader.riderX +=3; + + } + + else + {//roboter stand bewegung + protector.move = 0; + panel.xSpd=0; + } + + //roboter fliegt unten durch den Screen + if(protector.yPos > 800){ + protector.init(gc, state,-100 ); + panel.xPos = 350; + invader.init(gc, state); + invader.ball.killBall(gc); + ui.sbar_width = 0; + ui.dbar_width -=20 ; + invader.isParalized = false; + } + break; + } + } +} + + + diff --git a/QuellCode in txt/Menu.txt b/QuellCode in txt/Menu.txt new file mode 100644 index 0000000..dbe1388 --- /dev/null +++ b/QuellCode in txt/Menu.txt @@ -0,0 +1,70 @@ +package Main; + +import org.newdawn.slick.Animation; +import org.newdawn.slick.Color; +import org.newdawn.slick.GameContainer; +import org.newdawn.slick.Graphics; +import org.newdawn.slick.Image; +import org.newdawn.slick.Input; +import org.newdawn.slick.SlickException; +import org.newdawn.slick.SpriteSheet; + +public class Menu { + + Image menu; + State state; + String selectedOption; + int selectedString; + SpriteSheet shocking; + Animation selected; + + + public void init(GameContainer gc, State state) throws SlickException{ + this.state= state; + + selectedString = 0; + menu = new Image ("Main/menu.png"); + shocking = new SpriteSheet("Main/shock.png", 75, 45); + selected = new Animation(shocking,0,0,3,0,true,250,true);//Auswahl im Menu + } + + public void render(GameContainer gc, Graphics g)throws SlickException{ + g.setColor(Color.magenta); + + + + if(state.currentState == "menu"){ + menu.draw(0, 0); + g.scale(2, 2); + + switch(selectedString){ + case 0: selected.draw(100, 70);break; + case 1: selected.draw(100, 130);break; + case 2: selected.draw(100, 190);break; + + }//Menu auswahl punkte + g.drawString("Start Game", 150, 80); + g.drawString("Options Game", 150, 140); + g.drawString("Exit Game", 150, 200); + g.scale(0.5f, 0.5f); + } + } + + public void update(GameContainer gc, float _delta)throws SlickException{ + + switch(state.stateId){ + case 0: case 1:break; + case 4: switch(selectedString){ + case 0: selectedOption = "Start Game";selectedString = 0;break; + case 1: selectedOption = "Options";selectedString = 1;break; + case 2: selectedOption = "Exit Game";selectedString = 2;break; + } + if(gc.getInput().isKeyPressed(Input.KEY_UP)&& selectedString >0) + selectedString--; + if(gc.getInput().isKeyPressed(Input.KEY_DOWN)&& selectedString <2) + selectedString++; + + break; + } + } +} diff --git a/QuellCode in txt/Panel.txt b/QuellCode in txt/Panel.txt new file mode 100644 index 0000000..c086ff9 --- /dev/null +++ b/QuellCode in txt/Panel.txt @@ -0,0 +1,238 @@ +package Main; + +import org.newdawn.slick.Color; +import org.newdawn.slick.GameContainer; +import org.newdawn.slick.Graphics; +import org.newdawn.slick.Image; +import org.newdawn.slick.SlickException; +import org.newdawn.slick.geom.Rectangle; + +public class Panel { + + Image panel; + int i ; + int xPos,xSize,yPos,xSpd; + + //Level Modelierungen + int[] yPos_level1= {0,12,12,11,11,10,10, 9, 9,-1,-1, + -1,-1, 9, 9, 9,10,10,11,11,12, + 12,-1,-1,11,11,-1,-1,10,10,-1, + -1, 9, 9,-1,-1,-1, 9, 9,-1,-1, + -1,-1, 9, 9, 9,-1,-1,-1,-1,-1, + 11,11,-1,-1,10,10,12,12, 7, 7, + 7,-1,-1,-1,-1,-1, 8, 8, 7, 7, + -1,-1,-1,-1,-1,-1,11,11,-1,-1, + -1,-1,-1,12,12,10,10,-1,-1,-1, + -1,-1,11,11,12,12,12,12,12}; + + int[] yPos_level2= {0, 9, 9, 9,-1,-1, 8,10, 5,12, 5, + 12, 9, 9, 9,-1, 5,12,-1,-1, 5, + -1,12,-1,-1,-1,-1,11,11,11,-1, + -1,-1,-1,10,10,10,-1,-1,-1,-1, + 9, 9, 9,-1,12,12,12,-1, 8, 8, + 8,-1,12,12,-1, 7, 7, 7,12,12, + 12,12, 6, 6, 6,-1,12,12,-1, 5, + 5, 5,-1,-1,-1,-1,-1,-1,-1,12, + 12,12,12,12,12,-1,-1,-1,10,10, + 10,-1,-1,-1, 9, 9,10,10,10}; + + int[] yPos_level3= {0, 7, 7, 7,11,11,11,-1,-1, 8, 8, + 8,-1,-1,-1, 5, 5, 5,-1,-1, 8, + -1,-1,-1,-1,12,-1, 6,-1,-1, 9, + -1, 5, 5, 6, 6, 8, 8, 8, 9, 9, + -1,-1,-1, 6,12,-1, 9,-1,-1, 5, + 5, 5, 9, 7, 6,12,-1,-1,-1,11, + 6,12,12, 9, 5, 5,-1,-1,-1,-1, + -1,-1,-1, 9, 9, 9, 9,-1,-1,-1, + -1,12,12,-1, 6,12, 5,10,-1,-1, + -1, 5, 7, 7, 8, 8, 8, 8, 8}; + + int[] yPos_level4= {0,12,12,11,11,10,10,11,11,12,12, + 11,12,12,11,11,12,12,10,10,19, + 19,19,10,11,11,12,12,12,12,12, + 12,11,11,11,10,10,10, 8,11, 8, + 11,10,10,10, 9, 9, 8, 4,11, 4, + 11, 4,11, 8, 8, 8, 4,11, 4,11, + 4,12, 8, 8, 9, 9, 9,10,10,10, + 11,11,11,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12}; + + int[] yPos_finalLevel= {0,12, 5,-1, 9,-1,-1,12, 5,-1,-1, 9,-1, 5,12}; + + State state; + Rectangle[] collisionarea;// Kollisionsvierecke für die Roboter-Panel Kollisionen + int currentLevel; + + public void init(GameContainer gc, State state,int currentLevel) throws SlickException { + // TODO Auto-generated method stub + this.state = state; + this.currentLevel = currentLevel; + panel = new Image ("Main/panel.png"); + //initialwerte + xSize = 50;//breite eines Panels + yPos = 40;// höhe eines Panels + xPos = 350;//Initial Position des ersten Panels + xSpd = 0; + + //initiierung der einzelnen Kollisionsvierecke der jeweiligen Levels + switch(currentLevel){ + case 1: + collisionarea = new Rectangle[yPos_level1.length]; + + for(int i = 1; i + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Snake/.project b/Snake/.project new file mode 100644 index 0000000..919eb8f --- /dev/null +++ b/Snake/.project @@ -0,0 +1,17 @@ + + + Snake + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/Snake/bin/de/fhtrier/gde/snake/Snake$Direction.class b/Snake/bin/de/fhtrier/gde/snake/Snake$Direction.class new file mode 100644 index 0000000..ace9d50 Binary files /dev/null and b/Snake/bin/de/fhtrier/gde/snake/Snake$Direction.class differ diff --git a/Snake/bin/de/fhtrier/gde/snake/Snake.class b/Snake/bin/de/fhtrier/gde/snake/Snake.class new file mode 100644 index 0000000..02cf57c Binary files /dev/null and b/Snake/bin/de/fhtrier/gde/snake/Snake.class differ diff --git a/Snake/bin/de/fhtrier/gde/snake/SnakeGame.class b/Snake/bin/de/fhtrier/gde/snake/SnakeGame.class new file mode 100644 index 0000000..55bfb4f Binary files /dev/null and b/Snake/bin/de/fhtrier/gde/snake/SnakeGame.class differ diff --git a/Snake/bin/de/fhtrier/gde/snake/SnakeSegment.class b/Snake/bin/de/fhtrier/gde/snake/SnakeSegment.class new file mode 100644 index 0000000..294fcb0 Binary files /dev/null and b/Snake/bin/de/fhtrier/gde/snake/SnakeSegment.class differ diff --git a/Snake/bin/de/fhtrier/gde/snake/World.class b/Snake/bin/de/fhtrier/gde/snake/World.class new file mode 100644 index 0000000..653c353 Binary files /dev/null and b/Snake/bin/de/fhtrier/gde/snake/World.class differ diff --git a/Snake/build.xml b/Snake/build.xml new file mode 100644 index 0000000..c8c2610 --- /dev/null +++ b/Snake/build.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + Builds, tests, and runs the project Snake. + + + diff --git a/Snake/ext/lwjgl/AppleJavaExtensions.jar b/Snake/ext/lwjgl/AppleJavaExtensions.jar new file mode 100644 index 0000000..160d62b Binary files /dev/null and b/Snake/ext/lwjgl/AppleJavaExtensions.jar differ diff --git a/Snake/ext/lwjgl/asm-debug-all.jar b/Snake/ext/lwjgl/asm-debug-all.jar new file mode 100644 index 0000000..d5aa15e Binary files /dev/null and b/Snake/ext/lwjgl/asm-debug-all.jar differ diff --git a/Snake/ext/lwjgl/jinput.jar b/Snake/ext/lwjgl/jinput.jar new file mode 100644 index 0000000..7c2b6b0 Binary files /dev/null and b/Snake/ext/lwjgl/jinput.jar differ diff --git a/Snake/ext/lwjgl/lwjgl-debug.jar b/Snake/ext/lwjgl/lwjgl-debug.jar new file mode 100644 index 0000000..8ea8c64 Binary files /dev/null and b/Snake/ext/lwjgl/lwjgl-debug.jar differ diff --git a/Snake/ext/lwjgl/lwjgl.jar b/Snake/ext/lwjgl/lwjgl.jar new file mode 100644 index 0000000..a0fb56d Binary files /dev/null and b/Snake/ext/lwjgl/lwjgl.jar differ diff --git a/Snake/ext/lwjgl/lwjgl_test.jar b/Snake/ext/lwjgl/lwjgl_test.jar new file mode 100644 index 0000000..d6b0f09 Binary files /dev/null and b/Snake/ext/lwjgl/lwjgl_test.jar differ diff --git a/Snake/ext/lwjgl/lwjgl_util.jar b/Snake/ext/lwjgl/lwjgl_util.jar new file mode 100644 index 0000000..9973b24 Binary files /dev/null and b/Snake/ext/lwjgl/lwjgl_util.jar differ diff --git a/Snake/ext/lwjgl/lwjgl_util_applet.jar b/Snake/ext/lwjgl/lwjgl_util_applet.jar new file mode 100644 index 0000000..a8872e2 Binary files /dev/null and b/Snake/ext/lwjgl/lwjgl_util_applet.jar differ diff --git a/Snake/ext/lwjgl/lzma.jar b/Snake/ext/lwjgl/lzma.jar new file mode 100644 index 0000000..a2572d3 Binary files /dev/null and b/Snake/ext/lwjgl/lzma.jar differ diff --git a/Snake/ext/slick/hiero.jar b/Snake/ext/slick/hiero.jar new file mode 100644 index 0000000..fa05723 Binary files /dev/null and b/Snake/ext/slick/hiero.jar differ diff --git a/Snake/ext/slick/ibxm.jar b/Snake/ext/slick/ibxm.jar new file mode 100644 index 0000000..619d26e Binary files /dev/null and b/Snake/ext/slick/ibxm.jar differ diff --git a/Snake/ext/slick/jinput.jar b/Snake/ext/slick/jinput.jar new file mode 100644 index 0000000..e12e523 Binary files /dev/null and b/Snake/ext/slick/jinput.jar differ diff --git a/Snake/ext/slick/jnlp.jar b/Snake/ext/slick/jnlp.jar new file mode 100644 index 0000000..8fd98f2 Binary files /dev/null and b/Snake/ext/slick/jnlp.jar differ diff --git a/Snake/ext/slick/jogg-0.0.7.jar b/Snake/ext/slick/jogg-0.0.7.jar new file mode 100644 index 0000000..ecb0260 Binary files /dev/null and b/Snake/ext/slick/jogg-0.0.7.jar differ diff --git a/Snake/ext/slick/jorbis-0.0.15.jar b/Snake/ext/slick/jorbis-0.0.15.jar new file mode 100644 index 0000000..4cf51f9 Binary files /dev/null and b/Snake/ext/slick/jorbis-0.0.15.jar differ diff --git a/Snake/ext/slick/lwjgl.jar b/Snake/ext/slick/lwjgl.jar new file mode 100644 index 0000000..10602a4 Binary files /dev/null and b/Snake/ext/slick/lwjgl.jar differ diff --git a/Snake/ext/slick/natives-linux.jar b/Snake/ext/slick/natives-linux.jar new file mode 100644 index 0000000..15d28f0 Binary files /dev/null and b/Snake/ext/slick/natives-linux.jar differ diff --git a/Snake/ext/slick/natives-mac.jar b/Snake/ext/slick/natives-mac.jar new file mode 100644 index 0000000..de5e473 Binary files /dev/null and b/Snake/ext/slick/natives-mac.jar differ diff --git a/Snake/ext/slick/natives-win32.jar b/Snake/ext/slick/natives-win32.jar new file mode 100644 index 0000000..dc0c9eb Binary files /dev/null and b/Snake/ext/slick/natives-win32.jar differ diff --git a/Snake/ext/slick/packulike.jar b/Snake/ext/slick/packulike.jar new file mode 100644 index 0000000..15a56ea Binary files /dev/null and b/Snake/ext/slick/packulike.jar differ diff --git a/Snake/ext/slick/pedigree.jar b/Snake/ext/slick/pedigree.jar new file mode 100644 index 0000000..c75da2c Binary files /dev/null and b/Snake/ext/slick/pedigree.jar differ diff --git a/Snake/ext/slick/scalar.jar b/Snake/ext/slick/scalar.jar new file mode 100644 index 0000000..82268fc Binary files /dev/null and b/Snake/ext/slick/scalar.jar differ diff --git a/Snake/ext/slick/slick-examples.jar b/Snake/ext/slick/slick-examples.jar new file mode 100644 index 0000000..27fe577 Binary files /dev/null and b/Snake/ext/slick/slick-examples.jar differ diff --git a/Snake/ext/slick/slick-sources.jar b/Snake/ext/slick/slick-sources.jar new file mode 100644 index 0000000..0214ed0 Binary files /dev/null and b/Snake/ext/slick/slick-sources.jar differ diff --git a/Snake/ext/slick/slick-util-src.zip b/Snake/ext/slick/slick-util-src.zip new file mode 100644 index 0000000..58e6d02 Binary files /dev/null and b/Snake/ext/slick/slick-util-src.zip differ diff --git a/Snake/ext/slick/slick-util.jar b/Snake/ext/slick/slick-util.jar new file mode 100644 index 0000000..768941d Binary files /dev/null and b/Snake/ext/slick/slick-util.jar differ diff --git a/Snake/ext/slick/slick.jar b/Snake/ext/slick/slick.jar new file mode 100644 index 0000000..214033d Binary files /dev/null and b/Snake/ext/slick/slick.jar differ diff --git a/Snake/ext/slick/tinylinepp.jar b/Snake/ext/slick/tinylinepp.jar new file mode 100644 index 0000000..7e0075d Binary files /dev/null and b/Snake/ext/slick/tinylinepp.jar differ diff --git a/Snake/manifest.mf b/Snake/manifest.mf new file mode 100644 index 0000000..1574df4 --- /dev/null +++ b/Snake/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff --git a/Snake/native/linux/libjinput-linux.so b/Snake/native/linux/libjinput-linux.so new file mode 100644 index 0000000..3cdc439 Binary files /dev/null and b/Snake/native/linux/libjinput-linux.so differ diff --git a/Snake/native/linux/libjinput-linux64.so b/Snake/native/linux/libjinput-linux64.so new file mode 100644 index 0000000..de1ee5f Binary files /dev/null and b/Snake/native/linux/libjinput-linux64.so differ diff --git a/Snake/native/linux/liblwjgl.so b/Snake/native/linux/liblwjgl.so new file mode 100644 index 0000000..5a02874 Binary files /dev/null and b/Snake/native/linux/liblwjgl.so differ diff --git a/Snake/native/linux/liblwjgl64.so b/Snake/native/linux/liblwjgl64.so new file mode 100644 index 0000000..4572589 Binary files /dev/null and b/Snake/native/linux/liblwjgl64.so differ diff --git a/Snake/native/linux/libopenal.so b/Snake/native/linux/libopenal.so new file mode 100644 index 0000000..7742faf Binary files /dev/null and b/Snake/native/linux/libopenal.so differ diff --git a/Snake/native/linux/libopenal64.so b/Snake/native/linux/libopenal64.so new file mode 100644 index 0000000..d1e45e5 Binary files /dev/null and b/Snake/native/linux/libopenal64.so differ diff --git a/Snake/native/macosx/libjinput-osx.jnilib b/Snake/native/macosx/libjinput-osx.jnilib new file mode 100644 index 0000000..59a3eab Binary files /dev/null and b/Snake/native/macosx/libjinput-osx.jnilib differ diff --git a/Snake/native/macosx/liblwjgl.jnilib b/Snake/native/macosx/liblwjgl.jnilib new file mode 100644 index 0000000..cbb5b4c Binary files /dev/null and b/Snake/native/macosx/liblwjgl.jnilib differ diff --git a/Snake/native/macosx/openal.dylib b/Snake/native/macosx/openal.dylib new file mode 100644 index 0000000..c9ca66e Binary files /dev/null and b/Snake/native/macosx/openal.dylib differ diff --git a/Snake/native/solaris/liblwjgl.so b/Snake/native/solaris/liblwjgl.so new file mode 100644 index 0000000..57568ab Binary files /dev/null and b/Snake/native/solaris/liblwjgl.so differ diff --git a/Snake/native/solaris/liblwjgl64.so b/Snake/native/solaris/liblwjgl64.so new file mode 100644 index 0000000..96bc8a2 Binary files /dev/null and b/Snake/native/solaris/liblwjgl64.so differ diff --git a/Snake/native/solaris/libopenal.so b/Snake/native/solaris/libopenal.so new file mode 100644 index 0000000..17369bd Binary files /dev/null and b/Snake/native/solaris/libopenal.so differ diff --git a/Snake/native/solaris/libopenal64.so b/Snake/native/solaris/libopenal64.so new file mode 100644 index 0000000..1a51964 Binary files /dev/null and b/Snake/native/solaris/libopenal64.so differ diff --git a/Snake/native/windows/OpenAL32.dll b/Snake/native/windows/OpenAL32.dll new file mode 100644 index 0000000..6dd2600 Binary files /dev/null and b/Snake/native/windows/OpenAL32.dll differ diff --git a/Snake/native/windows/OpenAL64.dll b/Snake/native/windows/OpenAL64.dll new file mode 100644 index 0000000..00c98c0 Binary files /dev/null and b/Snake/native/windows/OpenAL64.dll differ diff --git a/Snake/native/windows/jinput-dx8.dll b/Snake/native/windows/jinput-dx8.dll new file mode 100644 index 0000000..6d27ad5 Binary files /dev/null and b/Snake/native/windows/jinput-dx8.dll differ diff --git a/Snake/native/windows/jinput-dx8_64.dll b/Snake/native/windows/jinput-dx8_64.dll new file mode 100644 index 0000000..6730589 Binary files /dev/null and b/Snake/native/windows/jinput-dx8_64.dll differ diff --git a/Snake/native/windows/jinput-raw.dll b/Snake/native/windows/jinput-raw.dll new file mode 100644 index 0000000..ce1d162 Binary files /dev/null and b/Snake/native/windows/jinput-raw.dll differ diff --git a/Snake/native/windows/jinput-raw_64.dll b/Snake/native/windows/jinput-raw_64.dll new file mode 100644 index 0000000..3d2b3ad Binary files /dev/null and b/Snake/native/windows/jinput-raw_64.dll differ diff --git a/Snake/native/windows/lwjgl.dll b/Snake/native/windows/lwjgl.dll new file mode 100644 index 0000000..6819404 Binary files /dev/null and b/Snake/native/windows/lwjgl.dll differ diff --git a/Snake/native/windows/lwjgl64.dll b/Snake/native/windows/lwjgl64.dll new file mode 100644 index 0000000..e66ab2a Binary files /dev/null and b/Snake/native/windows/lwjgl64.dll differ diff --git a/Snake/nbproject/build-impl.xml b/Snake/nbproject/build-impl.xml new file mode 100644 index 0000000..afc2595 --- /dev/null +++ b/Snake/nbproject/build-impl.xml @@ -0,0 +1,1347 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set src.res.dir + Must set test.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + + + + + + java -cp "${run.classpath.with.dist.jar}" ${main.class} + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Snake/nbproject/genfiles.properties b/Snake/nbproject/genfiles.properties new file mode 100644 index 0000000..973f7e5 --- /dev/null +++ b/Snake/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=38dbc30b +build.xml.script.CRC32=b8c51ff4 +build.xml.stylesheet.CRC32=28e38971@1.51.1.46 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=599cf24b +nbproject/build-impl.xml.script.CRC32=688ffdf9 +nbproject/build-impl.xml.stylesheet.CRC32=5183f3c0@1.51.1.46 diff --git a/Snake/nbproject/private/config.properties b/Snake/nbproject/private/config.properties new file mode 100644 index 0000000..e69de29 diff --git a/Snake/nbproject/private/private.properties b/Snake/nbproject/private/private.properties new file mode 100644 index 0000000..28c4ec1 --- /dev/null +++ b/Snake/nbproject/private/private.properties @@ -0,0 +1,6 @@ +compile.on.save=false +do.depend=false +do.jar=true +javac.debug=true +javadoc.preview=true +user.properties.file=C:\\Users\\Lusito\\AppData\\Roaming\\NetBeans\\7.2\\build.properties diff --git a/Snake/nbproject/private/private.xml b/Snake/nbproject/private/private.xml new file mode 100644 index 0000000..db03bb0 --- /dev/null +++ b/Snake/nbproject/private/private.xml @@ -0,0 +1,4 @@ + + + + diff --git a/Snake/nbproject/project.properties b/Snake/nbproject/project.properties new file mode 100644 index 0000000..43e3bb2 --- /dev/null +++ b/Snake/nbproject/project.properties @@ -0,0 +1,91 @@ +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.processors.list= +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=Snake +application.vendor=Roughael +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/Snake.jar +dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= +excludes= +file.reference.lwjgl.jar=ext/lwjgl/lwjgl.jar +file.reference.slick-sources.jar=ext/slick/slick-sources.jar +file.reference.slick.jar=ext/slick/slick.jar +includes=** +jar.archive.disabled=${jnlp.enabled} +jar.compress=false +jar.index=${jnlp.enabled} +javac.classpath=\ + ${file.reference.slick-sources.jar}:\ + ${file.reference.slick.jar}:\ + ${file.reference.lwjgl.jar} +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.processorpath=\ + ${javac.classpath} +javac.source=1.7 +javac.target=1.7 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +jnlp.codebase.type=no.codebase +jnlp.descriptor=application +jnlp.enabled=false +jnlp.mixed.code=default +jnlp.offline-allowed=false +jnlp.signed=false +jnlp.signing= +jnlp.signing.alias= +jnlp.signing.keystore= +main.class=de.fhtrier.gde.snake.SnakeGame +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +mkdist.disabled=false +platform.active=default_platform +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project +# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value +# or test-sys-prop.name=value to set system properties for unit tests): +run.jvmargs=-Djava.library.path="./ext/lwjgl/native/windows" -Djava.security.policy=applet.policy +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.dir=src +src.res.dir=res +test.src.dir=test diff --git a/Snake/nbproject/project.xml b/Snake/nbproject/project.xml new file mode 100644 index 0000000..86b2d15 --- /dev/null +++ b/Snake/nbproject/project.xml @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + Snake + + + + + + + + + + diff --git a/Snake/res/fonts/verdana_24.fnt b/Snake/res/fonts/verdana_24.fnt new file mode 100644 index 0000000..0888392 --- /dev/null +++ b/Snake/res/fonts/verdana_24.fnt @@ -0,0 +1,337 @@ +info face="Verdana" size=24 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=0 aa=1 padding=0,0,0,0 spacing=1,1 outline=1 +common lineHeight=23 base=19 scaleW=256 scaleH=256 pages=1 packed=0 alphaChnl=2 redChnl=0 greenChnl=0 blueChnl=0 +page id=0 file="verdana_24_0.tga" +chars count=191 +char id=32 x=249 y=140 width=3 height=3 xoffset=-1 yoffset=18 xadvance=7 page=0 chnl=15 +char id=33 x=218 y=112 width=4 height=16 xoffset=2 yoffset=4 xadvance=7 page=0 chnl=15 +char id=34 x=176 y=145 width=8 height=7 xoffset=1 yoffset=3 xadvance=9 page=0 chnl=15 +char id=35 x=0 y=98 width=14 height=16 xoffset=1 yoffset=4 xadvance=16 page=0 chnl=15 +char id=36 x=100 y=22 width=11 height=20 xoffset=1 yoffset=3 xadvance=12 page=0 chnl=15 +char id=37 x=0 y=81 width=20 height=16 xoffset=0 yoffset=4 xadvance=20 page=0 chnl=15 +char id=38 x=134 y=80 width=15 height=16 xoffset=0 yoffset=4 xadvance=14 page=0 chnl=15 +char id=39 x=185 y=145 width=4 height=7 xoffset=1 yoffset=3 xadvance=5 page=0 chnl=15 +char id=40 x=130 y=0 width=8 height=21 xoffset=1 yoffset=3 xadvance=9 page=0 chnl=15 +char id=41 x=139 y=0 width=8 height=21 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=15 +char id=42 x=37 y=146 width=11 height=11 xoffset=0 yoffset=3 xadvance=12 page=0 chnl=15 +char id=43 x=15 y=132 width=14 height=14 xoffset=1 yoffset=6 xadvance=16 page=0 chnl=15 +char id=44 x=154 y=145 width=6 height=8 xoffset=0 yoffset=15 xadvance=7 page=0 chnl=15 +char id=45 x=240 y=141 width=8 height=4 xoffset=0 yoffset=11 xadvance=9 page=0 chnl=15 +char id=46 x=217 y=143 width=4 height=5 xoffset=1 yoffset=15 xadvance=7 page=0 chnl=15 +char id=47 x=160 y=22 width=10 height=20 xoffset=-1 yoffset=3 xadvance=9 page=0 chnl=15 +char id=48 x=163 y=97 width=12 height=16 xoffset=0 yoffset=4 xadvance=12 page=0 chnl=15 +char id=49 x=141 y=114 width=10 height=16 xoffset=1 yoffset=4 xadvance=12 page=0 chnl=15 +char id=50 x=60 y=115 width=11 height=16 xoffset=1 yoffset=4 xadvance=12 page=0 chnl=15 +char id=51 x=72 y=114 width=11 height=16 xoffset=1 yoffset=4 xadvance=12 page=0 chnl=15 +char id=52 x=228 y=94 width=12 height=16 xoffset=0 yoffset=4 xadvance=12 page=0 chnl=15 +char id=53 x=84 y=114 width=11 height=16 xoffset=1 yoffset=4 xadvance=12 page=0 chnl=15 +char id=54 x=137 y=97 width=12 height=16 xoffset=0 yoffset=4 xadvance=12 page=0 chnl=15 +char id=55 x=48 y=115 width=11 height=16 xoffset=0 yoffset=4 xadvance=12 page=0 chnl=15 +char id=56 x=124 y=97 width=12 height=16 xoffset=0 yoffset=4 xadvance=12 page=0 chnl=15 +char id=57 x=111 y=97 width=12 height=16 xoffset=0 yoffset=4 xadvance=12 page=0 chnl=15 +char id=58 x=249 y=126 width=4 height=13 xoffset=2 yoffset=7 xadvance=9 page=0 chnl=15 +char id=59 x=211 y=112 width=6 height=16 xoffset=1 yoffset=7 xadvance=9 page=0 chnl=15 +char id=60 x=97 y=131 width=13 height=13 xoffset=1 yoffset=6 xadvance=16 page=0 chnl=15 +char id=61 x=140 y=145 width=13 height=8 xoffset=1 yoffset=9 xadvance=16 page=0 chnl=15 +char id=62 x=83 y=131 width=13 height=13 xoffset=1 yoffset=6 xadvance=16 page=0 chnl=15 +char id=63 x=163 y=114 width=10 height=16 xoffset=0 yoffset=4 xadvance=10 page=0 chnl=15 +char id=64 x=77 y=43 width=18 height=18 xoffset=1 yoffset=4 xadvance=19 page=0 chnl=15 +char id=65 x=118 y=80 width=15 height=16 xoffset=-1 yoffset=4 xadvance=13 page=0 chnl=15 +char id=66 x=85 y=97 width=12 height=16 xoffset=1 yoffset=4 xadvance=13 page=0 chnl=15 +char id=67 x=242 y=76 width=13 height=16 xoffset=0 yoffset=4 xadvance=13 page=0 chnl=15 +char id=68 x=197 y=78 width=14 height=16 xoffset=1 yoffset=4 xadvance=15 page=0 chnl=15 +char id=69 x=0 y=115 width=11 height=16 xoffset=1 yoffset=4 xadvance=12 page=0 chnl=15 +char id=70 x=24 y=115 width=11 height=16 xoffset=1 yoffset=4 xadvance=11 page=0 chnl=15 +char id=71 x=150 y=80 width=15 height=16 xoffset=0 yoffset=4 xadvance=15 page=0 chnl=15 +char id=72 x=202 y=95 width=12 height=16 xoffset=1 yoffset=4 xadvance=14 page=0 chnl=15 +char id=73 x=184 y=114 width=8 height=16 xoffset=0 yoffset=4 xadvance=8 page=0 chnl=15 +char id=74 x=174 y=114 width=9 height=16 xoffset=-1 yoffset=4 xadvance=9 page=0 chnl=15 +char id=75 x=43 y=98 width=13 height=16 xoffset=1 yoffset=4 xadvance=13 page=0 chnl=15 +char id=76 x=36 y=115 width=11 height=16 xoffset=1 yoffset=4 xadvance=11 page=0 chnl=15 +char id=77 x=182 y=80 width=14 height=16 xoffset=1 yoffset=4 xadvance=16 page=0 chnl=15 +char id=78 x=98 y=97 width=12 height=16 xoffset=1 yoffset=4 xadvance=14 page=0 chnl=15 +char id=79 x=166 y=80 width=15 height=16 xoffset=0 yoffset=4 xadvance=15 page=0 chnl=15 +char id=80 x=152 y=114 width=10 height=16 xoffset=1 yoffset=4 xadvance=11 page=0 chnl=15 +char id=81 x=16 y=22 width=15 height=20 xoffset=0 yoffset=4 xadvance=15 page=0 chnl=15 +char id=82 x=15 y=98 width=13 height=16 xoffset=1 yoffset=4 xadvance=13 page=0 chnl=15 +char id=83 x=71 y=97 width=13 height=16 xoffset=0 yoffset=4 xadvance=13 page=0 chnl=15 +char id=84 x=227 y=77 width=14 height=16 xoffset=-1 yoffset=4 xadvance=12 page=0 chnl=15 +char id=85 x=176 y=97 width=12 height=16 xoffset=1 yoffset=4 xadvance=14 page=0 chnl=15 +char id=86 x=240 y=59 width=15 height=16 xoffset=-1 yoffset=4 xadvance=13 page=0 chnl=15 +char id=87 x=62 y=80 width=19 height=16 xoffset=0 yoffset=4 xadvance=19 page=0 chnl=15 +char id=88 x=29 y=98 width=13 height=16 xoffset=0 yoffset=4 xadvance=13 page=0 chnl=15 +char id=89 x=212 y=78 width=14 height=16 xoffset=-1 yoffset=4 xadvance=12 page=0 chnl=15 +char id=90 x=57 y=98 width=13 height=16 xoffset=0 yoffset=4 xadvance=13 page=0 chnl=15 +char id=91 x=148 y=0 width=7 height=21 xoffset=1 yoffset=3 xadvance=9 page=0 chnl=15 +char id=92 x=182 y=21 width=10 height=20 xoffset=0 yoffset=3 xadvance=9 page=0 chnl=15 +char id=93 x=156 y=0 width=7 height=21 xoffset=1 yoffset=3 xadvance=9 page=0 chnl=15 +char id=94 x=115 y=145 width=14 height=9 xoffset=1 yoffset=4 xadvance=16 page=0 chnl=15 +char id=95 x=15 y=159 width=14 height=3 xoffset=-1 yoffset=20 xadvance=12 page=0 chnl=15 +char id=96 x=198 y=145 width=6 height=5 xoffset=2 yoffset=3 xadvance=12 page=0 chnl=15 +char id=97 x=185 y=131 width=10 height=13 xoffset=0 yoffset=7 xadvance=11 page=0 chnl=15 +char id=98 x=24 y=63 width=11 height=17 xoffset=1 yoffset=3 xadvance=12 page=0 chnl=15 +char id=99 x=174 y=131 width=10 height=13 xoffset=0 yoffset=7 xadvance=10 page=0 chnl=15 +char id=100 x=0 y=63 width=11 height=17 xoffset=0 yoffset=3 xadvance=12 page=0 chnl=15 +char id=101 x=138 y=131 width=11 height=13 xoffset=0 yoffset=7 xadvance=11 page=0 chnl=15 +char id=102 x=196 y=60 width=8 height=17 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=15 +char id=103 x=12 y=63 width=11 height=17 xoffset=0 yoffset=7 xadvance=12 page=0 chnl=15 +char id=104 x=185 y=62 width=10 height=17 xoffset=1 yoffset=3 xadvance=12 page=0 chnl=15 +char id=105 x=250 y=41 width=4 height=16 xoffset=1 yoffset=4 xadvance=6 page=0 chnl=15 +char id=106 x=211 y=21 width=7 height=20 xoffset=-1 yoffset=4 xadvance=7 page=0 chnl=15 +char id=107 x=238 y=41 width=11 height=17 xoffset=1 yoffset=3 xadvance=11 page=0 chnl=15 +char id=108 x=251 y=21 width=4 height=17 xoffset=1 yoffset=3 xadvance=6 page=0 chnl=15 +char id=109 x=49 y=132 width=16 height=13 xoffset=1 yoffset=7 xadvance=18 page=0 chnl=15 +char id=110 x=196 y=131 width=10 height=13 xoffset=1 yoffset=7 xadvance=12 page=0 chnl=15 +char id=111 x=125 y=131 width=12 height=13 xoffset=0 yoffset=7 xadvance=12 page=0 chnl=15 +char id=112 x=48 y=63 width=11 height=17 xoffset=1 yoffset=7 xadvance=12 page=0 chnl=15 +char id=113 x=36 y=63 width=11 height=17 xoffset=0 yoffset=7 xadvance=12 page=0 chnl=15 +char id=114 x=240 y=127 width=8 height=13 xoffset=1 yoffset=7 xadvance=8 page=0 chnl=15 +char id=115 x=207 y=129 width=10 height=13 xoffset=0 yoffset=7 xadvance=10 page=0 chnl=15 +char id=116 x=202 y=112 width=8 height=16 xoffset=0 yoffset=4 xadvance=7 page=0 chnl=15 +char id=117 x=218 y=129 width=10 height=13 xoffset=1 yoffset=7 xadvance=12 page=0 chnl=15 +char id=118 x=162 y=131 width=11 height=13 xoffset=0 yoffset=7 xadvance=11 page=0 chnl=15 +char id=119 x=66 y=132 width=16 height=13 xoffset=0 yoffset=7 xadvance=16 page=0 chnl=15 +char id=120 x=150 y=131 width=11 height=13 xoffset=0 yoffset=7 xadvance=11 page=0 chnl=15 +char id=121 x=72 y=62 width=11 height=17 xoffset=0 yoffset=7 xadvance=11 page=0 chnl=15 +char id=122 x=229 y=127 width=10 height=13 xoffset=0 yoffset=7 xadvance=10 page=0 chnl=15 +char id=123 x=85 y=0 width=11 height=21 xoffset=0 yoffset=3 xadvance=12 page=0 chnl=15 +char id=124 x=164 y=0 width=4 height=21 xoffset=3 yoffset=3 xadvance=9 page=0 chnl=15 +char id=125 x=97 y=0 width=11 height=21 xoffset=1 yoffset=3 xadvance=12 page=0 chnl=15 +char id=126 x=161 y=145 width=14 height=7 xoffset=1 yoffset=9 xadvance=16 page=0 chnl=15 +char id=160 x=30 y=159 width=3 height=3 xoffset=-1 yoffset=18 xadvance=7 page=0 chnl=15 +char id=161 x=223 y=112 width=4 height=16 xoffset=2 yoffset=4 xadvance=7 page=0 chnl=15 +char id=162 x=13 y=43 width=11 height=19 xoffset=0 yoffset=4 xadvance=12 page=0 chnl=15 +char id=163 x=215 y=95 width=12 height=16 xoffset=0 yoffset=4 xadvance=12 page=0 chnl=15 +char id=164 x=0 y=147 width=12 height=12 xoffset=0 yoffset=7 xadvance=12 page=0 chnl=15 +char id=165 x=241 y=94 width=12 height=16 xoffset=0 yoffset=4 xadvance=12 page=0 chnl=15 +char id=166 x=169 y=0 width=4 height=21 xoffset=3 yoffset=3 xadvance=9 page=0 chnl=15 +char id=167 x=112 y=22 width=11 height=20 xoffset=1 yoffset=4 xadvance=12 page=0 chnl=15 +char id=168 x=231 y=141 width=8 height=4 xoffset=2 yoffset=4 xadvance=12 page=0 chnl=15 +char id=169 x=96 y=43 width=18 height=18 xoffset=0 yoffset=4 xadvance=19 page=0 chnl=15 +char id=170 x=71 y=146 width=9 height=11 xoffset=0 yoffset=4 xadvance=10 page=0 chnl=15 +char id=171 x=13 y=147 width=11 height=11 xoffset=1 yoffset=7 xadvance=12 page=0 chnl=15 +char id=172 x=100 y=145 width=14 height=9 xoffset=1 yoffset=11 xadvance=16 page=0 chnl=15 +char id=173 x=222 y=143 width=8 height=4 xoffset=0 yoffset=11 xadvance=9 page=0 chnl=15 +char id=174 x=58 y=43 width=18 height=18 xoffset=0 yoffset=4 xadvance=19 page=0 chnl=15 +char id=175 x=0 y=160 width=14 height=3 xoffset=-1 yoffset=1 xadvance=12 page=0 chnl=15 +char id=176 x=130 y=145 width=9 height=9 xoffset=0 yoffset=4 xadvance=10 page=0 chnl=15 +char id=177 x=0 y=132 width=14 height=14 xoffset=1 yoffset=4 xadvance=16 page=0 chnl=15 +char id=178 x=49 y=146 width=10 height=11 xoffset=1 yoffset=4 xadvance=10 page=0 chnl=15 +char id=179 x=81 y=146 width=9 height=11 xoffset=1 yoffset=4 xadvance=10 page=0 chnl=15 +char id=180 x=205 y=145 width=6 height=5 xoffset=4 yoffset=3 xadvance=12 page=0 chnl=15 +char id=181 x=119 y=62 width=10 height=17 xoffset=1 yoffset=7 xadvance=12 page=0 chnl=15 +char id=182 x=37 y=43 width=11 height=19 xoffset=0 yoffset=4 xadvance=12 page=0 chnl=15 +char id=183 x=212 y=143 width=4 height=5 xoffset=1 yoffset=10 xadvance=7 page=0 chnl=15 +char id=184 x=190 y=145 width=7 height=6 xoffset=2 yoffset=18 xadvance=12 page=0 chnl=15 +char id=185 x=91 y=145 width=8 height=11 xoffset=2 yoffset=4 xadvance=10 page=0 chnl=15 +char id=186 x=60 y=146 width=10 height=11 xoffset=0 yoffset=4 xadvance=10 page=0 chnl=15 +char id=187 x=25 y=147 width=11 height=11 xoffset=1 yoffset=7 xadvance=12 page=0 chnl=15 +char id=188 x=82 y=80 width=18 height=16 xoffset=1 yoffset=4 xadvance=19 page=0 chnl=15 +char id=189 x=42 y=81 width=19 height=16 xoffset=1 yoffset=4 xadvance=19 page=0 chnl=15 +char id=190 x=219 y=60 width=20 height=16 xoffset=0 yoffset=4 xadvance=19 page=0 chnl=15 +char id=191 x=108 y=114 width=10 height=16 xoffset=0 yoffset=4 xadvance=10 page=0 chnl=15 +char id=192 x=238 y=0 width=15 height=20 xoffset=-1 yoffset=0 xadvance=13 page=0 chnl=15 +char id=193 x=0 y=22 width=15 height=20 xoffset=-1 yoffset=0 xadvance=13 page=0 chnl=15 +char id=194 x=16 y=0 width=15 height=21 xoffset=-1 yoffset=-1 xadvance=13 page=0 chnl=15 +char id=195 x=174 y=0 width=15 height=20 xoffset=-1 yoffset=0 xadvance=13 page=0 chnl=15 +char id=196 x=235 y=21 width=15 height=19 xoffset=-1 yoffset=1 xadvance=13 page=0 chnl=15 +char id=197 x=0 y=0 width=15 height=21 xoffset=-1 yoffset=-1 xadvance=13 page=0 chnl=15 +char id=198 x=21 y=81 width=20 height=16 xoffset=-1 yoffset=4 xadvance=19 page=0 chnl=15 +char id=199 x=47 y=22 width=13 height=20 xoffset=0 yoffset=4 xadvance=13 page=0 chnl=15 +char id=200 x=148 y=22 width=11 height=20 xoffset=1 yoffset=0 xadvance=12 page=0 chnl=15 +char id=201 x=124 y=22 width=11 height=20 xoffset=1 yoffset=0 xadvance=12 page=0 chnl=15 +char id=202 x=73 y=0 width=11 height=21 xoffset=1 yoffset=-1 xadvance=12 page=0 chnl=15 +char id=203 x=25 y=43 width=11 height=19 xoffset=1 yoffset=1 xadvance=12 page=0 chnl=15 +char id=204 x=202 y=21 width=8 height=20 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=15 +char id=205 x=193 y=21 width=8 height=20 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=15 +char id=206 x=121 y=0 width=8 height=21 xoffset=0 yoffset=-1 xadvance=8 page=0 chnl=15 +char id=207 x=49 y=43 width=8 height=19 xoffset=0 yoffset=1 xadvance=8 page=0 chnl=15 +char id=208 x=101 y=80 width=16 height=16 xoffset=-1 yoffset=4 xadvance=15 page=0 chnl=15 +char id=209 x=87 y=22 width=12 height=20 xoffset=1 yoffset=0 xadvance=14 page=0 chnl=15 +char id=210 x=222 y=0 width=15 height=20 xoffset=0 yoffset=0 xadvance=15 page=0 chnl=15 +char id=211 x=206 y=0 width=15 height=20 xoffset=0 yoffset=0 xadvance=15 page=0 chnl=15 +char id=212 x=32 y=0 width=15 height=21 xoffset=0 yoffset=-1 xadvance=15 page=0 chnl=15 +char id=213 x=190 y=0 width=15 height=20 xoffset=0 yoffset=0 xadvance=15 page=0 chnl=15 +char id=214 x=219 y=21 width=15 height=19 xoffset=0 yoffset=1 xadvance=15 page=0 chnl=15 +char id=215 x=111 y=131 width=13 height=13 xoffset=1 yoffset=6 xadvance=16 page=0 chnl=15 +char id=216 x=115 y=43 width=15 height=18 xoffset=0 yoffset=3 xadvance=15 page=0 chnl=15 +char id=217 x=74 y=22 width=12 height=20 xoffset=1 yoffset=0 xadvance=14 page=0 chnl=15 +char id=218 x=61 y=22 width=12 height=20 xoffset=1 yoffset=0 xadvance=14 page=0 chnl=15 +char id=219 x=48 y=0 width=12 height=21 xoffset=1 yoffset=-1 xadvance=14 page=0 chnl=15 +char id=220 x=0 y=43 width=12 height=19 xoffset=1 yoffset=1 xadvance=14 page=0 chnl=15 +char id=221 x=32 y=22 width=14 height=20 xoffset=-1 yoffset=0 xadvance=12 page=0 chnl=15 +char id=222 x=96 y=114 width=11 height=16 xoffset=1 yoffset=4 xadvance=12 page=0 chnl=15 +char id=223 x=96 y=62 width=11 height=17 xoffset=1 yoffset=3 xadvance=12 page=0 chnl=15 +char id=224 x=130 y=62 width=10 height=17 xoffset=0 yoffset=3 xadvance=11 page=0 chnl=15 +char id=225 x=163 y=62 width=10 height=17 xoffset=0 yoffset=3 xadvance=11 page=0 chnl=15 +char id=226 x=156 y=43 width=10 height=18 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=227 x=226 y=41 width=11 height=17 xoffset=0 yoffset=3 xadvance=11 page=0 chnl=15 +char id=228 x=119 y=114 width=10 height=16 xoffset=0 yoffset=4 xadvance=11 page=0 chnl=15 +char id=229 x=171 y=22 width=10 height=20 xoffset=0 yoffset=0 xadvance=11 page=0 chnl=15 +char id=230 x=30 y=132 width=18 height=13 xoffset=0 yoffset=7 xadvance=18 page=0 chnl=15 +char id=231 x=108 y=62 width=10 height=17 xoffset=0 yoffset=7 xadvance=10 page=0 chnl=15 +char id=232 x=60 y=62 width=11 height=17 xoffset=0 yoffset=3 xadvance=11 page=0 chnl=15 +char id=233 x=84 y=62 width=11 height=17 xoffset=0 yoffset=3 xadvance=11 page=0 chnl=15 +char id=234 x=144 y=43 width=11 height=18 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15 +char id=235 x=12 y=115 width=11 height=16 xoffset=0 yoffset=4 xadvance=11 page=0 chnl=15 +char id=236 x=212 y=60 width=6 height=17 xoffset=0 yoffset=3 xadvance=6 page=0 chnl=15 +char id=237 x=205 y=60 width=6 height=17 xoffset=1 yoffset=3 xadvance=6 page=0 chnl=15 +char id=238 x=178 y=43 width=8 height=18 xoffset=-1 yoffset=2 xadvance=6 page=0 chnl=15 +char id=239 x=193 y=114 width=8 height=16 xoffset=-1 yoffset=4 xadvance=6 page=0 chnl=15 +char id=240 x=150 y=97 width=12 height=16 xoffset=0 yoffset=4 xadvance=12 page=0 chnl=15 +char id=241 x=174 y=62 width=10 height=17 xoffset=1 yoffset=3 xadvance=12 page=0 chnl=15 +char id=242 x=187 y=42 width=12 height=17 xoffset=0 yoffset=3 xadvance=12 page=0 chnl=15 +char id=243 x=213 y=42 width=12 height=17 xoffset=0 yoffset=3 xadvance=12 page=0 chnl=15 +char id=244 x=131 y=43 width=12 height=18 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=15 +char id=245 x=200 y=42 width=12 height=17 xoffset=0 yoffset=3 xadvance=12 page=0 chnl=15 +char id=246 x=189 y=97 width=12 height=16 xoffset=0 yoffset=4 xadvance=12 page=0 chnl=15 +char id=247 x=241 y=111 width=14 height=14 xoffset=1 yoffset=6 xadvance=16 page=0 chnl=15 +char id=248 x=228 y=111 width=12 height=15 xoffset=0 yoffset=6 xadvance=12 page=0 chnl=15 +char id=249 x=141 y=62 width=10 height=17 xoffset=1 yoffset=3 xadvance=12 page=0 chnl=15 +char id=250 x=152 y=62 width=10 height=17 xoffset=1 yoffset=3 xadvance=12 page=0 chnl=15 +char id=251 x=167 y=43 width=10 height=18 xoffset=1 yoffset=2 xadvance=12 page=0 chnl=15 +char id=252 x=130 y=114 width=10 height=16 xoffset=1 yoffset=4 xadvance=12 page=0 chnl=15 +char id=253 x=109 y=0 width=11 height=21 xoffset=0 yoffset=3 xadvance=11 page=0 chnl=15 +char id=254 x=61 y=0 width=11 height=21 xoffset=1 yoffset=3 xadvance=12 page=0 chnl=15 +char id=255 x=136 y=22 width=11 height=20 xoffset=0 yoffset=4 xadvance=11 page=0 chnl=15 +kernings count=141 +kerning first=39 second=65 amount=-1 +kerning first=39 second=198 amount=-1 +kerning first=45 second=74 amount=-1 +kerning first=45 second=84 amount=-1 +kerning first=45 second=88 amount=-1 +kerning first=45 second=89 amount=-1 +kerning first=46 second=44 amount=-1 +kerning first=46 second=45 amount=-2 +kerning first=65 second=84 amount=-1 +kerning first=65 second=86 amount=-1 +kerning first=65 second=89 amount=-1 +kerning first=66 second=84 amount=-1 +kerning first=70 second=44 amount=-3 +kerning first=70 second=46 amount=-3 +kerning first=70 second=58 amount=-1 +kerning first=70 second=63 amount=1 +kerning first=70 second=65 amount=-1 +kerning first=70 second=97 amount=-1 +kerning first=70 second=198 amount=-1 +kerning first=70 second=230 amount=-1 +kerning first=75 second=45 amount=-1 +kerning first=75 second=97 amount=-1 +kerning first=75 second=101 amount=-1 +kerning first=75 second=111 amount=-1 +kerning first=75 second=118 amount=-1 +kerning first=75 second=119 amount=-1 +kerning first=75 second=121 amount=-1 +kerning first=75 second=230 amount=-1 +kerning first=75 second=248 amount=-1 +kerning first=76 second=39 amount=-1 +kerning first=76 second=45 amount=-2 +kerning first=76 second=74 amount=1 +kerning first=76 second=84 amount=-2 +kerning first=76 second=86 amount=-1 +kerning first=76 second=87 amount=-1 +kerning first=76 second=89 amount=-2 +kerning first=76 second=118 amount=-1 +kerning first=76 second=121 amount=-1 +kerning first=80 second=44 amount=-3 +kerning first=80 second=46 amount=-3 +kerning first=80 second=198 amount=-1 +kerning first=82 second=45 amount=-1 +kerning first=82 second=84 amount=-1 +kerning first=82 second=121 amount=-1 +kerning first=84 second=44 amount=-3 +kerning first=84 second=45 amount=-1 +kerning first=84 second=46 amount=-3 +kerning first=84 second=58 amount=-2 +kerning first=84 second=63 amount=1 +kerning first=84 second=65 amount=-1 +kerning first=84 second=84 amount=-1 +kerning first=84 second=97 amount=-2 +kerning first=84 second=99 amount=-2 +kerning first=84 second=101 amount=-2 +kerning first=84 second=103 amount=-2 +kerning first=84 second=111 amount=-2 +kerning first=84 second=114 amount=-2 +kerning first=84 second=115 amount=-2 +kerning first=84 second=117 amount=-2 +kerning first=84 second=118 amount=-2 +kerning first=84 second=119 amount=-2 +kerning first=84 second=121 amount=-2 +kerning first=84 second=122 amount=-2 +kerning first=84 second=198 amount=-1 +kerning first=84 second=230 amount=-2 +kerning first=84 second=248 amount=-2 +kerning first=86 second=44 amount=-3 +kerning first=86 second=46 amount=-3 +kerning first=86 second=58 amount=-1 +kerning first=86 second=65 amount=-1 +kerning first=86 second=97 amount=-1 +kerning first=86 second=101 amount=-1 +kerning first=86 second=111 amount=-1 +kerning first=86 second=117 amount=-1 +kerning first=86 second=121 amount=-1 +kerning first=86 second=198 amount=-1 +kerning first=86 second=230 amount=-1 +kerning first=86 second=248 amount=-1 +kerning first=87 second=44 amount=-3 +kerning first=87 second=46 amount=-2 +kerning first=87 second=58 amount=-1 +kerning first=87 second=97 amount=-1 +kerning first=87 second=101 amount=-1 +kerning first=87 second=111 amount=-1 +kerning first=87 second=114 amount=-1 +kerning first=87 second=117 amount=-1 +kerning first=87 second=121 amount=-1 +kerning first=87 second=198 amount=-1 +kerning first=87 second=230 amount=-1 +kerning first=87 second=248 amount=-1 +kerning first=88 second=45 amount=-1 +kerning first=88 second=101 amount=-1 +kerning first=88 second=111 amount=-1 +kerning first=88 second=121 amount=-1 +kerning first=88 second=248 amount=-1 +kerning first=89 second=44 amount=-3 +kerning first=89 second=45 amount=-1 +kerning first=89 second=46 amount=-3 +kerning first=89 second=58 amount=-2 +kerning first=89 second=65 amount=-1 +kerning first=89 second=97 amount=-1 +kerning first=89 second=100 amount=-1 +kerning first=89 second=101 amount=-1 +kerning first=89 second=103 amount=-1 +kerning first=89 second=109 amount=-1 +kerning first=89 second=110 amount=-1 +kerning first=89 second=111 amount=-1 +kerning first=89 second=112 amount=-1 +kerning first=89 second=113 amount=-1 +kerning first=89 second=114 amount=-1 +kerning first=89 second=115 amount=-1 +kerning first=89 second=117 amount=-1 +kerning first=89 second=118 amount=-1 +kerning first=89 second=198 amount=-1 +kerning first=89 second=230 amount=-1 +kerning first=89 second=248 amount=-1 +kerning first=90 second=45 amount=-1 +kerning first=90 second=101 amount=-1 +kerning first=90 second=111 amount=-1 +kerning first=90 second=121 amount=-1 +kerning first=90 second=248 amount=-1 +kerning first=99 second=84 amount=-1 +kerning first=101 second=84 amount=-1 +kerning first=102 second=34 amount=1 +kerning first=102 second=39 amount=1 +kerning first=102 second=41 amount=1 +kerning first=102 second=44 amount=-1 +kerning first=102 second=46 amount=-1 +kerning first=102 second=63 amount=1 +kerning first=102 second=92 amount=1 +kerning first=102 second=93 amount=1 +kerning first=102 second=125 amount=1 +kerning first=107 second=45 amount=-1 +kerning first=114 second=44 amount=-3 +kerning first=114 second=46 amount=-3 +kerning first=118 second=44 amount=-2 +kerning first=118 second=46 amount=-2 +kerning first=119 second=44 amount=-1 +kerning first=119 second=46 amount=-1 +kerning first=121 second=44 amount=-2 +kerning first=121 second=46 amount=-2 diff --git a/Snake/res/fonts/verdana_24_0.tga b/Snake/res/fonts/verdana_24_0.tga new file mode 100644 index 0000000..d636db2 Binary files /dev/null and b/Snake/res/fonts/verdana_24_0.tga differ diff --git a/Snake/res/maps/iron_tileset.png b/Snake/res/maps/iron_tileset.png new file mode 100644 index 0000000..3d42f7e Binary files /dev/null and b/Snake/res/maps/iron_tileset.png differ diff --git a/Snake/res/maps/iron_tileset.tsx b/Snake/res/maps/iron_tileset.tsx new file mode 100644 index 0000000..2636f6a --- /dev/null +++ b/Snake/res/maps/iron_tileset.tsx @@ -0,0 +1,469 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Snake/res/maps/level1.tmx b/Snake/res/maps/level1.tmx new file mode 100644 index 0000000..f264511 --- /dev/null +++ b/Snake/res/maps/level1.tmx @@ -0,0 +1,9 @@ + + + + + + H4sIAAAAAAAACzNhYGAQJQKLAbEHEIsQqR6EjYFYAIh9aIT5Rs0f0eZzADEnGuaiovkgeX40TIp/R82nzHxsZY0YFc2ndfocNZ+BQY8BM49SC+sCMQDSKCG/wAYAAA== + + + diff --git a/Snake/src/de/fhtrier/gde/snake/Snake.java b/Snake/src/de/fhtrier/gde/snake/Snake.java new file mode 100644 index 0000000..e0df91c --- /dev/null +++ b/Snake/src/de/fhtrier/gde/snake/Snake.java @@ -0,0 +1,48 @@ +package de.fhtrier.gde.snake; + +import java.util.LinkedList; + +import org.newdawn.slick.GameContainer; +import org.newdawn.slick.Graphics; +import org.newdawn.slick.SlickException; + +public class Snake +{ + private LinkedList segments = new LinkedList<>(); + + private static float SNAKE_WIDTH = 5.0f; + private static float GROW_SPEED = 20.0f; + private static float START_LENGTH = 50.0f; + private static float MOVES_SPEED = 50.0f; + private float remainingGrowth = START_LENGTH; + + public enum Direction{UP,DOWN,LEFT,RIGHT}; + + + public void init(final GameContainer container) throws SlickException + { + + } + + public void update(final GameContainer container, final int delta) throws SlickException + { + + } + + public void render(final GameContainer container, final Graphics g) throws SlickException + { + + } + + public void keyPressed(final int key, final char c) + { + + } + + public void eatApple() + { + + } + + +} diff --git a/Snake/src/de/fhtrier/gde/snake/SnakeGame.java b/Snake/src/de/fhtrier/gde/snake/SnakeGame.java new file mode 100644 index 0000000..07f9370 --- /dev/null +++ b/Snake/src/de/fhtrier/gde/snake/SnakeGame.java @@ -0,0 +1,96 @@ +package de.fhtrier.gde.snake; + +import java.io.File; + +import org.lwjgl.LWJGLUtil; +import org.newdawn.slick.AngelCodeFont; +import org.newdawn.slick.AppGameContainer; +import org.newdawn.slick.BasicGame; +import org.newdawn.slick.Color; +import org.newdawn.slick.Font; +import org.newdawn.slick.GameContainer; +import org.newdawn.slick.Graphics; +import org.newdawn.slick.SlickException; + +public class SnakeGame extends BasicGame +{ + + private Font font; + + private World world; + + private Snake snake; + + private SnakeSegment segment; + + public SnakeGame(final String[] args) + { + super("Snake"); + } + + @Override + public void init(final GameContainer container) throws SlickException + { + this.world = new World(); + this.segment = new SnakeSegment(); + this.snake = new Snake(); + + this.font = SnakeGame.createFont("verdana_24"); + this.world.init(container); + this.snake.init(container); + } + + @Override + public void update(final GameContainer container, final int delta) throws SlickException + { + this.world.update(container, delta); + this.snake.update(container, delta); + } + + @Override + public void render(final GameContainer container, final Graphics g) throws SlickException + { + this.world.render(container, g); + this.snake.render(container, g); + + g.setColor(Color.white); + this.font.drawString(30, -2, "Snake"); + } + + @Override + public void keyPressed(final int key, final char c) + { + this.world.keyPressed(key, c); + this.snake.keyPressed(key, c); + } + + public static Font createFont(final String name) throws SlickException + { + return new AngelCodeFont("res/fonts/" + name + ".fnt", "res/fonts/" + name + "_0.tga"); + } + + public static void main(final String[] args) + { + try + { + // Platform specific dll loading + System.setProperty("org.lwjgl.librarypath", new File(new File(System.getProperty("user.dir"), "native"), LWJGLUtil.getPlatformName()).getAbsolutePath()); + System.setProperty("net.java.games.input.librarypath", System.getProperty("org.lwjgl.librarypath")); + + // Create a new game + final SnakeGame instance = new SnakeGame(args); + final AppGameContainer app = new AppGameContainer(instance); + app.setDisplayMode(640, 480, false); + app.setAlwaysRender(true); + app.setVSync(true); + app.setShowFPS(false); + + app.start(); + } + catch (final SlickException e) + { + e.printStackTrace(); + } + + } +} \ No newline at end of file diff --git a/Snake/src/de/fhtrier/gde/snake/SnakeSegment.java b/Snake/src/de/fhtrier/gde/snake/SnakeSegment.java new file mode 100644 index 0000000..727ba64 --- /dev/null +++ b/Snake/src/de/fhtrier/gde/snake/SnakeSegment.java @@ -0,0 +1,29 @@ +package de.fhtrier.gde.snake; + +import org.newdawn.slick.geom.Rectangle; + +import de.fhtrier.gde.snake.Snake.Direction; + +public class SnakeSegment +{ + Rectangle rect; + + Snake.Direction dir; + + SnakeSegment() + { + + this.dir = Direction.DOWN; + + } + + public void grow() + { + + } + + public void shrink() + { + + } +} diff --git a/Snake/src/de/fhtrier/gde/snake/World.java b/Snake/src/de/fhtrier/gde/snake/World.java new file mode 100644 index 0000000..23f714e --- /dev/null +++ b/Snake/src/de/fhtrier/gde/snake/World.java @@ -0,0 +1,34 @@ +package de.fhtrier.gde.snake; + +import org.newdawn.slick.AppGameContainer; +import org.newdawn.slick.GameContainer; +import org.newdawn.slick.Graphics; +import org.newdawn.slick.SlickException; +import org.newdawn.slick.tiled.TiledMap; + +public class World +{ + private TiledMap map; + + public void init(final GameContainer container) throws SlickException + { + this.map = new TiledMap("res/maps/level1.tmx"); + ((AppGameContainer) container).setDisplayMode(this.map.getWidth() * this.map.getTileWidth(), this.map.getHeight() * this.map.getTileHeight(), false); + } + + public void update(final GameContainer container, final int delta) throws SlickException + { + + } + + public void render(final GameContainer container, final Graphics g) throws SlickException + { + this.map.render(0, 0); + } + + public void keyPressed(final int key, final char c) + { + + } + +} diff --git a/bin/LoA/AwesomeGuy.class b/bin/LoA/AwesomeGuy.class new file mode 100644 index 0000000..99d52a1 Binary files /dev/null and b/bin/LoA/AwesomeGuy.class differ diff --git a/bin/LoA/Block.class b/bin/LoA/Block.class new file mode 100644 index 0000000..3b4a27a Binary files /dev/null and b/bin/LoA/Block.class differ diff --git a/bin/LoA/BlockMap.class b/bin/LoA/BlockMap.class new file mode 100644 index 0000000..3745a47 Binary files /dev/null and b/bin/LoA/BlockMap.class differ diff --git a/bin/LoA/Legend_of_Awesome.class b/bin/LoA/Legend_of_Awesome.class new file mode 100644 index 0000000..3faf5d6 Binary files /dev/null and b/bin/LoA/Legend_of_Awesome.class differ diff --git a/bin/LoA/Overworld_BigTiles.png b/bin/LoA/Overworld_BigTiles.png new file mode 100644 index 0000000..33526f3 Binary files /dev/null and b/bin/LoA/Overworld_BigTiles.png differ diff --git a/bin/LoA/Overworld_Map.tmx b/bin/LoA/Overworld_Map.tmx new file mode 100644 index 0000000..b5d6e63 --- /dev/null +++ b/bin/LoA/Overworld_Map.tmx @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + H4sIAAAAAAAAC+3b2wnAIBREQQPpv+Z0ECMBH3dnwBLOfgi3NQAg3XW/P6CmXuM2AGr62rYNgHpGmtY/1KJ/yKV/yKV/yKV/yKV/yKV/yKV/yKV/yDXavw2AOkZ77t0JuSOCc8xs0gbAXmb3aANgHyta1D/sQf+QS/+Qa8U/vv7hXH83QP9wNv0DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC7eADJYKY9AAAEAA== + + + + + H4sIAAAAAAAAC+3bW06DQABAUTB+sI+2K3HltTFxG1rdh5DapCFAeQzMAOfjJE0VeZg7QDsUeZYVD76zdkXerms5IE1DO9Y+bMeYhrUP29DUcJ9r/LbljqVz+fq9Q+x9Bm6auu5zn9927q/e/0pgv4Dn6k33UV/uUPsb+od1CNH//f1Trn9Yk5D9F/qHVQnR//HhZ9W9QF566RB7n4Gbsf0D6zel/1THgs9EtwtSM7X/EGIfA9ires/nrH3ezqXh99fYf5bAcYcU1Hvu+uz+moXrNXb/2f/r19JbAv8HiOGxv9OC/d/XHfP6X/vs3Zjz/ynwGBBjv7UPw/r/ybqf6+nj8mQblqB9uBnSfwjVGFIfA5a+DzgkcNwhBUv3X6mPAb4LhDhi9F+51rbBGADLe+ytz9z9Jh8j1tvVvzEAljG1tcPIa4Zn/RsDYH4hWpurf2MAzCvEvN05+zcGwHyc/2G/QjQ2tX8gjqn9j/3OUP8Q35Dnf9v8jlhv0zxAYFmx5v9U+jxPEPv4wJYNef4X2JaY538gLv3Dfukf9kv/sF/6BwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOj2B38lG8MAAAQA + + + + + H4sIAAAAAAAAC+3dy44UVRzA4arpBC8o+gDuQJGL0QdQlwoCJvoSjAkYNdFXkJ0bLxEWbt2x8RIgXlagicJKDerClQPibaPGy8JzMn3STdPdU101Xadm6vuSf6bbzFijye/UdHVXVVHc7EaYB8vpc6W41f3l+tcv5vzcsuZymF/DdtfKKb8YUElqOLb/ZZhD5fS5XIzWgGfC82fH2t8342eWOWvWANgUqf1fwqyW0yd2ltaAF8Pzl8pR+4dn/Mwy57aVojg7XAPuCXPJGgALi+3/XKy3f/eMhvaEf75rYg0Yb/+pDO3tDf1fietAmIv6h1p2hm7uKkft757T0a7hvvbeMAeGP/dcxu526B8auSN0c2eFdvYMvyc1F/s/mLk5/UN9senJ/seP7U17rn/YHsb7T/v3dGxv98TzRP+wfVT9+z+9T6h/2D7G+98zp6HdE/0fCXM0zLEwnxX15/MGv3vT/q/Hv39WGvwCsMVN7v/TGjD5uj9JzUUnwtdrRbO5Huanmr97k/5T+/v0T4/N+vt/8nV/Mt7/6qD59pu8jqjbv/Zh/TV96n/e+/7jxvs/tQX71z6MVD3+lzwQvndvWf+8nd8n/n07W+xf+3CzRft/Inzvk2X983b2TqwBbe3/tQ+3WrT/6HhZ/7yduHbEcwjSGpDeR6hzDLBq/9qH6er030RcO+J5RL8VozUgvo9Q532A8f7jOcDXpoz2Yba2+4/ia4h0HlFypMYakPqPxyRuX5n+ekP7MFuO/qPx9xGSRdeAdCxyx8rs1xux/f1hTg+3dSZ8vW8z/0NgC+tS/9Eia0D8O+LVYvbf/mnOhTk/3NaF8PXR8HV1Yh7ZnP8s2FK61n+0yBpwfLgGzHvP4erEdr4Pz08Vozk0PCZpDaBv6n6Ov8nn9qN5/affa5E1YN57Dn9PbOe/8PzdYjTp+mbxmKQ1gL6Jx99Plu19bj/aqP+ozjHButK1jV5oYVvQFXU/w9/0dUOV/jdjO1VdDPOw/umZ+Bn+Otfvj9f+3L/k/tM1Rptsp4rY/o9hXgtzdrmbgk6Jjc277v+saXrd3yrnEbRxfWHt02fp+r+Lfo636XV/q5xHcHgTtjOP9um7+Po613W8Njpur31Yrlzv/+ekfVjXt/61DyN96l/7cLO+9K99uFUf+tc+TLfd+9c+zLad+9c+zFe1/9jSpaLZvX7avEeQ9mFjVfuPLa0Vze/308Y9grQP1VTtv+r5esuwyGsU7UN1Xe//RlH9HEDtw2K63n/VcwC1D4vrav9pv699WJ6u9p/a3+gcQO1DfV3r334f2tO1/u33oT1d6d9+H9rXlf7t96F9ufu334d8cvdvvw/55Orffh/yy9W//T7k13b/9vvQHVX7r3K/npfDvLLB99jvQ3dU7b/K/XoeWymKx1ea3zdM+9CORc6t3+h+Pan/Jvf00T60p0vX/9M+tKsr/Wsf2teF/rUPeeTuX/uQT87+tQ95HQjtHwzzUMtrgPYhvyOh+6NhjpXt3tPj6VL70AUnQosny3bv6fF8qX3I6YNBUbwd5vQg3+Mzg9z/F6CfXg/tnQtzfpDv8QX9Qxbvhfauhvl2kO/xd/qHLP4K7f0R5s9Bnsf/hPlX/5DFx6G9T8J8NMjz+FPtQzZvhv7eCvPGIM/jH/QP2cTj8B+GeX+Q57Hj/5CP4//QX/E4/Fdhvg7zTabHjv9DHjmP/78zcPwfcnL8H/rL8X8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAluF/ni3Q2gAABAA= + + + + + H4sIAAAAAAAAC+3WoRWAMAxF0ZQxQMAcsP8cdBMIqhYX0Xsn+OblJAIAYLhbRG/VK4AKz9f+Ur0CqLBn+8fP/te8FZtfAaZ0ZvuX/gEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYCovrHUboAAABAA= + + + diff --git a/bin/LoA/Overworld_tiles.png b/bin/LoA/Overworld_tiles.png new file mode 100644 index 0000000..0f37970 Binary files /dev/null and b/bin/LoA/Overworld_tiles.png differ diff --git a/bin/LoA/Rotes Viereck.jpg b/bin/LoA/Rotes Viereck.jpg new file mode 100644 index 0000000..9b895ee Binary files /dev/null and b/bin/LoA/Rotes Viereck.jpg differ diff --git a/bin/LoA/Scroller.class b/bin/LoA/Scroller.class new file mode 100644 index 0000000..394279e Binary files /dev/null and b/bin/LoA/Scroller.class differ diff --git a/bin/LoA/guy.png b/bin/LoA/guy.png new file mode 100644 index 0000000..dbee344 Binary files /dev/null and b/bin/LoA/guy.png differ diff --git a/bin/LoA/testing.png b/bin/LoA/testing.png new file mode 100644 index 0000000..47fa479 Binary files /dev/null and b/bin/LoA/testing.png differ diff --git a/bin/LoA/testmap.tmx b/bin/LoA/testmap.tmx new file mode 100644 index 0000000..984b8ef --- /dev/null +++ b/bin/LoA/testmap.tmx @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + H4sIAAAAAAAAC1NgYGBQGMWjeBSP4lE8ikfxkMYAph2yDtAHAAA= + + + + + + + + H4sIAAAAAAAAC9NlYGDQHcWDGlMCRu0YtWPUjlE7RvHIwAAYq2vo0AcAAA== + + + diff --git a/bin/Main/AwesomeGuy.class b/bin/Main/AwesomeGuy.class new file mode 100644 index 0000000..ba90b31 Binary files /dev/null and b/bin/Main/AwesomeGuy.class differ diff --git a/bin/Main/Ball.class b/bin/Main/Ball.class new file mode 100644 index 0000000..de2cd70 Binary files /dev/null and b/bin/Main/Ball.class differ diff --git a/bin/Main/Hack_Alert.png b/bin/Main/Hack_Alert.png new file mode 100644 index 0000000..5d728ab Binary files /dev/null and b/bin/Main/Hack_Alert.png differ diff --git a/bin/Main/Invader.class b/bin/Main/Invader.class new file mode 100644 index 0000000..a6ac0f0 Binary files /dev/null and b/bin/Main/Invader.class differ diff --git a/bin/Main/JnP.class b/bin/Main/JnP.class new file mode 100644 index 0000000..47b3581 Binary files /dev/null and b/bin/Main/JnP.class differ diff --git a/bin/Main/Level.class b/bin/Main/Level.class new file mode 100644 index 0000000..a125397 Binary files /dev/null and b/bin/Main/Level.class differ diff --git a/bin/Main/LoA_MainGame.class b/bin/Main/LoA_MainGame.class new file mode 100644 index 0000000..7325435 Binary files /dev/null and b/bin/Main/LoA_MainGame.class differ diff --git a/bin/Main/Map.class b/bin/Main/Map.class new file mode 100644 index 0000000..3d31e24 Binary files /dev/null and b/bin/Main/Map.class differ diff --git a/bin/Main/Menu.class b/bin/Main/Menu.class new file mode 100644 index 0000000..0116e10 Binary files /dev/null and b/bin/Main/Menu.class differ diff --git a/bin/Main/Panel.class b/bin/Main/Panel.class new file mode 100644 index 0000000..0a16676 Binary files /dev/null and b/bin/Main/Panel.class differ diff --git a/bin/Main/Panel.png b/bin/Main/Panel.png new file mode 100644 index 0000000..6aeb197 Binary files /dev/null and b/bin/Main/Panel.png differ diff --git a/bin/Main/Player.class b/bin/Main/Player.class new file mode 100644 index 0000000..9ced49b Binary files /dev/null and b/bin/Main/Player.class differ diff --git a/bin/Main/Robot_sheet.png b/bin/Main/Robot_sheet.png new file mode 100644 index 0000000..df23405 Binary files /dev/null and b/bin/Main/Robot_sheet.png differ diff --git a/bin/Main/Robot_sheet1.png b/bin/Main/Robot_sheet1.png new file mode 100644 index 0000000..258c4be Binary files /dev/null and b/bin/Main/Robot_sheet1.png differ diff --git a/bin/Main/State.class b/bin/Main/State.class new file mode 100644 index 0000000..26aef3d Binary files /dev/null and b/bin/Main/State.class differ diff --git a/bin/Main/UserInterface.class b/bin/Main/UserInterface.class new file mode 100644 index 0000000..33dc58e Binary files /dev/null and b/bin/Main/UserInterface.class differ diff --git a/bin/Main/Virus_sheet.png b/bin/Main/Virus_sheet.png new file mode 100644 index 0000000..eba9e0f Binary files /dev/null and b/bin/Main/Virus_sheet.png differ diff --git a/bin/Main/arrows_sheet.png b/bin/Main/arrows_sheet.png new file mode 100644 index 0000000..bcb5f72 Binary files /dev/null and b/bin/Main/arrows_sheet.png differ diff --git a/bin/Main/cybergrid.png b/bin/Main/cybergrid.png new file mode 100644 index 0000000..0af39f7 Binary files /dev/null and b/bin/Main/cybergrid.png differ diff --git a/bin/Main/destruction_bar.png b/bin/Main/destruction_bar.png new file mode 100644 index 0000000..74dff5b Binary files /dev/null and b/bin/Main/destruction_bar.png differ diff --git a/bin/Main/empty_bar.png b/bin/Main/empty_bar.png new file mode 100644 index 0000000..67358e1 Binary files /dev/null and b/bin/Main/empty_bar.png differ diff --git a/bin/Main/game_Over1.png b/bin/Main/game_Over1.png new file mode 100644 index 0000000..6187bfe Binary files /dev/null and b/bin/Main/game_Over1.png differ diff --git a/bin/Main/game_Over2.png b/bin/Main/game_Over2.png new file mode 100644 index 0000000..3a6d994 Binary files /dev/null and b/bin/Main/game_Over2.png differ diff --git a/bin/Main/game_Over3.png b/bin/Main/game_Over3.png new file mode 100644 index 0000000..c7cadee Binary files /dev/null and b/bin/Main/game_Over3.png differ diff --git a/bin/Main/game_Over4.png b/bin/Main/game_Over4.png new file mode 100644 index 0000000..c9cd823 Binary files /dev/null and b/bin/Main/game_Over4.png differ diff --git a/bin/Main/game_Over5.png b/bin/Main/game_Over5.png new file mode 100644 index 0000000..6e027e1 Binary files /dev/null and b/bin/Main/game_Over5.png differ diff --git a/bin/Main/game_Over6.png b/bin/Main/game_Over6.png new file mode 100644 index 0000000..f649b89 Binary files /dev/null and b/bin/Main/game_Over6.png differ diff --git a/bin/Main/game_Over7.png b/bin/Main/game_Over7.png new file mode 100644 index 0000000..9ae010e Binary files /dev/null and b/bin/Main/game_Over7.png differ diff --git a/bin/Main/game_Over8.png b/bin/Main/game_Over8.png new file mode 100644 index 0000000..9bad6ca Binary files /dev/null and b/bin/Main/game_Over8.png differ diff --git a/bin/Main/key_instructions.png b/bin/Main/key_instructions.png new file mode 100644 index 0000000..e34dc44 Binary files /dev/null and b/bin/Main/key_instructions.png differ diff --git a/bin/Main/level_cleared_screen.png b/bin/Main/level_cleared_screen.png new file mode 100644 index 0000000..721d3d2 Binary files /dev/null and b/bin/Main/level_cleared_screen.png differ diff --git a/bin/Main/mechrider.png b/bin/Main/mechrider.png new file mode 100644 index 0000000..06b7ee8 Binary files /dev/null and b/bin/Main/mechrider.png differ diff --git a/bin/Main/menu.png b/bin/Main/menu.png new file mode 100644 index 0000000..4c13c33 Binary files /dev/null and b/bin/Main/menu.png differ diff --git a/bin/Main/mission.png b/bin/Main/mission.png new file mode 100644 index 0000000..2e51d10 Binary files /dev/null and b/bin/Main/mission.png differ diff --git a/bin/Main/paused_menu.png b/bin/Main/paused_menu.png new file mode 100644 index 0000000..1c5dc9e Binary files /dev/null and b/bin/Main/paused_menu.png differ diff --git a/bin/Main/resources/BasicLink.png b/bin/Main/resources/BasicLink.png new file mode 100644 index 0000000..9c148bf Binary files /dev/null and b/bin/Main/resources/BasicLink.png differ diff --git a/bin/Main/resources/OverworldMapBig.tmx b/bin/Main/resources/OverworldMapBig.tmx new file mode 100644 index 0000000..878ef67 --- /dev/null +++ b/bin/Main/resources/OverworldMapBig.tmx @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + H4sIAAAAAAAAC+3bMQ7CMBBE0dBQc//TIiSqFAMGe5ON3pO2s0Q1vyLbBgCwxu0x54DrmdUHnYDrmd0HrYDrWNkHjYDeVvdBL6Cv0Q3/81YjoJ/R/b7e3N/37XuNgL5GtzvaiP1vaAT0Ut0IoJeK/WoE9KURQKIRQKIRQKIRQKIRQKIRQKIRQKIRQKIRQKIRQKIRQKIRQKIRQKIRQKIRQKIRQFLdiF8POIZGAEnVDjUCeqrcoUZAP9U71AjoxQ6BRCOARCOARCOARCOARCOARCOARCOAxH+VgE9mfHM1+4BzOboJGgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOfwBN8z8DRESQQA + + + + + H4sIAAAAAAAAC+3cSZYjBxHH4eL573X4CozXCa7AcBXGDWxggVcGrsJ4BLxiPAfSs/NZna1qt6uHiIJvEe/rzpbUtdGvQsqUKg8PH12mTv7qg4eHX1/mF5c///Iyv3lkPv58Hvu38+OSfF72Zb5742Mt+Nvnc68PH9/5+zHnxyf5vDx6cW7BeT7N/T3h3ItPT/OqPpHc77UTr2rDeX84H7vXiaf2iuQ+r5243RWOeew1x2OvQ45GXO/7VTtFcq9f9hrjq8y1D6/TJZLPx/Nz/LF5nd6Q/N+z80Ufbj3mR5f5cZ7Wn59c5qdPuB/JPVY+68X53OVx/A+X+WOe1p8/XebPT7gfyT127p+//PqHDw/fuMw3L/OtD5/eobf1OCRnPO8Px2uN712ez9+/zA8u88MPn96ht/U4JGe8d87zeuyTy/P5t5f53WV+/wa//9/W45Cc8ejF+Rzo3y/P539c5p+X+dcb/P5/W49DcsZrJ669OPpwu1Ncj//sMj/P0/pz3Per3o/kHiuf9eJ8jcTxPsVfLvPXvLozxznO8/Hjvo/dj+R+711XfW3E7XuYR08e2wuOc5zn4ySfv49dU3304bYnr9oLjn6cj5N83p7fh7j987Uj//7g4eE/H3z5dQ5HP+7d7rhW8/b2JJ+H9z6ncXud5bcv8528/nUO9253XKt57/Ykd3vtxPlzn9dOHO9DHnvE+TqHxz7Hcb7dm5wXITnvtRP3Pvd5vA957BHn6xwe+xzH+Xavc16E5F7vfWel74EgeXivD+/j+6y/9tF8H0l+uY99x0zn3fbp2oh3+fgk346VmT7ZI8jnYeflblyfv/eOvw2P/cEeQT4PKy934/r8vXf8bXjsD/YI8nnYefn3vD2C5GHl5d/z72uPeOq8i5+L5J494k3nXfxcJHfsEZU3b8S7+rlIzu8R5//HHkHu9tyN97FHHNojyP2eu/G+9giSz8PK3B5Bcr+dF7thjyB5a+XFbtgjSN7aebEb9giSt1Ze7IY9guStnRe7YY8geWvli17cXoNwe5zk/6+d+U6R3GtlvlMk99qZ7xTJvVbmO0Vyr535TpHca2W+UyT32pnvFMm9VuY7RXKvnflOkdxrZb5TJPfame8Uyb1W5jtFcq+d+U6R3GtlvlMk99qZ7xTJvVbmO0Vyr535TpHca2W+UyT32pnvFMm9VuY7RXKvnflOkdxrZb5TJPfame8Uyb1W5jtFcq+d+U6R3GtlvlMk99qZ7xTJvVbmO0Vyr535TpHca2W+UyT32pnvFMm9VuY7RXKvnflOkdxrZb5TJPfame8Uyb1W5jtFcq+d+U6R3GtlvlMk99qZ7xTJvVbmO0Vyr535TpHca2W+UyT32pnvFMm9VuY7RXKvnflOkdxrZb5TJPfame8Uyb1W5jtFcq+d+U6R3GtlvlMk99qZ7xTJvVbmO0Vyr535TpHca2W+UyT32pnvFMm9VuY7RXKvnflOkdxrZb5TJPfame8Uyb1W5jtFcq+d+U6R3GtlvlMk99qZ7xTJvVbmO0Vyr535TpHca2W+UyT32pnvFMm9VuY7RXKvnflOkdxrZb5TJPfame8Uyb1W5jtFcq+d+U6R3GtlvlMk99qZ7xTJvVbmO0Vyr535TpHca2W+UyT32pnvFMm9VuY7RXKvnflOkdxrZb5TJPfame8Uyb1W5jtFcq+d+U6R3GtlvlMk99qZ7xTJvVbmO0Vyr535TpHca2W+UyT32pnvFMm9VuY7RXKvnflOkdxrZb5TJPfame8Uyb1W5jtFcq+d+U6R3GtlvlMk99qZ7xTJvVbmO0Vyr535TpHca2W+UyT32pnvFMm9VuY7RXKvnflOkdxrZb5TJPfame8Uyb1W5jtFcq+d+U6R3GtlvlMk99qZ7xTJvVbmO0Vyr535TpHca2W+UyT32pnvFMm9VuY7RXKvnflOkdxrZb5TJPfame8Uyb1W5jtFcq+d+U6R3GtlvlMk99qZ7xTJvVbmO0Vyr535TpHca2W+UyT32pnvFMm9VuY7RXKvnflOkdxrZb5TJPfame8Uyb1W5jtFcq+d+U6R3GtlvlMk99qZ7xTJvVbmO0Vyr535TpHca2W+UyT32pnvFMm9VuY7RXKvnflOkdxrZb5TJPfame8Uyb1W5jtFcq+d+U6R3GtlvlMk99qZ7xTJvVbmO0Vyr535TpHca2W+UyT32pnvFMm9VuY7RXKvnflOkdxrZb5TJPfame8Uyb1W5jtFcq+d+U6R3GtlvlMk99qZ7xTJvVbmO0Vyr535TpHca2W+UyT32pnvFMm9VuY7RXKvnflOkdxrZb5TJPfame8Uyb1W5jtFcq+d+U6R3GtlvlMk99qZ7xTJvVbmO0Vyr535TpHca2W+UyT32pnvFMm9VuY7RXKvnflOkdxrZb5TJPfame8Uyb1W5jtFcq+d+U6R3GtlvlMk99qZ7xTJvVbmO0Vyr535TpHca2W+UyT32pnvFMm9VuY7RXKvnflOkdxrZb5TJPfame8Uyb1W5jtFcq+d+U6R3GtlvlMk99qZ7xTJvVbmO0Vyr535TpHca2W+UyT32pnvFMm9VuY7RXKvnflOkdxrZb5TJPfame8Uyb1W5jtFcq+d+U6R3GtlvlMk99qZ7xTJvVbmO0Vyr535TpHca2W+UyT32pnvFMm9VuY7RXKvnflOkdxrZb5TJPfame8Uyb1W5jtFcq+d+U6R3GtlvlMk99qZ7xTJvVbmO0Vyr535TpHca2W+UyT32pnvFMm9VuY7RXKv/wVB2SuXREkEAA== + + + + + H4sIAAAAAAAAC+3dV5Nb5R0GcCWS4szQTHGlJF8BMhO4oSQhuaGZ4krLV4DMBG4oiUmZoZnqklC+AqTATapJmQGMKa4JhK8AKXCT8mh2z1iclV5r5bPeo/j3m3nmLLta7Zrx+/cj6ZxXnc54N/Q6nQ3zOZnWdDudtcnj+bk7ekt/BBbvmazRZxtcpy/kvl7sTvazN+d2W5K38n37e0t/BBbvQNbowQbX6d9yXx8eZ0ZU/WFdsj45q9/prOwv/fH8/KwLJpxfcKpbqnVarcMLk4vGrMeqP2xNtiU35Ps29Jf+eGt+1m1mBExkqdZptQ5vT+6orcfqcc3OZFeyO9kzeJyT79vRX/rjT/KzftrVJ6BkmnV6+YpO54rkyuSqFZOtw+eS57uffX6ielxzKDmcHEmODh7n5Pv295f++Jf8rL929QkomWad3pe5cH/yQPLgisnW4fvJB93PPj+xqtfprE7WJGuTdcn6JXwusXq9pnJ+Pr6gp09AyTTr9FeZC79OfpP8dkX5ttU6vDC5KPlS8uX5+9+Y46Zkc7Il2ZpsO4mvN9yan3VbT5+AkmnW6b8zF/6T/HcwH75Yvm21Dm9P7kjuTL49f//P5PhssjPZlexO9izDa5L6BIxXX6eTuDxz4YrkyuSq48yIynO57+cH50skLyYrk7OTc5Jzk/OW8XwFfQLGO5C1cTA5lByecJ3el7lwf/JA8uCEM+L93PcHg/Mlkg/nnxe4MbkpuTm5pQXnNOkTsNA0PWIabe4RFX0CFpqmR0xjFnpERZ+AY/SIhfQJOEaPGE+fAD2iRJ8APWISi+0TF+drlyRfSX6e/CL5anJpcpkZw4zRI45vsX3irnzt7uQ7yT+SfybfTe5J7jUjmDFt6hFHWjwnBup94tF8/Fhv4b46L+VrLyc/G+oRv0xeSV41I5gxy9UjKqv7nc6aZG2yLjkra2hlt7nj2ck5ybnJeSe4Put94s18vK+3cF+dj/K1j5O/D/WIfyWfJJ+aEcyY5eoRlU2ZC5uTLcnWwb4TWUMbus0db0xuanBODFR94oz8vmf2F+6rc3G+dknvWIeoXJrPXdabbN8daIt6j/hc/s5+PukmvQaO/eQL3fE9YnAN186sq13J7sG+E7ntjm5zxyeSJxueE1WfuC6/7/Uj9tW5K1+7u3esQ1Tuyefu7ZX33YG2qfeIr+Xv7NeTbyRXN3D8ZvKt7vgecag/l8PJkcG+E7nt/m5zx7eTd5ZgTgw8mt/3sRH76ryUP9/LIzrZK/ncq73yvjvQNvUe8b38Xf1+sj15qIHjD5IfFnrEUhs8llmqOfFm7nvfiH11Psqf7+MRf8ZP8rlPe+V9d6Bt6j1ieEZsr635Uh6q3a7672pGjOsRJ8NSzolplPbdgbap94jfZc38Ptk7n2rN7x3xuVHZOzQjXkv+kPxxGXtEpU1zorTvDrRNqUdUXaC+7veOyPYRt21Lj6iMmhM3J7csU88/Lf8fTh+8PpKcaUbQUuN6xPBMeKi27kel/nijPiMezn0/0ht/3tGJHid9faA+J55Knl6mGXFNfu9rB6+PJNebEbTU8XpE/fmIam6M+nh4ptRnxOu57zd64887OtHjY1POiXeT9/QIGKvUI0Y9ZznuNYzhxxqv1WbFj5PTsi5P748/76ip9wObpfMN9AhmQalHlJ6fHDUj6n2iyo9qc2LUeUdNvR/YYvrEctMjmAWTvK4x6vWM4RlRf96yPi/+NDQnrh1z3lHT78s3C3NCj2AWjOsR1WOG7bWPRz3+KM2H7UNz4s/zc2LUeUdNvy/fLPQJPYJZUOoR42bEqPMmSudcDc+Iqk90u5Nd7zH8ccm4fWDaNidOy+9zenJGV49gNozqEfVzI6oZMeq8iPpjknGzY/hxR+kcrHHPfVx9nLU+bh+YNvWJ4flwXXJh+s9FyZf6y/2bwXijekR9XTeR0vqfNJNoc5+4Jr/DtfPz4dHusflwpxlBi43qEcd7PWP4vIj6eRPjMuoxyWJ6xGsTrvE29onh/vDI/Hx4czC35ufDC2YELVa67vNE1vW4DD+/udgsRpv6xHB/eGN+PuxLnjcfmAHjXtcYXpujnntY7Po+kdkwzYxoU594uNYf9i1Tn4Fp1PemXtWbe11j+HHCuDVbemxRzzTfU8802tAnXq/1B5gl9R4x2Jv66ODf3/+T1+La0CecB8EsW9VfuDf1+n7z+1Mv537WA8vZJ5wHwSzb2F+4N/W2fvP7U0+7n3VT+zuU+sSk15/DqWx4b+o9/eb3p552P+um93eo94nFXKcOp7LhvamP9pvfn3ra/ayb3t+h3icWc5060B6jXm9pUtUnFnOdOtAe9fcC3djwjKj6xGKuUwfaY6l7RGUx16kD7bHUPQKYbSerRwCzSY8ASvQIoESPAMbZPDjPMjPhyeSp5One3D4XAM9mPuwcnGeZmfBO8m7yXm/u+lTg1DbcH87udzrnJOcm5/Xnrk8FTl31/nBjZsJNyc3JLf2561OBdlrbbf59vuvHen94InnSXIDWG/T/Ld3m3+e7fqz3h7eTd8wIaK013bn+MOj/u7rNv893/ag/wGyp+sPWZFu3+ff5rh/1B5gdBzITDiaHkt3Jnm7z7/NdP+oPMBuG58Ph5EhytNv8+3zXj/oDzIbBdRGrkzXJ2mRdst75S8C8wXURm5LNyZZka7LNjADm1a+3BBhWv94SYJgeAZToEUCJHgGU6BFAiR4BlOgRQIkeAZToEUCJHgGU6BFAiR4BlOgRQIkeAZToEUCJHgGU6BFAiR4BlOgRQIkeAZToEUCJHgGU6BFAiR4BlOgRQIkeAZToEUCJHgGU6BFAiR4BlOgRQMmBzIWDyaHkcHLEnACGrOp3OquTNcnaZF2yPjmr2+ms7DZ3BGbTxsyDTcnmZEuyNdmW3JB1vaHb3BGYbTszF3Ylu5M9yeNZ1zu6zR2B2XYoc+FwciQ5mryVdb2/29wRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAZv0P1AdBM0RJBAA= + + + diff --git a/bin/Main/resources/Overworld_BigTiles.png b/bin/Main/resources/Overworld_BigTiles.png new file mode 100644 index 0000000..f463c5b Binary files /dev/null and b/bin/Main/resources/Overworld_BigTiles.png differ diff --git a/bin/Main/resources/Rotes Viereck.jpg b/bin/Main/resources/Rotes Viereck.jpg new file mode 100644 index 0000000..98eedc5 Binary files /dev/null and b/bin/Main/resources/Rotes Viereck.jpg differ diff --git a/bin/Main/resources/ZeldaPersons.png b/bin/Main/resources/ZeldaPersons.png new file mode 100644 index 0000000..e4a8232 Binary files /dev/null and b/bin/Main/resources/ZeldaPersons.png differ diff --git a/bin/Main/resources/testRoom.tmx b/bin/Main/resources/testRoom.tmx new file mode 100644 index 0000000..9963134 --- /dev/null +++ b/bin/Main/resources/testRoom.tmx @@ -0,0 +1,24 @@ + + + + + + + + + + + H4sIAAAAAAAAC+2VSw6AIAxEceOa+5/WFRsUZfh1pPMSEkOwnZdgDUEIYc0R2xdbHnnMy7/6vV6v3lry6Ou98ltDQL128Fg9+xBaPdipvVfsyIMLeXAhDy68ebD9z3NQj7THhheP/Fx6tqZ0z//u0ZKN1QOdQRbz6y33qP7ywHrM8Piqi/YZXc+bB9qb2QPJljjjfdV4WOLJ4+nsTh4slHLJA+MCu3G7NkAfAAA= + + + + + H4sIAAAAAAAAC+3RsRGAMADDwFRZPGwWBiMtE0hwKnzq/WuOcZ2tj3ef3T8o/WMeeZhL/5hHHubSP+aRh7n0j3nkYS79Yx55mEv/mEce5tI/5pGHufSPeeRhLv1jHnmYS/+Yx7sPJLgP1UAfAAA= + + + + + H4sIAAAAAAAAC+2YSQ7DIAxFWYVTdThUu+xwo3bRLjvcqD1CnYUlSlPimI9DpDwJIRSwbPyxCM7NzMzksvDOLamtqK29/RjFjmztqR2oHb39GMWNbN2pPag9vf1YCyrPqHVaO6g8o9Zp7aDyjFontWNVL7RI/bOqF1qk/pWqFyik/tWmoz6kuqodqa76ONGcs//tpWwa57bNt60hoHT1ojlv/9tLuVAM1+bb1hDQuuray1SOOA9tn5NTlK6YcC/Zj64cxb62+cjJKbpehXsY+9H1jfvc84HWVWrP+VvoJ5+LVPwS0LpK7Xkc6z/fS9Yrqc5S/sTjcK5VvdLoLN73Lv/C2PiMaPJhcb+SnPcWzkkcvyQutK6GxNXnX6r+1nRv15wD1t7U7+2svan+H9b2zqCltncGLWO9Zw3N+1TeRa3tSUG/Y1q9i/ZRWldWlNaVFaV1NRa5uqqFXF3VQq6ucvkAdlAzyUAfAAA= + + + diff --git a/bin/Main/resources/worldMap.tmx b/bin/Main/resources/worldMap.tmx new file mode 100644 index 0000000..5798ab7 --- /dev/null +++ b/bin/Main/resources/worldMap.tmx @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + H4sIAAAAAAAAC+3b2wnAIBREQQPpv+Z0ECMBH3dnwBLOfgi3NQAg3XW/P6CmXuM2AGr62rYNgHpGmtY/1KJ/yKV/yKV/yKV/yKV/yKV/yKV/yKV/yDXavw2AOkZ77t0JuSOCc8xs0gbAXmb3aANgHyta1D/sQf+QS/+Qa8U/vv7hXH83QP9wNv0DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC7eADJYKY9AAAEAA== + + + + + H4sIAAAAAAAAC+3bW06DQABAUTB+sI+2K3HltTFxG1rdh5DapCFAeQzMAOfjJE0VeZg7QDsUeZYVD76zdkXerms5IE1DO9Y+bMeYhrUP29DUcJ9r/LbljqVz+fq9Q+x9Bm6auu5zn9927q/e/0pgv4Dn6k33UV/uUPsb+od1CNH//f1Trn9Yk5D9F/qHVQnR//HhZ9W9QF566RB7n4Gbsf0D6zel/1THgs9EtwtSM7X/EGIfA9ires/nrH3ezqXh99fYf5bAcYcU1Hvu+uz+moXrNXb/2f/r19JbAv8HiOGxv9OC/d/XHfP6X/vs3Zjz/ynwGBBjv7UPw/r/ybqf6+nj8mQblqB9uBnSfwjVGFIfA5a+DzgkcNwhBUv3X6mPAb4LhDhi9F+51rbBGADLe+ytz9z9Jh8j1tvVvzEAljG1tcPIa4Zn/RsDYH4hWpurf2MAzCvEvN05+zcGwHyc/2G/QjQ2tX8gjqn9j/3OUP8Q35Dnf9v8jlhv0zxAYFmx5v9U+jxPEPv4wJYNef4X2JaY538gLv3Dfukf9kv/sF/6BwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOj2B38lG8MAAAQA + + + + + H4sIAAAAAAAAC+3dy44UVRzA4arpBC8o+gDuQJGL0QdQlwoCJvoSjAkYNdFXkJ0bLxEWbt2x8RIgXlagicJKDerClQPibaPGy8JzMn3STdPdU101Xadm6vuSf6bbzFijye/UdHVXVVHc7EaYB8vpc6W41f3l+tcv5vzcsuZymF/DdtfKKb8YUElqOLb/ZZhD5fS5XIzWgGfC82fH2t8342eWOWvWANgUqf1fwqyW0yd2ltaAF8Pzl8pR+4dn/Mwy57aVojg7XAPuCXPJGgALi+3/XKy3f/eMhvaEf75rYg0Yb/+pDO3tDf1fietAmIv6h1p2hm7uKkft757T0a7hvvbeMAeGP/dcxu526B8auSN0c2eFdvYMvyc1F/s/mLk5/UN9senJ/seP7U17rn/YHsb7T/v3dGxv98TzRP+wfVT9+z+9T6h/2D7G+98zp6HdE/0fCXM0zLEwnxX15/MGv3vT/q/Hv39WGvwCsMVN7v/TGjD5uj9JzUUnwtdrRbO5Huanmr97k/5T+/v0T4/N+vt/8nV/Mt7/6qD59pu8jqjbv/Zh/TV96n/e+/7jxvs/tQX71z6MVD3+lzwQvndvWf+8nd8n/n07W+xf+3CzRft/Inzvk2X983b2TqwBbe3/tQ+3WrT/6HhZ/7yduHbEcwjSGpDeR6hzDLBq/9qH6er030RcO+J5RL8VozUgvo9Q532A8f7jOcDXpoz2Yba2+4/ia4h0HlFypMYakPqPxyRuX5n+ekP7MFuO/qPx9xGSRdeAdCxyx8rs1xux/f1hTg+3dSZ8vW8z/0NgC+tS/9Eia0D8O+LVYvbf/mnOhTk/3NaF8PXR8HV1Yh7ZnP8s2FK61n+0yBpwfLgGzHvP4erEdr4Pz08Vozk0PCZpDaBv6n6Ov8nn9qN5/affa5E1YN57Dn9PbOe/8PzdYjTp+mbxmKQ1gL6Jx99Plu19bj/aqP+ozjHButK1jV5oYVvQFXU/w9/0dUOV/jdjO1VdDPOw/umZ+Bn+Otfvj9f+3L/k/tM1Rptsp4rY/o9hXgtzdrmbgk6Jjc277v+saXrd3yrnEbRxfWHt02fp+r+Lfo636XV/q5xHcHgTtjOP9um7+Po613W8Njpur31Yrlzv/+ekfVjXt/61DyN96l/7cLO+9K99uFUf+tc+TLfd+9c+zLad+9c+zFe1/9jSpaLZvX7avEeQ9mFjVfuPLa0Vze/308Y9grQP1VTtv+r5esuwyGsU7UN1Xe//RlH9HEDtw2K63n/VcwC1D4vrav9pv699WJ6u9p/a3+gcQO1DfV3r334f2tO1/u33oT1d6d9+H9rXlf7t96F9ufu334d8cvdvvw/55Orffh/yy9W//T7k13b/9vvQHVX7r3K/npfDvLLB99jvQ3dU7b/K/XoeWymKx1ea3zdM+9CORc6t3+h+Pan/Jvf00T60p0vX/9M+tKsr/Wsf2teF/rUPeeTuX/uQT87+tQ95HQjtHwzzUMtrgPYhvyOh+6NhjpXt3tPj6VL70AUnQosny3bv6fF8qX3I6YNBUbwd5vQg3+Mzg9z/F6CfXg/tnQtzfpDv8QX9Qxbvhfauhvl2kO/xd/qHLP4K7f0R5s9Bnsf/hPlX/5DFx6G9T8J8NMjz+FPtQzZvhv7eCvPGIM/jH/QP2cTj8B+GeX+Q57Hj/5CP4//QX/E4/Fdhvg7zTabHjv9DHjmP/78zcPwfcnL8H/rL8X8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAluF/ni3Q2gAABAA= + + + + + H4sIAAAAAAAAC+3WoRWAMAxF0ZQxQMAcsP8cdBMIqhYX0Xsn+OblJAIAYLhbRG/VK4AKz9f+Ur0CqLBn+8fP/te8FZtfAaZ0ZvuX/gEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYCovrHUboAAABAA= + + + diff --git a/bin/Main/rider.png b/bin/Main/rider.png new file mode 100644 index 0000000..b57c440 Binary files /dev/null and b/bin/Main/rider.png differ diff --git a/bin/Main/robot_sheet_good.png b/bin/Main/robot_sheet_good.png new file mode 100644 index 0000000..ae06686 Binary files /dev/null and b/bin/Main/robot_sheet_good.png differ diff --git a/bin/Main/shield_bar.png b/bin/Main/shield_bar.png new file mode 100644 index 0000000..c3c4904 Binary files /dev/null and b/bin/Main/shield_bar.png differ diff --git a/bin/Main/shock.png b/bin/Main/shock.png new file mode 100644 index 0000000..6949fae Binary files /dev/null and b/bin/Main/shock.png differ diff --git a/bin/Main/titel.png b/bin/Main/titel.png new file mode 100644 index 0000000..808034f Binary files /dev/null and b/bin/Main/titel.png differ diff --git a/bin/Testspiel.zip b/bin/Testspiel.zip new file mode 100644 index 0000000..9f0e866 Binary files /dev/null and b/bin/Testspiel.zip differ diff --git a/bin/Testspiel/Auto.class b/bin/Testspiel/Auto.class new file mode 100644 index 0000000..26935b4 Binary files /dev/null and b/bin/Testspiel/Auto.class differ diff --git a/bin/Testspiel/Frosch.class b/bin/Testspiel/Frosch.class new file mode 100644 index 0000000..b0c2671 Binary files /dev/null and b/bin/Testspiel/Frosch.class differ diff --git a/bin/de/fhtrier/gdw/commons/netcode/NetConnection.class b/bin/de/fhtrier/gdw/commons/netcode/NetConnection.class new file mode 100644 index 0000000..3078da7 Binary files /dev/null and b/bin/de/fhtrier/gdw/commons/netcode/NetConnection.class differ diff --git a/bin/de/fhtrier/gdw/commons/netcode/NetReception.class b/bin/de/fhtrier/gdw/commons/netcode/NetReception.class new file mode 100644 index 0000000..44d6b87 Binary files /dev/null and b/bin/de/fhtrier/gdw/commons/netcode/NetReception.class differ diff --git a/bin/de/fhtrier/gdw/commons/netcode/datagram/INetDatagram$MessageType.class b/bin/de/fhtrier/gdw/commons/netcode/datagram/INetDatagram$MessageType.class new file mode 100644 index 0000000..e25879c Binary files /dev/null and b/bin/de/fhtrier/gdw/commons/netcode/datagram/INetDatagram$MessageType.class differ diff --git a/bin/de/fhtrier/gdw/commons/netcode/datagram/INetDatagram$Type.class b/bin/de/fhtrier/gdw/commons/netcode/datagram/INetDatagram$Type.class new file mode 100644 index 0000000..740243a Binary files /dev/null and b/bin/de/fhtrier/gdw/commons/netcode/datagram/INetDatagram$Type.class differ diff --git a/bin/de/fhtrier/gdw/commons/netcode/datagram/INetDatagram.class b/bin/de/fhtrier/gdw/commons/netcode/datagram/INetDatagram.class new file mode 100644 index 0000000..bc6ae98 Binary files /dev/null and b/bin/de/fhtrier/gdw/commons/netcode/datagram/INetDatagram.class differ diff --git a/bin/de/fhtrier/gdw/commons/netcode/datagram/INetDatagramFactory.class b/bin/de/fhtrier/gdw/commons/netcode/datagram/INetDatagramFactory.class new file mode 100644 index 0000000..ae993b6 Binary files /dev/null and b/bin/de/fhtrier/gdw/commons/netcode/datagram/INetDatagramFactory.class differ diff --git a/bin/de/fhtrier/gdw/commons/netcode/datagram/NetDatagram.class b/bin/de/fhtrier/gdw/commons/netcode/datagram/NetDatagram.class new file mode 100644 index 0000000..739d0db Binary files /dev/null and b/bin/de/fhtrier/gdw/commons/netcode/datagram/NetDatagram.class differ diff --git a/bin/de/fhtrier/gdw/commons/netcode/datagram/NetEventDatagram.class b/bin/de/fhtrier/gdw/commons/netcode/datagram/NetEventDatagram.class new file mode 100644 index 0000000..dd52324 Binary files /dev/null and b/bin/de/fhtrier/gdw/commons/netcode/datagram/NetEventDatagram.class differ diff --git a/bin/de/fhtrier/gdw/commons/netcode/examples/game/BaseGame$1.class b/bin/de/fhtrier/gdw/commons/netcode/examples/game/BaseGame$1.class new file mode 100644 index 0000000..b0cc708 Binary files /dev/null and b/bin/de/fhtrier/gdw/commons/netcode/examples/game/BaseGame$1.class differ diff --git a/bin/de/fhtrier/gdw/commons/netcode/examples/game/BaseGame.class b/bin/de/fhtrier/gdw/commons/netcode/examples/game/BaseGame.class new file mode 100644 index 0000000..034da1d Binary files /dev/null and b/bin/de/fhtrier/gdw/commons/netcode/examples/game/BaseGame.class differ diff --git a/bin/de/fhtrier/gdw/commons/netcode/examples/game/Client.class b/bin/de/fhtrier/gdw/commons/netcode/examples/game/Client.class new file mode 100644 index 0000000..f78b37f Binary files /dev/null and b/bin/de/fhtrier/gdw/commons/netcode/examples/game/Client.class differ diff --git a/bin/de/fhtrier/gdw/commons/netcode/examples/game/ClientGame$Entity.class b/bin/de/fhtrier/gdw/commons/netcode/examples/game/ClientGame$Entity.class new file mode 100644 index 0000000..d0eac29 Binary files /dev/null and b/bin/de/fhtrier/gdw/commons/netcode/examples/game/ClientGame$Entity.class differ diff --git a/bin/de/fhtrier/gdw/commons/netcode/examples/game/ClientGame.class b/bin/de/fhtrier/gdw/commons/netcode/examples/game/ClientGame.class new file mode 100644 index 0000000..4595cce Binary files /dev/null and b/bin/de/fhtrier/gdw/commons/netcode/examples/game/ClientGame.class differ diff --git a/bin/de/fhtrier/gdw/commons/netcode/examples/game/DatagramFactory.class b/bin/de/fhtrier/gdw/commons/netcode/examples/game/DatagramFactory.class new file mode 100644 index 0000000..90a186d Binary files /dev/null and b/bin/de/fhtrier/gdw/commons/netcode/examples/game/DatagramFactory.class differ diff --git a/bin/de/fhtrier/gdw/commons/netcode/examples/game/PlayerDatagram.class b/bin/de/fhtrier/gdw/commons/netcode/examples/game/PlayerDatagram.class new file mode 100644 index 0000000..0e7d27d Binary files /dev/null and b/bin/de/fhtrier/gdw/commons/netcode/examples/game/PlayerDatagram.class differ diff --git a/bin/de/fhtrier/gdw/commons/netcode/examples/game/Server.class b/bin/de/fhtrier/gdw/commons/netcode/examples/game/Server.class new file mode 100644 index 0000000..08a7b76 Binary files /dev/null and b/bin/de/fhtrier/gdw/commons/netcode/examples/game/Server.class differ diff --git a/bin/de/fhtrier/gdw/commons/netcode/examples/game/ServerGame$Entity.class b/bin/de/fhtrier/gdw/commons/netcode/examples/game/ServerGame$Entity.class new file mode 100644 index 0000000..397aa78 Binary files /dev/null and b/bin/de/fhtrier/gdw/commons/netcode/examples/game/ServerGame$Entity.class differ diff --git a/bin/de/fhtrier/gdw/commons/netcode/examples/game/ServerGame.class b/bin/de/fhtrier/gdw/commons/netcode/examples/game/ServerGame.class new file mode 100644 index 0000000..e5b13fd Binary files /dev/null and b/bin/de/fhtrier/gdw/commons/netcode/examples/game/ServerGame.class differ diff --git a/bin/de/fhtrier/gdw/commons/netcode/examples/pingpong/ChatDatagram.class b/bin/de/fhtrier/gdw/commons/netcode/examples/pingpong/ChatDatagram.class new file mode 100644 index 0000000..f428763 Binary files /dev/null and b/bin/de/fhtrier/gdw/commons/netcode/examples/pingpong/ChatDatagram.class differ diff --git a/bin/de/fhtrier/gdw/commons/netcode/examples/pingpong/ClientTest.class b/bin/de/fhtrier/gdw/commons/netcode/examples/pingpong/ClientTest.class new file mode 100644 index 0000000..7d51f80 Binary files /dev/null and b/bin/de/fhtrier/gdw/commons/netcode/examples/pingpong/ClientTest.class differ diff --git a/bin/de/fhtrier/gdw/commons/netcode/examples/pingpong/DatagramFactory.class b/bin/de/fhtrier/gdw/commons/netcode/examples/pingpong/DatagramFactory.class new file mode 100644 index 0000000..4076c47 Binary files /dev/null and b/bin/de/fhtrier/gdw/commons/netcode/examples/pingpong/DatagramFactory.class differ diff --git a/bin/de/fhtrier/gdw/commons/netcode/examples/pingpong/ServerTest.class b/bin/de/fhtrier/gdw/commons/netcode/examples/pingpong/ServerTest.class new file mode 100644 index 0000000..3d613f0 Binary files /dev/null and b/bin/de/fhtrier/gdw/commons/netcode/examples/pingpong/ServerTest.class differ diff --git a/bin/de/fhtrier/gdw/commons/netcode/examples/readme.txt b/bin/de/fhtrier/gdw/commons/netcode/examples/readme.txt new file mode 100644 index 0000000..3c1aeb5 --- /dev/null +++ b/bin/de/fhtrier/gdw/commons/netcode/examples/readme.txt @@ -0,0 +1,6 @@ +== PingPong == +This is a small example to show how a ping pong server can be achieved. + +== Game == +This is a small demo how delta compressed datagrams will only be send when they change. +The clients circle will be drawn red when a datagram arrived recently diff --git a/bin/de/fhtrier/gdw/commons/netcode/message/INetMessageIn.class b/bin/de/fhtrier/gdw/commons/netcode/message/INetMessageIn.class new file mode 100644 index 0000000..fbc84f6 Binary files /dev/null and b/bin/de/fhtrier/gdw/commons/netcode/message/INetMessageIn.class differ diff --git a/bin/de/fhtrier/gdw/commons/netcode/message/INetMessageInternal.class b/bin/de/fhtrier/gdw/commons/netcode/message/INetMessageInternal.class new file mode 100644 index 0000000..3c64de1 Binary files /dev/null and b/bin/de/fhtrier/gdw/commons/netcode/message/INetMessageInternal.class differ diff --git a/bin/de/fhtrier/gdw/commons/netcode/message/INetMessageOut.class b/bin/de/fhtrier/gdw/commons/netcode/message/INetMessageOut.class new file mode 100644 index 0000000..eaaa39c Binary files /dev/null and b/bin/de/fhtrier/gdw/commons/netcode/message/INetMessageOut.class differ diff --git a/bin/de/fhtrier/gdw/commons/netcode/message/NetMessage.class b/bin/de/fhtrier/gdw/commons/netcode/message/NetMessage.class new file mode 100644 index 0000000..9e371d7 Binary files /dev/null and b/bin/de/fhtrier/gdw/commons/netcode/message/NetMessage.class differ diff --git a/bin/de/fhtrier/gdw/commons/netcode/message/NetMessageAllocator.class b/bin/de/fhtrier/gdw/commons/netcode/message/NetMessageAllocator.class new file mode 100644 index 0000000..f815fa8 Binary files /dev/null and b/bin/de/fhtrier/gdw/commons/netcode/message/NetMessageAllocator.class differ diff --git a/bin/de/fhtrier/gdw/commons/netcode/message/NetMessageCache.class b/bin/de/fhtrier/gdw/commons/netcode/message/NetMessageCache.class new file mode 100644 index 0000000..a16a05a Binary files /dev/null and b/bin/de/fhtrier/gdw/commons/netcode/message/NetMessageCache.class differ diff --git a/bin/de/fhtrier/gdw/commons/netcode/message/NetMessageDelta.class b/bin/de/fhtrier/gdw/commons/netcode/message/NetMessageDelta.class new file mode 100644 index 0000000..32a1103 Binary files /dev/null and b/bin/de/fhtrier/gdw/commons/netcode/message/NetMessageDelta.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/SotfGame.class b/bin/de/fhtrier/gdw2/sotf/SotfGame.class new file mode 100644 index 0000000..24ab743 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/SotfGame.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/Config$Animations$Animation.class b/bin/de/fhtrier/gdw2/sotf/config/Config$Animations$Animation.class new file mode 100644 index 0000000..41c8f23 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/Config$Animations$Animation.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/Config$Animations.class b/bin/de/fhtrier/gdw2/sotf/config/Config$Animations.class new file mode 100644 index 0000000..002ac2e Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/Config$Animations.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/Config$Eatables$Eatable$Energy.class b/bin/de/fhtrier/gdw2/sotf/config/Config$Eatables$Eatable$Energy.class new file mode 100644 index 0000000..e2d2943 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/Config$Eatables$Eatable$Energy.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/Config$Eatables$Eatable$Image.class b/bin/de/fhtrier/gdw2/sotf/config/Config$Eatables$Eatable$Image.class new file mode 100644 index 0000000..d233915 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/Config$Eatables$Eatable$Image.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/Config$Eatables$Eatable$Powerups$Powerup.class b/bin/de/fhtrier/gdw2/sotf/config/Config$Eatables$Eatable$Powerups$Powerup.class new file mode 100644 index 0000000..e97dd96 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/Config$Eatables$Eatable$Powerups$Powerup.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/Config$Eatables$Eatable$Powerups.class b/bin/de/fhtrier/gdw2/sotf/config/Config$Eatables$Eatable$Powerups.class new file mode 100644 index 0000000..77c4fe6 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/Config$Eatables$Eatable$Powerups.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/Config$Eatables$Eatable$Sound.class b/bin/de/fhtrier/gdw2/sotf/config/Config$Eatables$Eatable$Sound.class new file mode 100644 index 0000000..c45f48a Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/Config$Eatables$Eatable$Sound.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/Config$Eatables$Eatable$Usable.class b/bin/de/fhtrier/gdw2/sotf/config/Config$Eatables$Eatable$Usable.class new file mode 100644 index 0000000..e6296a9 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/Config$Eatables$Eatable$Usable.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/Config$Eatables$Eatable.class b/bin/de/fhtrier/gdw2/sotf/config/Config$Eatables$Eatable.class new file mode 100644 index 0000000..36975d9 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/Config$Eatables$Eatable.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/Config$Eatables.class b/bin/de/fhtrier/gdw2/sotf/config/Config$Eatables.class new file mode 100644 index 0000000..3d8897a Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/Config$Eatables.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/Config$Effects$Effect.class b/bin/de/fhtrier/gdw2/sotf/config/Config$Effects$Effect.class new file mode 100644 index 0000000..888089e Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/Config$Effects$Effect.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/Config$Effects.class b/bin/de/fhtrier/gdw2/sotf/config/Config$Effects.class new file mode 100644 index 0000000..cf21cbc Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/Config$Effects.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/Config$FloatValues$Value.class b/bin/de/fhtrier/gdw2/sotf/config/Config$FloatValues$Value.class new file mode 100644 index 0000000..cd8ed5d Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/Config$FloatValues$Value.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/Config$FloatValues.class b/bin/de/fhtrier/gdw2/sotf/config/Config$FloatValues.class new file mode 100644 index 0000000..d346fa0 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/Config$FloatValues.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/Config$Fonts$Font.class b/bin/de/fhtrier/gdw2/sotf/config/Config$Fonts$Font.class new file mode 100644 index 0000000..348e692 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/Config$Fonts$Font.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/Config$Fonts.class b/bin/de/fhtrier/gdw2/sotf/config/Config$Fonts.class new file mode 100644 index 0000000..050b089 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/Config$Fonts.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/Config$Images$Image.class b/bin/de/fhtrier/gdw2/sotf/config/Config$Images$Image.class new file mode 100644 index 0000000..155e04a Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/Config$Images$Image.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/Config$Images.class b/bin/de/fhtrier/gdw2/sotf/config/Config$Images.class new file mode 100644 index 0000000..fc40898 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/Config$Images.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/Config$Maps$Map.class b/bin/de/fhtrier/gdw2/sotf/config/Config$Maps$Map.class new file mode 100644 index 0000000..880f2a3 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/Config$Maps$Map.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/Config$Maps.class b/bin/de/fhtrier/gdw2/sotf/config/Config$Maps.class new file mode 100644 index 0000000..5d61e10 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/Config$Maps.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/Config$Musics$Music.class b/bin/de/fhtrier/gdw2/sotf/config/Config$Musics$Music.class new file mode 100644 index 0000000..67c7a9a Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/Config$Musics$Music.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/Config$Musics.class b/bin/de/fhtrier/gdw2/sotf/config/Config$Musics.class new file mode 100644 index 0000000..0fdc84d Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/Config$Musics.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/Config$Sounds$Sound$Src.class b/bin/de/fhtrier/gdw2/sotf/config/Config$Sounds$Sound$Src.class new file mode 100644 index 0000000..70b922a Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/Config$Sounds$Sound$Src.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/Config$Sounds$Sound.class b/bin/de/fhtrier/gdw2/sotf/config/Config$Sounds$Sound.class new file mode 100644 index 0000000..49d2cc5 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/Config$Sounds$Sound.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/Config$Sounds.class b/bin/de/fhtrier/gdw2/sotf/config/Config$Sounds.class new file mode 100644 index 0000000..5747b8e Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/Config$Sounds.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/Config$StringValues$Value.class b/bin/de/fhtrier/gdw2/sotf/config/Config$StringValues$Value.class new file mode 100644 index 0000000..2edae3c Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/Config$StringValues$Value.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/Config$StringValues.class b/bin/de/fhtrier/gdw2/sotf/config/Config$StringValues.class new file mode 100644 index 0000000..6004ec9 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/Config$StringValues.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/Config$Usables$Usable$Action.class b/bin/de/fhtrier/gdw2/sotf/config/Config$Usables$Usable$Action.class new file mode 100644 index 0000000..6f3b9bf Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/Config$Usables$Usable$Action.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/Config$Usables$Usable$Image.class b/bin/de/fhtrier/gdw2/sotf/config/Config$Usables$Usable$Image.class new file mode 100644 index 0000000..fd0103f Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/Config$Usables$Usable$Image.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/Config$Usables$Usable$Sound.class b/bin/de/fhtrier/gdw2/sotf/config/Config$Usables$Usable$Sound.class new file mode 100644 index 0000000..718e4b6 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/Config$Usables$Usable$Sound.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/Config$Usables$Usable.class b/bin/de/fhtrier/gdw2/sotf/config/Config$Usables$Usable.class new file mode 100644 index 0000000..18cdbf6 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/Config$Usables$Usable.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/Config$Usables.class b/bin/de/fhtrier/gdw2/sotf/config/Config$Usables.class new file mode 100644 index 0000000..534703c Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/Config$Usables.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/Config.class b/bin/de/fhtrier/gdw2/sotf/config/Config.class new file mode 100644 index 0000000..340397d Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/Config.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/config/ObjectFactory.class b/bin/de/fhtrier/gdw2/sotf/config/ObjectFactory.class new file mode 100644 index 0000000..16f6152 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/config/ObjectFactory.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/events/Anchor$AnchorPos.class b/bin/de/fhtrier/gdw2/sotf/events/Anchor$AnchorPos.class new file mode 100644 index 0000000..197e730 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/events/Anchor$AnchorPos.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/events/Anchor.class b/bin/de/fhtrier/gdw2/sotf/events/Anchor.class new file mode 100644 index 0000000..5accebb Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/events/Anchor.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/events/GameEvent.class b/bin/de/fhtrier/gdw2/sotf/events/GameEvent.class new file mode 100644 index 0000000..01f8cd4 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/events/GameEvent.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/events/GameEventManager$1.class b/bin/de/fhtrier/gdw2/sotf/events/GameEventManager$1.class new file mode 100644 index 0000000..121683f Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/events/GameEventManager$1.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/events/GameEventManager.class b/bin/de/fhtrier/gdw2/sotf/events/GameEventManager.class new file mode 100644 index 0000000..7272b27 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/events/GameEventManager.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/events/GameEventSchedule.class b/bin/de/fhtrier/gdw2/sotf/events/GameEventSchedule.class new file mode 100644 index 0000000..a69be39 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/events/GameEventSchedule.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/game/Bot$IgnoreEatable.class b/bin/de/fhtrier/gdw2/sotf/game/Bot$IgnoreEatable.class new file mode 100644 index 0000000..4d912c6 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/game/Bot$IgnoreEatable.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/game/Bot.class b/bin/de/fhtrier/gdw2/sotf/game/Bot.class new file mode 100644 index 0000000..4d15038 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/game/Bot.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/game/Candle.class b/bin/de/fhtrier/gdw2/sotf/game/Candle.class new file mode 100644 index 0000000..5d21c2c Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/game/Candle.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/game/Constants$ObjectType.class b/bin/de/fhtrier/gdw2/sotf/game/Constants$ObjectType.class new file mode 100644 index 0000000..3a965b3 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/game/Constants$ObjectType.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/game/Constants.class b/bin/de/fhtrier/gdw2/sotf/game/Constants.class new file mode 100644 index 0000000..6debd6f Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/game/Constants.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/game/Eatable.class b/bin/de/fhtrier/gdw2/sotf/game/Eatable.class new file mode 100644 index 0000000..8f68b42 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/game/Eatable.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/game/Entity.class b/bin/de/fhtrier/gdw2/sotf/game/Entity.class new file mode 100644 index 0000000..e6c7b49 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/game/Entity.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/game/EntityFactory.class b/bin/de/fhtrier/gdw2/sotf/game/EntityFactory.class new file mode 100644 index 0000000..f6385c8 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/game/EntityFactory.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/game/IdGenerator.class b/bin/de/fhtrier/gdw2/sotf/game/IdGenerator.class new file mode 100644 index 0000000..29c567f Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/game/IdGenerator.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/game/Key.class b/bin/de/fhtrier/gdw2/sotf/game/Key.class new file mode 100644 index 0000000..346c82b Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/game/Key.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/game/KeyControl.class b/bin/de/fhtrier/gdw2/sotf/game/KeyControl.class new file mode 100644 index 0000000..1e9ab3e Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/game/KeyControl.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/game/Minimap.class b/bin/de/fhtrier/gdw2/sotf/game/Minimap.class new file mode 100644 index 0000000..0245ef6 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/game/Minimap.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/game/Player.class b/bin/de/fhtrier/gdw2/sotf/game/Player.class new file mode 100644 index 0000000..41628e0 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/game/Player.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/game/PlayerState.class b/bin/de/fhtrier/gdw2/sotf/game/PlayerState.class new file mode 100644 index 0000000..facdd60 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/game/PlayerState.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/game/PlayerStatistic.class b/bin/de/fhtrier/gdw2/sotf/game/PlayerStatistic.class new file mode 100644 index 0000000..306ce75 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/game/PlayerStatistic.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/game/Powerup.class b/bin/de/fhtrier/gdw2/sotf/game/Powerup.class new file mode 100644 index 0000000..820c848 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/game/Powerup.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/game/Team.class b/bin/de/fhtrier/gdw2/sotf/game/Team.class new file mode 100644 index 0000000..3f26a83 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/game/Team.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/game/Useable.class b/bin/de/fhtrier/gdw2/sotf/game/Useable.class new file mode 100644 index 0000000..3af73f8 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/game/Useable.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/game/World$1.class b/bin/de/fhtrier/gdw2/sotf/game/World$1.class new file mode 100644 index 0000000..1748eb2 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/game/World$1.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/game/World.class b/bin/de/fhtrier/gdw2/sotf/game/World.class new file mode 100644 index 0000000..9fc0e7e Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/game/World.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/gfx/AnimationRenderEffect.class b/bin/de/fhtrier/gdw2/sotf/gfx/AnimationRenderEffect.class new file mode 100644 index 0000000..00d1461 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/gfx/AnimationRenderEffect.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/gfx/ParticleRenderEffect.class b/bin/de/fhtrier/gdw2/sotf/gfx/ParticleRenderEffect.class new file mode 100644 index 0000000..c86ec64 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/gfx/ParticleRenderEffect.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/gfx/PlayerRenderEffect.class b/bin/de/fhtrier/gdw2/sotf/gfx/PlayerRenderEffect.class new file mode 100644 index 0000000..51b8067 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/gfx/PlayerRenderEffect.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/handler/CollisionHandler$CollisionResult.class b/bin/de/fhtrier/gdw2/sotf/handler/CollisionHandler$CollisionResult.class new file mode 100644 index 0000000..6e3be3b Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/handler/CollisionHandler$CollisionResult.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/handler/CollisionHandler$CollisionType.class b/bin/de/fhtrier/gdw2/sotf/handler/CollisionHandler$CollisionType.class new file mode 100644 index 0000000..2a1adbc Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/handler/CollisionHandler$CollisionType.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/handler/CollisionHandler$State.class b/bin/de/fhtrier/gdw2/sotf/handler/CollisionHandler$State.class new file mode 100644 index 0000000..2faf6da Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/handler/CollisionHandler$State.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/handler/CollisionHandler.class b/bin/de/fhtrier/gdw2/sotf/handler/CollisionHandler.class new file mode 100644 index 0000000..f2c15b3 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/handler/CollisionHandler.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/handler/ControlHandler.class b/bin/de/fhtrier/gdw2/sotf/handler/ControlHandler.class new file mode 100644 index 0000000..00fc4c3 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/handler/ControlHandler.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/handler/TeleportTile.class b/bin/de/fhtrier/gdw2/sotf/handler/TeleportTile.class new file mode 100644 index 0000000..23bbcbe Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/handler/TeleportTile.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/handler/controllers/AnalogueController.class b/bin/de/fhtrier/gdw2/sotf/handler/controllers/AnalogueController.class new file mode 100644 index 0000000..de8ebde Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/handler/controllers/AnalogueController.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/handler/controllers/BasicController.class b/bin/de/fhtrier/gdw2/sotf/handler/controllers/BasicController.class new file mode 100644 index 0000000..6bb542d Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/handler/controllers/BasicController.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/handler/controllers/ClassicKeyboardController.class b/bin/de/fhtrier/gdw2/sotf/handler/controllers/ClassicKeyboardController.class new file mode 100644 index 0000000..c58c0c2 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/handler/controllers/ClassicKeyboardController.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/handler/controllers/ControlMethod.class b/bin/de/fhtrier/gdw2/sotf/handler/controllers/ControlMethod.class new file mode 100644 index 0000000..f594e70 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/handler/controllers/ControlMethod.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/handler/controllers/DigitalController.class b/bin/de/fhtrier/gdw2/sotf/handler/controllers/DigitalController.class new file mode 100644 index 0000000..c444d87 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/handler/controllers/DigitalController.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/handler/controllers/GamePadController.class b/bin/de/fhtrier/gdw2/sotf/handler/controllers/GamePadController.class new file mode 100644 index 0000000..a37e653 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/handler/controllers/GamePadController.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/handler/controllers/KeyboardController.class b/bin/de/fhtrier/gdw2/sotf/handler/controllers/KeyboardController.class new file mode 100644 index 0000000..a502171 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/handler/controllers/KeyboardController.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/handler/controllers/MouseController.class b/bin/de/fhtrier/gdw2/sotf/handler/controllers/MouseController.class new file mode 100644 index 0000000..0942629 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/handler/controllers/MouseController.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/handler/controllers/TurningKeyboardController.class b/bin/de/fhtrier/gdw2/sotf/handler/controllers/TurningKeyboardController.class new file mode 100644 index 0000000..b3f38cb Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/handler/controllers/TurningKeyboardController.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/hud/HUD.class b/bin/de/fhtrier/gdw2/sotf/hud/HUD.class new file mode 100644 index 0000000..ffb51c3 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/hud/HUD.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/hud/HudInventory.class b/bin/de/fhtrier/gdw2/sotf/hud/HudInventory.class new file mode 100644 index 0000000..1b7c089 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/hud/HudInventory.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/hud/HudPizza.class b/bin/de/fhtrier/gdw2/sotf/hud/HudPizza.class new file mode 100644 index 0000000..827a132 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/hud/HudPizza.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/hud/HudPowerups.class b/bin/de/fhtrier/gdw2/sotf/hud/HudPowerups.class new file mode 100644 index 0000000..0a6cb7f Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/hud/HudPowerups.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/hud/HudScore.class b/bin/de/fhtrier/gdw2/sotf/hud/HudScore.class new file mode 100644 index 0000000..69855ab Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/hud/HudScore.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/interfaces/ICandle.class b/bin/de/fhtrier/gdw2/sotf/interfaces/ICandle.class new file mode 100644 index 0000000..66d9b3f Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/interfaces/ICandle.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/interfaces/ICollision.class b/bin/de/fhtrier/gdw2/sotf/interfaces/ICollision.class new file mode 100644 index 0000000..438bd6b Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/interfaces/ICollision.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/interfaces/IEatable.class b/bin/de/fhtrier/gdw2/sotf/interfaces/IEatable.class new file mode 100644 index 0000000..d2d2850 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/interfaces/IEatable.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/interfaces/IEntity.class b/bin/de/fhtrier/gdw2/sotf/interfaces/IEntity.class new file mode 100644 index 0000000..76e89f3 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/interfaces/IEntity.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/interfaces/IPlayer.class b/bin/de/fhtrier/gdw2/sotf/interfaces/IPlayer.class new file mode 100644 index 0000000..312ecdd Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/interfaces/IPlayer.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/interfaces/IPowerup$PowerupType.class b/bin/de/fhtrier/gdw2/sotf/interfaces/IPowerup$PowerupType.class new file mode 100644 index 0000000..0332bde Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/interfaces/IPowerup$PowerupType.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/interfaces/IPowerup.class b/bin/de/fhtrier/gdw2/sotf/interfaces/IPowerup.class new file mode 100644 index 0000000..4a2aa8f Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/interfaces/IPowerup.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/interfaces/ITeam.class b/bin/de/fhtrier/gdw2/sotf/interfaces/ITeam.class new file mode 100644 index 0000000..110e08e Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/interfaces/ITeam.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/interfaces/IUseable.class b/bin/de/fhtrier/gdw2/sotf/interfaces/IUseable.class new file mode 100644 index 0000000..f6a5653 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/interfaces/IUseable.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/interfaces/IWorld.class b/bin/de/fhtrier/gdw2/sotf/interfaces/IWorld.class new file mode 100644 index 0000000..c495aa7 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/interfaces/IWorld.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/interfaces/base/IRenderable.class b/bin/de/fhtrier/gdw2/sotf/interfaces/base/IRenderable.class new file mode 100644 index 0000000..c61235f Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/interfaces/base/IRenderable.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/interfaces/base/IUpdateable.class b/bin/de/fhtrier/gdw2/sotf/interfaces/base/IUpdateable.class new file mode 100644 index 0000000..3e7de34 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/interfaces/base/IUpdateable.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/interfaces/gfx/IPlayerRenderEffect.class b/bin/de/fhtrier/gdw2/sotf/interfaces/gfx/IPlayerRenderEffect.class new file mode 100644 index 0000000..9e7168a Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/interfaces/gfx/IPlayerRenderEffect.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/interfaces/menu/IActionListener.class b/bin/de/fhtrier/gdw2/sotf/interfaces/menu/IActionListener.class new file mode 100644 index 0000000..b92c15c Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/interfaces/menu/IActionListener.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/interfaces/menu/IUpdateListener.class b/bin/de/fhtrier/gdw2/sotf/interfaces/menu/IUpdateListener.class new file mode 100644 index 0000000..30d43be Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/interfaces/menu/IUpdateListener.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/Animated.class b/bin/de/fhtrier/gdw2/sotf/menu/Animated.class new file mode 100644 index 0000000..f21c874 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/Animated.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/Button$1.class b/bin/de/fhtrier/gdw2/sotf/menu/Button$1.class new file mode 100644 index 0000000..5b58c52 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/Button$1.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/Button$2.class b/bin/de/fhtrier/gdw2/sotf/menu/Button$2.class new file mode 100644 index 0000000..8cb77fb Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/Button$2.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/Button$State.class b/bin/de/fhtrier/gdw2/sotf/menu/Button$State.class new file mode 100644 index 0000000..34b49d5 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/Button$State.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/Button.class b/bin/de/fhtrier/gdw2/sotf/menu/Button.class new file mode 100644 index 0000000..1df62db Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/Button.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/CreditsAnimation$Style.class b/bin/de/fhtrier/gdw2/sotf/menu/CreditsAnimation$Style.class new file mode 100644 index 0000000..000d942 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/CreditsAnimation$Style.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/CreditsAnimation.class b/bin/de/fhtrier/gdw2/sotf/menu/CreditsAnimation.class new file mode 100644 index 0000000..49b0fc9 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/CreditsAnimation.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/Label.class b/bin/de/fhtrier/gdw2/sotf/menu/Label.class new file mode 100644 index 0000000..d6b7b13 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/Label.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/MenuManager$Type.class b/bin/de/fhtrier/gdw2/sotf/menu/MenuManager$Type.class new file mode 100644 index 0000000..2e07798 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/MenuManager$Type.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/MenuManager.class b/bin/de/fhtrier/gdw2/sotf/menu/MenuManager.class new file mode 100644 index 0000000..590de2f Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/MenuManager.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/MenuPage.class b/bin/de/fhtrier/gdw2/sotf/menu/MenuPage.class new file mode 100644 index 0000000..3561c00 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/MenuPage.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/MenuPageAction.class b/bin/de/fhtrier/gdw2/sotf/menu/MenuPageAction.class new file mode 100644 index 0000000..06fc0e0 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/MenuPageAction.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/PathMover$Destination.class b/bin/de/fhtrier/gdw2/sotf/menu/PathMover$Destination.class new file mode 100644 index 0000000..7dda77b Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/PathMover$Destination.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/PathMover.class b/bin/de/fhtrier/gdw2/sotf/menu/PathMover.class new file mode 100644 index 0000000..84d93a1 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/PathMover.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/Slider.class b/bin/de/fhtrier/gdw2/sotf/menu/Slider.class new file mode 100644 index 0000000..fd784a6 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/Slider.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/TextField.class b/bin/de/fhtrier/gdw2/sotf/menu/TextField.class new file mode 100644 index 0000000..f216841 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/TextField.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/ToggleButton.class b/bin/de/fhtrier/gdw2/sotf/menu/ToggleButton.class new file mode 100644 index 0000000..60112dd Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/ToggleButton.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/Widget$Align.class b/bin/de/fhtrier/gdw2/sotf/menu/Widget$Align.class new file mode 100644 index 0000000..6118e03 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/Widget$Align.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/Widget.class b/bin/de/fhtrier/gdw2/sotf/menu/Widget.class new file mode 100644 index 0000000..afa6344 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/Widget.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/credits/Credits$Animations$Animation.class b/bin/de/fhtrier/gdw2/sotf/menu/credits/Credits$Animations$Animation.class new file mode 100644 index 0000000..c7adf45 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/credits/Credits$Animations$Animation.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/credits/Credits$Animations.class b/bin/de/fhtrier/gdw2/sotf/menu/credits/Credits$Animations.class new file mode 100644 index 0000000..06da6a7 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/credits/Credits$Animations.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/credits/Credits$Paths$Path$Destination.class b/bin/de/fhtrier/gdw2/sotf/menu/credits/Credits$Paths$Path$Destination.class new file mode 100644 index 0000000..54d681b Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/credits/Credits$Paths$Path$Destination.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/credits/Credits$Paths$Path.class b/bin/de/fhtrier/gdw2/sotf/menu/credits/Credits$Paths$Path.class new file mode 100644 index 0000000..890d9cb Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/credits/Credits$Paths$Path.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/credits/Credits$Paths.class b/bin/de/fhtrier/gdw2/sotf/menu/credits/Credits$Paths.class new file mode 100644 index 0000000..2eedcf4 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/credits/Credits$Paths.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/credits/Credits$Styles$Style.class b/bin/de/fhtrier/gdw2/sotf/menu/credits/Credits$Styles$Style.class new file mode 100644 index 0000000..96482d9 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/credits/Credits$Styles$Style.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/credits/Credits$Styles.class b/bin/de/fhtrier/gdw2/sotf/menu/credits/Credits$Styles.class new file mode 100644 index 0000000..91856fd Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/credits/Credits$Styles.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/credits/Credits$Texts$Text.class b/bin/de/fhtrier/gdw2/sotf/menu/credits/Credits$Texts$Text.class new file mode 100644 index 0000000..0124466 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/credits/Credits$Texts$Text.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/credits/Credits$Texts.class b/bin/de/fhtrier/gdw2/sotf/menu/credits/Credits$Texts.class new file mode 100644 index 0000000..5c80772 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/credits/Credits$Texts.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/credits/Credits.class b/bin/de/fhtrier/gdw2/sotf/menu/credits/Credits.class new file mode 100644 index 0000000..2ec0af6 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/credits/Credits.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/credits/ObjectFactory.class b/bin/de/fhtrier/gdw2/sotf/menu/credits/ObjectFactory.class new file mode 100644 index 0000000..656f553 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/credits/ObjectFactory.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageControls$1.class b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageControls$1.class new file mode 100644 index 0000000..3e8aa8d Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageControls$1.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageControls$2.class b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageControls$2.class new file mode 100644 index 0000000..21321bb Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageControls$2.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageControls$3.class b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageControls$3.class new file mode 100644 index 0000000..8a84c74 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageControls$3.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageControls$4.class b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageControls$4.class new file mode 100644 index 0000000..1535d00 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageControls$4.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageControls$5.class b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageControls$5.class new file mode 100644 index 0000000..e64d351 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageControls$5.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageControls$KeyListener.class b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageControls$KeyListener.class new file mode 100644 index 0000000..2340d16 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageControls$KeyListener.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageControls.class b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageControls.class new file mode 100644 index 0000000..df0d7d4 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageControls.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageCreateServer.class b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageCreateServer.class new file mode 100644 index 0000000..53fd0ad Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageCreateServer.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageCredits$1.class b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageCredits$1.class new file mode 100644 index 0000000..8156939 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageCredits$1.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageCredits.class b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageCredits.class new file mode 100644 index 0000000..17683fd Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageCredits.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageHelp$1.class b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageHelp$1.class new file mode 100644 index 0000000..2c73e15 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageHelp$1.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageHelp.class b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageHelp.class new file mode 100644 index 0000000..24b8071 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageHelp.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageHelpImageScroller$1.class b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageHelpImageScroller$1.class new file mode 100644 index 0000000..e4b1e1b Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageHelpImageScroller$1.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageHelpImageScroller$2.class b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageHelpImageScroller$2.class new file mode 100644 index 0000000..aa8a40a Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageHelpImageScroller$2.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageHelpImageScroller.class b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageHelpImageScroller.class new file mode 100644 index 0000000..ba24f4c Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageHelpImageScroller.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageJoin.class b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageJoin.class new file mode 100644 index 0000000..4d5d9a5 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageJoin.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageLobby$1.class b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageLobby$1.class new file mode 100644 index 0000000..ab9de30 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageLobby$1.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageLobby.class b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageLobby.class new file mode 100644 index 0000000..1dbd4d1 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageLobby.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageOptions$1.class b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageOptions$1.class new file mode 100644 index 0000000..096b91d Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageOptions$1.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageOptions$2.class b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageOptions$2.class new file mode 100644 index 0000000..904a877 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageOptions$2.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageOptions$3.class b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageOptions$3.class new file mode 100644 index 0000000..839ede5 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageOptions$3.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageOptions$4.class b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageOptions$4.class new file mode 100644 index 0000000..ba7815d Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageOptions$4.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageOptions.class b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageOptions.class new file mode 100644 index 0000000..2f0a63a Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageOptions.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageRoot$1.class b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageRoot$1.class new file mode 100644 index 0000000..2897f88 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageRoot$1.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageRoot$2.class b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageRoot$2.class new file mode 100644 index 0000000..30c4269 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageRoot$2.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageRoot$3.class b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageRoot$3.class new file mode 100644 index 0000000..fefdb57 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageRoot$3.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageRoot.class b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageRoot.class new file mode 100644 index 0000000..f408811 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageRoot.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageStatistic.class b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageStatistic.class new file mode 100644 index 0000000..09a4901 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/pages/MenuPageStatistic.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/pages/abstrct/MenuPageConnection$1.class b/bin/de/fhtrier/gdw2/sotf/menu/pages/abstrct/MenuPageConnection$1.class new file mode 100644 index 0000000..9f9d3bc Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/pages/abstrct/MenuPageConnection$1.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/pages/abstrct/MenuPageConnection$2.class b/bin/de/fhtrier/gdw2/sotf/menu/pages/abstrct/MenuPageConnection$2.class new file mode 100644 index 0000000..1dab0d2 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/pages/abstrct/MenuPageConnection$2.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/menu/pages/abstrct/MenuPageConnection.class b/bin/de/fhtrier/gdw2/sotf/menu/pages/abstrct/MenuPageConnection.class new file mode 100644 index 0000000..34b33e2 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/menu/pages/abstrct/MenuPageConnection.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/network/NetGame.class b/bin/de/fhtrier/gdw2/sotf/network/NetGame.class new file mode 100644 index 0000000..21eb7f9 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/network/NetGame.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/network/NetGameClient.class b/bin/de/fhtrier/gdw2/sotf/network/NetGameClient.class new file mode 100644 index 0000000..7550e55 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/network/NetGameClient.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/network/NetGameServer.class b/bin/de/fhtrier/gdw2/sotf/network/NetGameServer.class new file mode 100644 index 0000000..b0f0683 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/network/NetGameServer.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/network/datagrams/ConnectDatagram.class b/bin/de/fhtrier/gdw2/sotf/network/datagrams/ConnectDatagram.class new file mode 100644 index 0000000..50ecbb6 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/network/datagrams/ConnectDatagram.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/network/datagrams/CreateEntityDatagram.class b/bin/de/fhtrier/gdw2/sotf/network/datagrams/CreateEntityDatagram.class new file mode 100644 index 0000000..8fae520 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/network/datagrams/CreateEntityDatagram.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/network/datagrams/DatagramFactory.class b/bin/de/fhtrier/gdw2/sotf/network/datagrams/DatagramFactory.class new file mode 100644 index 0000000..d1d08c8 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/network/datagrams/DatagramFactory.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/network/datagrams/DatagramType.class b/bin/de/fhtrier/gdw2/sotf/network/datagrams/DatagramType.class new file mode 100644 index 0000000..daa9834 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/network/datagrams/DatagramType.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/network/datagrams/PlayerControlDatagram.class b/bin/de/fhtrier/gdw2/sotf/network/datagrams/PlayerControlDatagram.class new file mode 100644 index 0000000..21fc664 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/network/datagrams/PlayerControlDatagram.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/network/datagrams/PlayerStateDatagram.class b/bin/de/fhtrier/gdw2/sotf/network/datagrams/PlayerStateDatagram.class new file mode 100644 index 0000000..012c5ce Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/network/datagrams/PlayerStateDatagram.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/network/datagrams/TeleportDatagram.class b/bin/de/fhtrier/gdw2/sotf/network/datagrams/TeleportDatagram.class new file mode 100644 index 0000000..bdb3be4 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/network/datagrams/TeleportDatagram.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/network/datagrams/WorldSetupDatagram.class b/bin/de/fhtrier/gdw2/sotf/network/datagrams/WorldSetupDatagram.class new file mode 100644 index 0000000..4926fea Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/network/datagrams/WorldSetupDatagram.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/network/datagrams/WorldSoundDatagram.class b/bin/de/fhtrier/gdw2/sotf/network/datagrams/WorldSoundDatagram.class new file mode 100644 index 0000000..cecf88c Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/network/datagrams/WorldSoundDatagram.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/network/datagrams/WorldStateDatagram.class b/bin/de/fhtrier/gdw2/sotf/network/datagrams/WorldStateDatagram.class new file mode 100644 index 0000000..1982507 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/network/datagrams/WorldStateDatagram.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/settings/GlobalSettings.class b/bin/de/fhtrier/gdw2/sotf/settings/GlobalSettings.class new file mode 100644 index 0000000..71bcb0c Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/settings/GlobalSettings.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/settings/ObjectFactory.class b/bin/de/fhtrier/gdw2/sotf/settings/ObjectFactory.class new file mode 100644 index 0000000..bee9cd7 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/settings/ObjectFactory.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/settings/Settings$ControlKeys$Key.class b/bin/de/fhtrier/gdw2/sotf/settings/Settings$ControlKeys$Key.class new file mode 100644 index 0000000..69c94c2 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/settings/Settings$ControlKeys$Key.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/settings/Settings$ControlKeys.class b/bin/de/fhtrier/gdw2/sotf/settings/Settings$ControlKeys.class new file mode 100644 index 0000000..f90cabf Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/settings/Settings$ControlKeys.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/settings/Settings$FloatValues$Value.class b/bin/de/fhtrier/gdw2/sotf/settings/Settings$FloatValues$Value.class new file mode 100644 index 0000000..14891f6 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/settings/Settings$FloatValues$Value.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/settings/Settings$FloatValues.class b/bin/de/fhtrier/gdw2/sotf/settings/Settings$FloatValues.class new file mode 100644 index 0000000..1b1173e Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/settings/Settings$FloatValues.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/settings/Settings$StringValues$Value.class b/bin/de/fhtrier/gdw2/sotf/settings/Settings$StringValues$Value.class new file mode 100644 index 0000000..a081962 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/settings/Settings$StringValues$Value.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/settings/Settings$StringValues.class b/bin/de/fhtrier/gdw2/sotf/settings/Settings$StringValues.class new file mode 100644 index 0000000..a1c6000 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/settings/Settings$StringValues.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/settings/Settings.class b/bin/de/fhtrier/gdw2/sotf/settings/Settings.class new file mode 100644 index 0000000..986c4ea Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/settings/Settings.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/settings/SettingsLoader.class b/bin/de/fhtrier/gdw2/sotf/settings/SettingsLoader.class new file mode 100644 index 0000000..145e1ca Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/settings/SettingsLoader.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/states/ClanArena.class b/bin/de/fhtrier/gdw2/sotf/states/ClanArena.class new file mode 100644 index 0000000..8fc23a8 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/states/ClanArena.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/states/GameplayState.class b/bin/de/fhtrier/gdw2/sotf/states/GameplayState.class new file mode 100644 index 0000000..58c0202 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/states/GameplayState.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/states/LoadGameState.class b/bin/de/fhtrier/gdw2/sotf/states/LoadGameState.class new file mode 100644 index 0000000..9934dee Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/states/LoadGameState.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/states/MainMenuState.class b/bin/de/fhtrier/gdw2/sotf/states/MainMenuState.class new file mode 100644 index 0000000..f05ac36 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/states/MainMenuState.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/states/TeamDeathmatch.class b/bin/de/fhtrier/gdw2/sotf/states/TeamDeathmatch.class new file mode 100644 index 0000000..bbf4509 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/states/TeamDeathmatch.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/utils/AssetLoader.class b/bin/de/fhtrier/gdw2/sotf/utils/AssetLoader.class new file mode 100644 index 0000000..5ab67ee Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/utils/AssetLoader.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/utils/MapCalculator$Tile.class b/bin/de/fhtrier/gdw2/sotf/utils/MapCalculator$Tile.class new file mode 100644 index 0000000..ff39629 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/utils/MapCalculator$Tile.class differ diff --git a/bin/de/fhtrier/gdw2/sotf/utils/MapCalculator.class b/bin/de/fhtrier/gdw2/sotf/utils/MapCalculator.class new file mode 100644 index 0000000..58dd238 Binary files /dev/null and b/bin/de/fhtrier/gdw2/sotf/utils/MapCalculator.class differ diff --git a/bin/first/Ball.class b/bin/first/Ball.class new file mode 100644 index 0000000..4276fdb Binary files /dev/null and b/bin/first/Ball.class differ diff --git a/bin/first/Enemy.class b/bin/first/Enemy.class new file mode 100644 index 0000000..2138b05 Binary files /dev/null and b/bin/first/Enemy.class differ diff --git a/bin/first/Player.class b/bin/first/Player.class new file mode 100644 index 0000000..95351aa Binary files /dev/null and b/bin/first/Player.class differ diff --git a/bin/first/Pong.class b/bin/first/Pong.class new file mode 100644 index 0000000..7a8d8d1 Binary files /dev/null and b/bin/first/Pong.class differ diff --git a/bin/loa/LoA.class b/bin/loa/LoA.class new file mode 100644 index 0000000..0078899 Binary files /dev/null and b/bin/loa/LoA.class differ diff --git a/bin/loa/Map.class b/bin/loa/Map.class new file mode 100644 index 0000000..1a8f71a Binary files /dev/null and b/bin/loa/Map.class differ diff --git a/bin/loa/Overworld_tiles.png b/bin/loa/Overworld_tiles.png new file mode 100644 index 0000000..0f37970 Binary files /dev/null and b/bin/loa/Overworld_tiles.png differ diff --git a/bin/loa/Player.class b/bin/loa/Player.class new file mode 100644 index 0000000..9f6238d Binary files /dev/null and b/bin/loa/Player.class differ diff --git a/bin/loa/testmap.tmx b/bin/loa/testmap.tmx new file mode 100644 index 0000000..91ccbb6 --- /dev/null +++ b/bin/loa/testmap.tmx @@ -0,0 +1,21 @@ + + + + + + + + + + + + + H4sIAAAAAAAAC+3DAQkAAAwEoesf4dOux1Bw1VRVVfX5A9KNrUgADwAA + + + + + H4sIAAAAAAAAC+3NsQkAAAzDsPwP/blXBDJI4NkJa66Uv7+/v7//8h8A2h6myYf9AA8AAA== + + + diff --git a/bin/spiel/Boss.class b/bin/spiel/Boss.class new file mode 100644 index 0000000..9f7e098 Binary files /dev/null and b/bin/spiel/Boss.class differ diff --git a/bin/spiel/NinjaFight.class b/bin/spiel/NinjaFight.class new file mode 100644 index 0000000..bdda22c Binary files /dev/null and b/bin/spiel/NinjaFight.class differ diff --git a/bin/spiel/TestSpiel.class b/bin/spiel/TestSpiel.class new file mode 100644 index 0000000..748ae01 Binary files /dev/null and b/bin/spiel/TestSpiel.class differ diff --git a/bin/src/spiel/Auto.class b/bin/src/spiel/Auto.class new file mode 100644 index 0000000..7d2e40f Binary files /dev/null and b/bin/src/spiel/Auto.class differ diff --git a/bin/src/spiel/Boss.class b/bin/src/spiel/Boss.class new file mode 100644 index 0000000..4105fca Binary files /dev/null and b/bin/src/spiel/Boss.class differ diff --git a/bin/src/spiel/Frosch.class b/bin/src/spiel/Frosch.class new file mode 100644 index 0000000..2fa436d Binary files /dev/null and b/bin/src/spiel/Frosch.class differ diff --git a/bin/src/spiel/NinjaFight.class b/bin/src/spiel/NinjaFight.class new file mode 100644 index 0000000..ffb800c Binary files /dev/null and b/bin/src/spiel/NinjaFight.class differ diff --git a/bin/src/spiel/TestSpiel.class b/bin/src/spiel/TestSpiel.class new file mode 100644 index 0000000..77484dd Binary files /dev/null and b/bin/src/spiel/TestSpiel.class differ diff --git a/desktop.ini b/desktop.ini new file mode 100644 index 0000000..2421d1f --- /dev/null +++ b/desktop.ini @@ -0,0 +1,6 @@ +[.ShellClassInfo] +IconResource=C:\Users\AlexD\Downloads\themes\Neon_Green_Lamellen_ICONS\Icons\ACDSee_ .ico,0 +[ViewState] +Mode= +Vid= +FolderType=Generic diff --git a/ext/jogg.jar b/ext/jogg.jar new file mode 100644 index 0000000..93b84db Binary files /dev/null and b/ext/jogg.jar differ diff --git a/ext/jorbis.jar b/ext/jorbis.jar new file mode 100644 index 0000000..cb4c377 Binary files /dev/null and b/ext/jorbis.jar differ diff --git a/ext/lwjgl/doc/3rdparty/jinput_license.txt b/ext/lwjgl/doc/3rdparty/jinput_license.txt new file mode 100644 index 0000000..cee4669 --- /dev/null +++ b/ext/lwjgl/doc/3rdparty/jinput_license.txt @@ -0,0 +1,32 @@ +/***************************************************************************** + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistribution of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materails provided with the distribution. + * + * Neither the name Sun Microsystems, Inc. or the names of the contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING + * ANY IMPLIED WARRANT OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR + * NON-INFRINGEMEN, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND + * ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS + * A RESULT OF USING, MODIFYING OR DESTRIBUTING THIS SOFTWARE OR ITS + * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST + * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, + * INCIDENTAL OR PUNITIVE DAMAGES. HOWEVER CAUSED AND REGARDLESS OF THE THEORY + * OF LIABILITY, ARISING OUT OF THE USE OF OUR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for us in + * the design, construction, operation or maintenance of any nuclear facility + * + *****************************************************************************/ \ No newline at end of file diff --git a/ext/lwjgl/doc/3rdparty/jogl_license.txt b/ext/lwjgl/doc/3rdparty/jogl_license.txt new file mode 100644 index 0000000..db9b933 --- /dev/null +++ b/ext/lwjgl/doc/3rdparty/jogl_license.txt @@ -0,0 +1,152 @@ +JOGL is released under the BSD license. The full license terms follow: + + Copyright (c) 2003-2009 Sun Microsystems, Inc. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + - Redistribution of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistribution in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of Sun Microsystems, Inc. or the names of + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + This software is provided "AS IS," without a warranty of any kind. ALL + EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + + You acknowledge that this software is not designed or intended for use + in the design, construction, operation or maintenance of any nuclear + facility. + +The JOGL source tree contains code ported from the OpenGL sample +implementation by Silicon Graphics, Inc. This code is licensed under +the SGI Free Software License B (Sun is redistributing the modified code +under a slightly modified, alternative license, which is described two +paragraphs below after "NOTE:"): + + License Applicability. Except to the extent portions of this file are + made subject to an alternative license as permitted in the SGI Free + Software License B, Version 1.1 (the "License"), the contents of this + file are subject only to the provisions of the License. You may not use + this file except in compliance with the License. You may obtain a copy + of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 + Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: + + http://oss.sgi.com/projects/FreeB + + Note that, as provided in the License, the Software is distributed on an + "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS + DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND + CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A + PARTICULAR PURPOSE, AND NON-INFRINGEMENT. + + NOTE: The Original Code (as defined below) has been licensed to Sun + Microsystems, Inc. ("Sun") under the SGI Free Software License B + (Version 1.1), shown above ("SGI License"). Pursuant to Section + 3.2(3) of the SGI License, Sun is distributing the Covered Code to + you under an alternative license ("Alternative License"). This + Alternative License includes all of the provisions of the SGI License + except that Section 2.2 and 11 are omitted. Any differences between + the Alternative License and the SGI License are offered solely by Sun + and not by SGI. + + Original Code. The Original Code is: OpenGL Sample Implementation, + Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, + Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. + Copyright in any portions created by third parties is as indicated + elsewhere herein. All Rights Reserved. + + Additional Notice Provisions: The application programming interfaces + established by SGI in conjunction with the Original Code are The + OpenGL(R) Graphics System: A Specification (Version 1.2.1), released + April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version + 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X + Window System(R) (Version 1.3), released October 19, 1998. This software + was created using the OpenGL(R) version 1.2.1 Sample Implementation + published by SGI, but has not been independently verified as being + compliant with the OpenGL(R) version 1.2.1 Specification. + + +The JOGL source tree contains code from the LWJGL project which is +similarly covered by the BSD license: + + Copyright (c) 2002-2004 LWJGL Project + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of 'LWJGL' nor the names of + its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The JOGL source tree also contains a Java port of Brian Paul's Tile +Rendering library, used with permission of the author under the BSD +license instead of the original LGPL: + + Copyright (c) 1997-2005 Brian Paul. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + - Redistribution of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistribution in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of Brian Paul or the names of contributors may be + used to endorse or promote products derived from this software + without specific prior written permission. + + This software is provided "AS IS," without a warranty of any + kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND + WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY + EXCLUDED. THE COPYRIGHT HOLDERS AND CONTRIBUTORS SHALL NOT BE + LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, + MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO + EVENT WILL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY + LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, + CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND + REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR + INABILITY TO USE THIS SOFTWARE, EVEN IF THE COPYRIGHT HOLDERS OR + CONTRIBUTORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. diff --git a/ext/lwjgl/doc/3rdparty/lzma_license.txt b/ext/lwjgl/doc/3rdparty/lzma_license.txt new file mode 100644 index 0000000..d825219 --- /dev/null +++ b/ext/lwjgl/doc/3rdparty/lzma_license.txt @@ -0,0 +1,15 @@ +LZMA# SDK is licensed under two licenses: + +1) GNU Lesser General Public License (GNU LGPL) +2) Common Public License (CPL) + +It means that you can select one of these two licenses and +follow rules of that license. + +SPECIAL EXCEPTION +Igor Pavlov, as the author of this code, expressly permits you +to statically or dynamically link your code (or bind by name) +to the files from LZMA# SDK without subjecting your linked +code to the terms of the CPL or GNU LGPL. +Any modifications or additions to files from LZMA# SDK, however, +are subject to the GNU LGPL or CPL terms. \ No newline at end of file diff --git a/ext/lwjgl/doc/3rdparty/openal_license.txt b/ext/lwjgl/doc/3rdparty/openal_license.txt new file mode 100644 index 0000000..339560d --- /dev/null +++ b/ext/lwjgl/doc/3rdparty/openal_license.txt @@ -0,0 +1,437 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS diff --git a/ext/lwjgl/doc/CREDITS b/ext/lwjgl/doc/CREDITS new file mode 100644 index 0000000..fa24eb0 --- /dev/null +++ b/ext/lwjgl/doc/CREDITS @@ -0,0 +1,38 @@ +The following people have helped to make this project what it is today: + - Caspian Rychlik-Prince + - Brian Matzon + - Elias Naur + - Ioannis Tsakpinis + - Niels Jürgensen + - Tristan Campbell + - Gregory Pierce + - Luke Holden + - Mark Bernard + - Erik Duijs + - Jos Hirth + - Kevin Glass + - Atsuya Takagi + - kappaOne + - Simon Felix + - Ryan McNally + - Ciardhubh + - Jens von Pilgrim + - Ruben Garat + - Pelle Johnsen + - Jae Kwon + +additional credits goes to: + - Joseph I. Valenzuela [OpenAL stuff] + - Lev Povalahev [OpenGL Extensions] + - Endolf [Nightly builds and JInput] + +The LWJGL project includes files from or depends on the following projects: + - OpenGL, SGI - http://opengl.org/ + - OpenAL, Creative Labs - http://openal.org/ + - jinput, Sun - https://jinput.dev.java.net/ + - lzma, p7zip - http://p7zip.sourceforge.net/ + - JOGL, Sun - http://kenai.com/projects/jogl/pages/Home + +Please see the /doc/3rdparty/ directory for licenses. + +All trademarks and registered trademarks are the property of their respective owners. diff --git a/ext/lwjgl/doc/LICENSE b/ext/lwjgl/doc/LICENSE new file mode 100644 index 0000000..d277220 --- /dev/null +++ b/ext/lwjgl/doc/LICENSE @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2002-2008 Lightweight Java Game Library Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'Light Weight Java Game Library' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ \ No newline at end of file diff --git a/ext/lwjgl/doc/README b/ext/lwjgl/doc/README new file mode 100644 index 0000000..977ae02 --- /dev/null +++ b/ext/lwjgl/doc/README @@ -0,0 +1,50 @@ +This is the official readme file for lwjgl. + +Unless otherwise stated, all files distributed or in SVN are covered by +the license as stated in the LICENSE file. If you have not received this +file, please download it from the cvs server. + +To run some of the included tests: + Extract the archive, and cd into directory + (please substitute ; and \ according to platform) + + java -cp .;res;jar\lwjgl.jar;jar\lwjgl_test.jar;jar\lwjgl_util.jar;jar\jinput.jar; -Djava.library.path=native\ TEST + (this specifies that the jvm should locate the lwjgl native libs in 'native' directory) + + where TEST is some of the following: + + org.lwjgl.test.WindowCreationTest + org.lwjgl.test.SysTest + org.lwjgl.test.DisplayTest + + org.lwjgl.test.input.MouseCreationTest + org.lwjgl.test.input.MouseTest + org.lwjgl.test.input.HWCursorTest + org.lwjgl.test.input.KeyboardTest + org.lwjgl.test.input.TestControllers + + org.lwjgl.test.openal.ALCTest + org.lwjgl.test.openal.OpenALCreationTest + org.lwjgl.test.openal.MovingSoundTest + org.lwjgl.test.openal.PlayTest + org.lwjgl.test.openal.PlayTestMemory + org.lwjgl.test.openal.SourceLimitTest + org.lwjgl.test.openal.PositionTest + org.lwjgl.test.openal.StressTest + org.lwjgl.test.openal.SourceLimitTest + + org.lwjgl.test.opengl.FullScreenWindowedTest + org.lwjgl.test.opengl.PbufferTest + org.lwjgl.test.opengl.VBOIndexTest + org.lwjgl.test.opengl.VBOTest + + org.lwjgl.test.opengl.pbuffers.PbufferTest + + org.lwjgl.test.opengl.shaders.ShadersTest + +You may also run the Space invaders demo by executing: + java -cp .;res;jar\lwjgl.jar;jar\lwjgl_test.jar;jar\lwjgl_util.jar; -Djava.library.path=native\ org.lwjgl.examples.spaceinvaders.Game + +Project Webpage: www.lwjgl.org +Project Forum: forum.lwjgl.org +Project SVN: https://java-game-lib.svn.sourceforge.net/svnroot/java-game-lib diff --git a/ext/lwjgl/doc/lwjgl_hidden_switches.text b/ext/lwjgl/doc/lwjgl_hidden_switches.text new file mode 100644 index 0000000..6efed5e --- /dev/null +++ b/ext/lwjgl/doc/lwjgl_hidden_switches.text @@ -0,0 +1,25 @@ +LWJGL "Hidden" switches: + +org.lwjgl.opengl.Display.noinput +Do not initialize any controls when creating the display + +org.lwjgl.opengl.Display.nomouse +Do not create the mouse when creating the display + +org.lwjgl.opengl.Display.nokeyboard +Do not create the keyboard when creating the display + +org.lwjgl.util.Debug +Whether to output debug info + +org.lwjgl.util.NoChecks +Whether to disable runtime function/buffer checks and state tracking. + +org.lwjgl.opengl.Display.allowSoftwareOpenGL +Whether to allow creation of a software only opengl context + +org.lwjgl.opengl.Window.undecorated +Whether to create an undecorated window (no title bar) + +org.lwjgl.input.Mouse.allowNegativeMouseCoords +Usually mouse is clamped to 0,0 - setting this to true will cause you to get negative values if dragging outside and below or left of window \ No newline at end of file diff --git a/ext/lwjgl/jar/AppleJavaExtensions.jar b/ext/lwjgl/jar/AppleJavaExtensions.jar new file mode 100644 index 0000000..160d62b Binary files /dev/null and b/ext/lwjgl/jar/AppleJavaExtensions.jar differ diff --git a/ext/lwjgl/jar/asm-debug-all.jar b/ext/lwjgl/jar/asm-debug-all.jar new file mode 100644 index 0000000..d5aa15e Binary files /dev/null and b/ext/lwjgl/jar/asm-debug-all.jar differ diff --git a/ext/lwjgl/jar/jinput.jar b/ext/lwjgl/jar/jinput.jar new file mode 100644 index 0000000..7c2b6b0 Binary files /dev/null and b/ext/lwjgl/jar/jinput.jar differ diff --git a/ext/lwjgl/jar/lwjgl-debug.jar b/ext/lwjgl/jar/lwjgl-debug.jar new file mode 100644 index 0000000..8ea8c64 Binary files /dev/null and b/ext/lwjgl/jar/lwjgl-debug.jar differ diff --git a/ext/lwjgl/jar/lwjgl.jar b/ext/lwjgl/jar/lwjgl.jar new file mode 100644 index 0000000..a0fb56d Binary files /dev/null and b/ext/lwjgl/jar/lwjgl.jar differ diff --git a/ext/lwjgl/jar/lwjgl_test.jar b/ext/lwjgl/jar/lwjgl_test.jar new file mode 100644 index 0000000..d6b0f09 Binary files /dev/null and b/ext/lwjgl/jar/lwjgl_test.jar differ diff --git a/ext/lwjgl/jar/lwjgl_util.jar b/ext/lwjgl/jar/lwjgl_util.jar new file mode 100644 index 0000000..9973b24 Binary files /dev/null and b/ext/lwjgl/jar/lwjgl_util.jar differ diff --git a/ext/lwjgl/jar/lwjgl_util_applet.jar b/ext/lwjgl/jar/lwjgl_util_applet.jar new file mode 100644 index 0000000..a8872e2 Binary files /dev/null and b/ext/lwjgl/jar/lwjgl_util_applet.jar differ diff --git a/ext/lwjgl/jar/lzma.jar b/ext/lwjgl/jar/lzma.jar new file mode 100644 index 0000000..a2572d3 Binary files /dev/null and b/ext/lwjgl/jar/lzma.jar differ diff --git a/ext/lwjgl/native/linux/libjinput-linux.so b/ext/lwjgl/native/linux/libjinput-linux.so new file mode 100644 index 0000000..3cdc439 Binary files /dev/null and b/ext/lwjgl/native/linux/libjinput-linux.so differ diff --git a/ext/lwjgl/native/linux/libjinput-linux64.so b/ext/lwjgl/native/linux/libjinput-linux64.so new file mode 100644 index 0000000..de1ee5f Binary files /dev/null and b/ext/lwjgl/native/linux/libjinput-linux64.so differ diff --git a/ext/lwjgl/native/linux/liblwjgl.so b/ext/lwjgl/native/linux/liblwjgl.so new file mode 100644 index 0000000..5a02874 Binary files /dev/null and b/ext/lwjgl/native/linux/liblwjgl.so differ diff --git a/ext/lwjgl/native/linux/liblwjgl64.so b/ext/lwjgl/native/linux/liblwjgl64.so new file mode 100644 index 0000000..4572589 Binary files /dev/null and b/ext/lwjgl/native/linux/liblwjgl64.so differ diff --git a/ext/lwjgl/native/linux/libopenal.so b/ext/lwjgl/native/linux/libopenal.so new file mode 100644 index 0000000..7742faf Binary files /dev/null and b/ext/lwjgl/native/linux/libopenal.so differ diff --git a/ext/lwjgl/native/linux/libopenal64.so b/ext/lwjgl/native/linux/libopenal64.so new file mode 100644 index 0000000..d1e45e5 Binary files /dev/null and b/ext/lwjgl/native/linux/libopenal64.so differ diff --git a/ext/lwjgl/native/macosx/libjinput-osx.jnilib b/ext/lwjgl/native/macosx/libjinput-osx.jnilib new file mode 100644 index 0000000..59a3eab Binary files /dev/null and b/ext/lwjgl/native/macosx/libjinput-osx.jnilib differ diff --git a/ext/lwjgl/native/macosx/liblwjgl.jnilib b/ext/lwjgl/native/macosx/liblwjgl.jnilib new file mode 100644 index 0000000..cbb5b4c Binary files /dev/null and b/ext/lwjgl/native/macosx/liblwjgl.jnilib differ diff --git a/ext/lwjgl/native/macosx/openal.dylib b/ext/lwjgl/native/macosx/openal.dylib new file mode 100644 index 0000000..c9ca66e Binary files /dev/null and b/ext/lwjgl/native/macosx/openal.dylib differ diff --git a/ext/lwjgl/native/solaris/liblwjgl.so b/ext/lwjgl/native/solaris/liblwjgl.so new file mode 100644 index 0000000..57568ab Binary files /dev/null and b/ext/lwjgl/native/solaris/liblwjgl.so differ diff --git a/ext/lwjgl/native/solaris/liblwjgl64.so b/ext/lwjgl/native/solaris/liblwjgl64.so new file mode 100644 index 0000000..96bc8a2 Binary files /dev/null and b/ext/lwjgl/native/solaris/liblwjgl64.so differ diff --git a/ext/lwjgl/native/solaris/libopenal.so b/ext/lwjgl/native/solaris/libopenal.so new file mode 100644 index 0000000..17369bd Binary files /dev/null and b/ext/lwjgl/native/solaris/libopenal.so differ diff --git a/ext/lwjgl/native/solaris/libopenal64.so b/ext/lwjgl/native/solaris/libopenal64.so new file mode 100644 index 0000000..1a51964 Binary files /dev/null and b/ext/lwjgl/native/solaris/libopenal64.so differ diff --git a/ext/lwjgl/native/windows/OpenAL32.dll b/ext/lwjgl/native/windows/OpenAL32.dll new file mode 100644 index 0000000..6dd2600 Binary files /dev/null and b/ext/lwjgl/native/windows/OpenAL32.dll differ diff --git a/ext/lwjgl/native/windows/OpenAL64.dll b/ext/lwjgl/native/windows/OpenAL64.dll new file mode 100644 index 0000000..00c98c0 Binary files /dev/null and b/ext/lwjgl/native/windows/OpenAL64.dll differ diff --git a/ext/lwjgl/native/windows/jinput-dx8.dll b/ext/lwjgl/native/windows/jinput-dx8.dll new file mode 100644 index 0000000..6d27ad5 Binary files /dev/null and b/ext/lwjgl/native/windows/jinput-dx8.dll differ diff --git a/ext/lwjgl/native/windows/jinput-dx8_64.dll b/ext/lwjgl/native/windows/jinput-dx8_64.dll new file mode 100644 index 0000000..6730589 Binary files /dev/null and b/ext/lwjgl/native/windows/jinput-dx8_64.dll differ diff --git a/ext/lwjgl/native/windows/jinput-raw.dll b/ext/lwjgl/native/windows/jinput-raw.dll new file mode 100644 index 0000000..ce1d162 Binary files /dev/null and b/ext/lwjgl/native/windows/jinput-raw.dll differ diff --git a/ext/lwjgl/native/windows/jinput-raw_64.dll b/ext/lwjgl/native/windows/jinput-raw_64.dll new file mode 100644 index 0000000..3d2b3ad Binary files /dev/null and b/ext/lwjgl/native/windows/jinput-raw_64.dll differ diff --git a/ext/lwjgl/native/windows/lwjgl.dll b/ext/lwjgl/native/windows/lwjgl.dll new file mode 100644 index 0000000..6819404 Binary files /dev/null and b/ext/lwjgl/native/windows/lwjgl.dll differ diff --git a/ext/lwjgl/native/windows/lwjgl64.dll b/ext/lwjgl/native/windows/lwjgl64.dll new file mode 100644 index 0000000..e66ab2a Binary files /dev/null and b/ext/lwjgl/native/windows/lwjgl64.dll differ diff --git a/ext/lwjgl/res/Footsteps.wav b/ext/lwjgl/res/Footsteps.wav new file mode 100644 index 0000000..074c936 Binary files /dev/null and b/ext/lwjgl/res/Footsteps.wav differ diff --git a/ext/lwjgl/res/appletlogo.gif b/ext/lwjgl/res/appletlogo.gif new file mode 100644 index 0000000..283e15e Binary files /dev/null and b/ext/lwjgl/res/appletlogo.gif differ diff --git a/ext/lwjgl/res/appletprogress.gif b/ext/lwjgl/res/appletprogress.gif new file mode 100644 index 0000000..3ed42c6 Binary files /dev/null and b/ext/lwjgl/res/appletprogress.gif differ diff --git a/ext/lwjgl/res/ball.png b/ext/lwjgl/res/ball.png new file mode 100644 index 0000000..53aa857 Binary files /dev/null and b/ext/lwjgl/res/ball.png differ diff --git a/ext/lwjgl/res/ball_sm.png b/ext/lwjgl/res/ball_sm.png new file mode 100644 index 0000000..0c9db45 Binary files /dev/null and b/ext/lwjgl/res/ball_sm.png differ diff --git a/ext/lwjgl/res/center.wav b/ext/lwjgl/res/center.wav new file mode 100644 index 0000000..29e6dde Binary files /dev/null and b/ext/lwjgl/res/center.wav differ diff --git a/ext/lwjgl/res/ding.wav b/ext/lwjgl/res/ding.wav new file mode 100644 index 0000000..fdb625a Binary files /dev/null and b/ext/lwjgl/res/ding.wav differ diff --git a/ext/lwjgl/res/left.wav b/ext/lwjgl/res/left.wav new file mode 100644 index 0000000..87635a5 Binary files /dev/null and b/ext/lwjgl/res/left.wav differ diff --git a/ext/lwjgl/res/logo/lwjgl_logo-with_jacket.ai b/ext/lwjgl/res/logo/lwjgl_logo-with_jacket.ai new file mode 100644 index 0000000..a220dec --- /dev/null +++ b/ext/lwjgl/res/logo/lwjgl_logo-with_jacket.ai @@ -0,0 +1,1086 @@ +%PDF-1.4 %âãÏÓ +1 0 obj<> endobj 2 0 obj<> endobj 5 0 obj<>/ArtBox[40.3667 286.146 522.334 477.897]/MediaBox[0.0 0.0 612.0 792.0]/Thumb 231 0 R/TrimBox[0.0 0.0 612.0 792.0]/Resources<>/ProcSet[/PDF/Text]/Properties<>/MC1<>/MC2<>/MC3<>>>/ExtGState<>>>/Type/Page/LastModified(D:20050904082821-06'00')>> endobj 214 0 obj<> endobj 215 0 obj<> endobj 216 0 obj<>stream +%!PS-Adobe-3.0 +%%Creator: Adobe Illustrator(R) 12.0 +%%AI8_CreatorVersion: 12.0.0 +%%For: (Glen Moyes) (Glen Moyes Studios) +%%Title: (lwjgl_logo.ai) +%%CreationDate: 9/4/2005 8:28 AM +%%BoundingBox: 31 281 575 478 +%%HiResBoundingBox: 31.6665 281.667 574.9473 477.897 +%%DocumentProcessColors: Cyan Magenta Yellow Black +%AI5_FileFormat 8.0 +%AI12_BuildNumber: 198 +%AI3_ColorUsage: Color +%AI7_ImageSettings: 0 +%%CMYKCustomColor: 1 1 1 1 ([Registration]) +%AI3_TemplateBox: 306.5 395.5 306.5 395.5 +%AI3_TileBox: 1.00781 0.000061 611.9998 792 +%AI3_DocumentPreview: None +%AI5_ArtSize: 612 792 +%AI5_RulerUnits: 2 +%AI9_ColorModel: 2 +%AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 +%AI5_TargetResolution: 800 +%AI5_NumLayers: 4 +%AI9_OpenToView: -176 819 1 988 914 18 1 1 8 81 0 0 1 1 1 0 1 +%AI5_OpenViewLayers: 7777 +%%PageOrigin:1 0 +%AI7_GridSettings: 72 8 72 8 1 0 0.8 0.8 0.8 0.9 0.9 0.9 +%AI9_Flatten: 1 +%AI12_CMSettings: 00.MS +%%EndComments + +endstream endobj 217 0 obj<>stream +%%BoundingBox: 31 281 575 478 +%%HiResBoundingBox: 31.6665 281.667 574.9473 477.897 +%AI7_Thumbnail: 128 48 8 +%%BeginData: 7644 Hex Bytes +%0000330000660000990000CC0033000033330033660033990033CC0033FF +%0066000066330066660066990066CC0066FF009900009933009966009999 +%0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 +%00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 +%3333663333993333CC3333FF3366003366333366663366993366CC3366FF +%3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 +%33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 +%6600666600996600CC6600FF6633006633336633666633996633CC6633FF +%6666006666336666666666996666CC6666FF669900669933669966669999 +%6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 +%66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF +%9933009933339933669933999933CC9933FF996600996633996666996699 +%9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 +%99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF +%CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 +%CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 +%CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF +%CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC +%FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 +%FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 +%FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 +%000011111111220000002200000022222222440000004400000044444444 +%550000005500000055555555770000007700000077777777880000008800 +%000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB +%DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF +%00FF0000FFFFFF0000FF00FFFFFF00FFFFFF +%524C45FD97FFA97E85365A5A8584FD76FF846114140E3614140E3636A9FD +%73FF7E360E140E140E140E140E140E84FD71FFA8AFA9FFA9AFA8AF858514 +%3614361485FD6EFFA8272727522752527D52A8FF840E3614140E85FD6DFF +%5227F8272727F8FD0427FFFF5A0E36143614FD6CFFA852F827F827F827F8 +%27F852A8FFA8140E360E145AFD10FFAF7DA87DA8FFFF7DA87DA8FD0CFF7D +%A87DA8A8FD06FFA87DA87DA8A8FD05FFA87D527D7DA8FD06FF7DA87DA8FD +%1DFF7EFF2727202727272027272752FFFFFF843614361436A9FD0FFF5227 +%20277DFFFF52272727FD0BFF7DFD0427FD07FF5227212752FD04FFA85227 +%F8272127F87DFD04FF5227F82752FD1CFF5A847D27F827F827F827F827F8 +%7DFD04FF143614360E85FD0EFFA827F82727FFFFFF2727F852FD0BFF52F8 +%27F87DFD06FFA827F827F87DFFFFFF7DF827F8272727F827F87DFFFFA827 +%F827F8A8FD1BFFA814A97DF8272727F8272727F827A8FD04FF5A14361414 +%36FD0EFFA8F827277DFFFFFF27F82752FD0AFFA82727F827A9FD06FFA820 +%27F827A8FFFFA82027F8277DFF52FD0427FFFFA8F8272027A8FD1BFF5A0E +%FF2727F827F827F827F82727FD05FF36140E360E14A8FD0DFF5227F8277D +%FFFFFF2727F852FD0AFF5227F82752FD07FF5227F82727FFFFA8F827F827 +%7DFFFF7DF827F828A8FF5227F82752FD1BFFA90E61A92720272727202727 +%27207DFD04FFA9361436143614A9FD0DFF27202727FFFFFFA827202753FD +%09FFA827272720FD08FF522127F87DFFFF5227272752FFFFFF5227272752 +%FFFF52F827217DFD1BFF36145AA8F827F827F827F827F8277DFD04FF850E +%360E3614145AFD0CFFA8F827F852FFFFFFA8F827F87DFD09FF7DF827F852 +%FD07FFA8F827F827A8FFA827F827F8A8FFFFFF27F827F87DFFA8F827F827 +%A8FD1AFF841414A952272727F8272727F82727A9FD04FF5A361436143614 +%85FD0CFF522727277DFFFFFFA82727277DFD09FF2727F8277EFD07FF7D27 +%F82752FFFF7D2027F852FFFFFFA82027F827A8FF59FD0427FD16FF7D7DA8 +%FFFFA95A5AA97DF827F827F827F827F852FD04FFA9360E360E140E145AFD +%0BFFA827F82727FD04FFA8F827F87DFD08FF7D27F82727FD08FF52F827F8 +%7DFFFFF827F8277DFFFFFF5927272752FFFF52F827F87DFD15FF7E272027 +%527D7EFFFFFFA87D522720272727202752FD04FFAF1436143614361461FD +%0BFFA820272752FD04FF7D2727277DFD08FF52272720A8FD08FF2127F827 +%A8FF7DFD0427FD0AFFA8F8272727A8FD15FF7DF827F827F827277DFD04FF +%A8A852522727F8A8FD04FF7E140E3614360E145AFD0BFF5227F8277DFD04 +%FF7DF827F87DFD07FFA8F827F827A8FD07FF5927F82727FFFF52F827F852 +%FD0AFF7D27F82727FD16FFFD0427F8272727F8FFFFFF7DA8FD04FFA8A8FD +%05FF611436143614361461FD0BFF522727F8FD05FF7D27F827A8FFFD04A8 +%FFFF5227F82753FD08FF52F827207DFFFF2727F8277DFD0AFF522027F87D +%FD15FFA827F827F827F827F827A8FF2727F827527DA8FD06FFAF0D140E14 +%0E360E145AFD0AFFA8F827F852FD05FF7DF827F8A8FF28F827F8A8A827F8 +%27F8A8FD08FFF827F8277DFF7D27F827F8FFFFFFFD05A8FFFFA8F827F827 +%7DFD15FFA8202727272027272752FFA827202727272027277D7DFFFFFFA9 +%A98585363614140E85FD0AFF7D2727277DFD05FF7D272027AF7D20272727 +%A8A827272053FD08FF7D27202727FFFF7D2127F852FFFF52272127F827A8 +%FF7DFD0427FD16FF5227F827F827F827F87DFFA8F827F827F827F827F827 +%52FD07FFA8A95A367EFD0AFF52F827F8A8FD05FF7DF82727A82727F827F8 +%A82727F8277DFF7D7D7D7EA8FFFF52F827F87DFFFF2727F82752FFA827F8 +%27F82727FFFF52F827F852FD16FF522727F8272727F827A8FF52272727F8 +%272727F8272752FFFFFF7D527DA8FD0FFF2727F852FD06FF52FD0727F827 +%5227F82727FFA8272027F8FFFFFF272721277DFFFF27F82720A8FFFFA853 +%F827207DFFFF2727F8277DFD15FFA9F827F827F827F827F8FFFF52F827F8 +%27F827F827F82752FFFF7DF827F82727527DA8FD0AFF5327F82752FD06FF +%52F827F827F827F827F827F827F87DFF7DF827F852FFFF7D27F82727FFFF +%A8F827F827A8FFFFFF2727F8277DFF7D27F827F8A8FD15FFA82727272027 +%27272052FFFFFD0427202727272027277DFFFF5227202727272027275252 +%A8FD07FF52272720A8FD06FF5227272720272727202727272027FFFF5227 +%27277DFFFF7DF827217DFFFFA827F82727FFFFFFA827F82727FFFF7D2127 +%F852FD17FF5227F827F827F82752FF7D27F827F827F827F827F827A8FFA8 +%27F827F827F827F827F827F8A8FD05FFA8F827F827FD07FF52F827F827F8 +%7D5227F827F82752FFA827F827F8A8FFFF2727F8277DFFFFA8F827F827FF +%FFFF7DF827F87DFFFF2727F82752FD18FFA87D2727F82727A8FF7D2727F8 +%272727F8FD0427FFFFA8F8272727F8272727F827272752FD05FF7D27F827 +%7DFD07FF2727F8272752FF7D2727F82727A8FFA82027F827FFFF7DFD0427 +%FFFFFF7E27202727FFFFA8202721277DFF7D27F82720A8FD1BFF5227F827 +%A8FF2727F827F827F827F827F852FFFF5327F827F827F827F827F827F87D +%FD05FF52F827F8FD047DA87DA8A827F827F827A8FF5227F827F852FFFFA8 +%27F827F87D7D27F827F87DFFFFFFA8F827F827527D2727F82727FFFF59F8 +%27F8277D7E7D7E7D7D7DA8FD15FFA87D7DFFA82727272027272720272727 +%7DFFFF52202727272027272720272727A8FD05FF27272027272720272727 +%7DFF2727202753FFFF7D27272027A8FFFFFF5227F8272127F8272152FD05 +%FF7D21272127212721272153FFFF272720272727F8272127F8277DFD1AFF +%2727F827F827F827F827F8A8FFFF2727F827F827F827F827F82727FD05FF +%7D27F827F827F827F827F8FFA827F82727FFFFFF5227F82727FD05FF5227 +%F827F827277DFD06FFA852F827F827F827F8277DFF7D27F827F827F827F8 +%27F827F8FD1BFFA8FD0427F8272727F827FFFF7E272727F8272727F82727 +%27F87DFD06FFFD09A8A9FFFFA8A8A8FD05FFA8A8A8FD07FFA8A87DA8A8FD +%0AFFA87DA8A8A9A8A8A8FFFFFF84FD0BA8FD1DFF7D52F827F827F82727FF +%FF7DF827F827F827F827F827F8277DFD61FF6184FD06FFA852272727207D +%FFFF52272727202727272027272720A8FD06FFA8FD0FFFA8FFFFFFA8FD21 +%FFAFFD24FF5A145AAFFD06FFA87D2127A8FFA827F827F827F827F827F827 +%F827A8FFFFFFA87DFFA87DFFFFFF52FFA9FD08FF7DFFFFA852FFFFA8FFFF +%7DA9FD09FF7D7D7DFD0BFF7DFF7E7D52FD23FFAF14141485FD08FFA8FFFF +%FF2727F8272727F8272727F82752FD04FFA852FF7DFF7D847D527D5252A8 +%7DA87D7D7EFF7D847DA827A87D4BFFFF52A87DA87DA87DA87DFFA8527DA8 +%A8FD057DA8AF7DFF8452FFA87D527D7D7DA97DA87D7D7DA884FD19FF5A0E +%140D367EFD09FFA827F827F827F827F827F827F87DFD04FF527DA8525252 +%277D522727A8FD0427F87D5227FD0752FF7DFD055227525252FF527D7D52 +%7D27272752287D2727A87D7DFF52525227277DFD04527D5227A8FD19FF85 +%141436141436A9FD08FFA85220272727202727272027A8FD04FF52527D7D +%7D277D7D7E527DA852527D7D52A87D5252597D7D7D53A8A8537D5252FF27 +%A87D527DFF7E5252845252527E7D7D84527DFF527D527D7D7D527EA85252 +%7D7DFF2784FD1BFF5A140E3614140E5AA8FD08FFA82827F827F827F82727 +%FD05FFA8A8A8FF5252A8FFFFFFA8FD05FFA8FFA87D27A8AFFFFFFFA8FFA8 +%FFA8FD05FFA8FFFFFFA8FFFFFFA8FFA8FFA8FFA8FFFFFFA8FFFFFFA8FFFF +%FFA8FFA8FF7D7DFD1DFF3614143614360E365AFD09FF84592727F8272752 +%FD18FFA8FD2EFFA8FD1FFF36140E360E360E141484A8FD08FFA85227F827 +%7DFD68FF5A361436143614360E3784FD09FFA87D7DFD6AFF845A14140E14 +%14365AA9FD79FFA9AFA8AFFDFCFFFDFCFFFDF0FFFF +%%EndData + +endstream endobj 218 0 obj<>stream +%AI12_CompressedDataxœì½ks%·‘ ú½#ú?ð~˜;vÅ.< ð˜òœÕ m9lywbCAuSR¯ùв»åÕþú›o ªpH¶š{l²ìO$‰|ãïþŸßþþ³£7·__~§ƒ—/þîïvw—ïoï~u@àƒÏ¯®>¼{‡ _üî—ÎNØêèóò•´ü—Ë»wooo~Eã¿žá÷ñW—7¿¾ýñòÝ/û¿ÿáÍÛÛw¿Ä–_¾}u m¯þô¿¾½úêêöÛÛË·¿´q@¿'ï¡A}_ùiJåW¾ý[ß~¸yóöæÛãÛÿó«ƒà|qiNq.øçÿñöw—ïVmsÎ Â/34Ž‡5ξ1–:ã·Nn_¸¾¼yÿÛ»Û×—ïÞín¯nïÞýê`÷ã ÿâ[øËÅÁ¿_^]ÝþéàøêâõûïœÝÞ¼‡¶Gwo/®>ûüýÅÕÛ׿þü·ƒ_ÿÀ7Ÿß^½á?ôßûÍåå›Ë7ùí£ÏÓWgo¯.××ï +aþèsç¿:þðöêÍo>\} «àj!xøŠ¦ò‡w0˜þNðù«Ï¯ôûË÷ïMð~ZÀݯÿýŸw°î·×Ôº‘çÿñ»ËoßAÀÚüÏ_Jß_^^ Åhžòa:5á¿íwm +c¦fîpšfX2 øÉî ;wXk-sõÒ¸­Æåo/ÿô«ƒßÜÞ\ÊäîÞÿþíÿ…Édçí+é«ß}¸º¼ûÃÍ[Ä%Ã*Oý×·o.¯F_?»º ÓãÚ¿ÒâË‹»o/ßÝ^}xO^&ý ÷üâÇK$(/ùâûË›/oÿ…†ù™›óAqú«¥T\!–ƒ¢orô6'=â×ñËÚí ?¸¿…Õùâîí·oo~ådhóWÿx÷öM[²ÙC·ôu}XºÿWý¿Œæüþý%îV%—ݯ»ÅŸý{|ëéÍ›Ýí5¢þm5XôØ°Aå¯íý ºøðýËÿñòE¨¯þ÷‡Û÷—ï Ç«Ëƒš_}{wñÃ%p‡òêôÃÝí S×âë‹w—¯¾åy{ÃÐ7_3 öØÛïß½…׿zsñí·—wòøû«×oï€.¾¹ºü?¯¾¿¼{ÿÝí‡w7o^ýþõÅÝíÍ«oþ©ï«ËoÞ¿úâôÀÅÄ}#ú£ß¥ß½·WRSýÀúú@ß¿º¼ysñî»W—×ôŸ÷°µ._ù¿¹¼¾¸ƒÝÿêÝê¥üåÛËWonµ½{÷öÀ¥úêßß¼½¼ƒ¿;xõîû‹×€Ž_½þpwwyóúGø_}}wûÇË›¯/`[º\^iûW¯o¿ÿQú¼{óÍåõÛ›·7ðõÙ¿ü¿}}qusûþÕw?~ÿÝåÍ«;Ú›ðÅ7¯®/^ã°­°H¯¾&ßüðîÕû?ݾûH{{{÷êýww——öéâõ‡÷—¯®?¥†W{óÖŸz{}ùæíÕÕôûϾº¾x÷úèüãÿþpqßÁ_¿»¸ú†ß!ÀwÀü«#¢èêˆßxÔ­æã÷Èf„xutúj'CxuJ_uJ_†^N»¯ŸÚ÷>çVŸó+>ïÚ|nmNß÷ê7ôBèæ þÂü…/º/|Ácú¾wýáêýÛï¯~|õÅ»+¤‰?è„þÀ_þC÷å?Ø·þÿøåw·w@-—À´o€ÔÞ½ºà_è|.ºo_ð«/¬“ BÇÅå«×ŠŽKþú%÷~Ù¾ ]]Ú÷Þr«·Üêm÷Š·ÖæÐqÃ/¼åæ·:¦Ûî ·Òľ÷æíoÀÈøÀ_ýÀoú°ÏûÎüç÷„ŒüòÅ—§Ì Ó?}õå;àüÃ_£9½y}‹ú¯¾Zž”›ƒó?^­Z¼Z}ùþ—ÿßKêxšö}ðå݇Ë/üþò#³9¢G‡6 i}µý&£‘LÖFÆ£ø +†õ[ Œ÷´×ó=ý­|õÛ«ðǼ»ýðýç7ßܾ|ñ –è~{ñþ;]€¯¡\Æ0þxÀ_èùÛòØ÷¿| Ë/ï€jax_|ý¿._¿‡ï  ýöûoß_>¦«ßâñtwóÅ óîûï¾¼½½²¡Jù“ŽsþÎ_ÌK¬ñèðÇ¿ÜÎwpöÂ>þþ; ÆAÿƒ¿Û‹ö|÷1¯…ã±>zãòOö²í7þ2ÞƒˆþæíÍø +~ÃÜíõ÷¨‘üþ»‹ïŒ-Ϻ–ÿÉå7 ówkKÐÓ›.¯n¿¿lpƒÀIsð¯wß? +9?^}{õöÝuÃI±ßÓˆšwÝpè#ü÷üï£véÕÅÍÅÝýÁFCœé·ÀíVÜŠ`­Û„|³çŒŸ}v?Ïtáàø¦oòwxîÞ¼í”4½ÿ~ð¯ßÁ +üÀWð–!·?8~C¢ðß¿|qvvvzvr¶;;>;:«gål>Ëgé,ž…3æΦӳÓÓÓ“ÓÝéñéÑi=-§ói>M§ñ4œúSw:œœžœœìNŽOŽNê èr'óI>I'ñ$œøw2íÎv§»“Ýnw¼;ÚÕ]ÙÍ»¼K»¸ ;¿s»éøìøôøäxw|||t\Ëñ|œÓq<Çþ؃Fqtvtztr´;:>::ªGåh>ÊGé(…#䎦zVOëIÝÕãz„úYk®©Æª¯®N嬜–“²+Ç娀ºóòE™K.©Ä +èÚešÏæÓùdÞÍÇóÑ\çªMžAûžÃìg7Où,Ÿæ“¼ËÇù(×\òŒzzŽ9dŸ]†1¦³tšNÒ.§£TS=?§”b +É'—¦xOãIÜÅãxk,qŽ9¦cˆ>º8…³pNÂ.‡£PŒ1Ì!‡bÁÐIü™?õ'~çý‘¯¾øÙgŸ|ôÁ{ïüäÎÜ©;q;wìŽ\uÅÍ.»ä¢ Î;ç`Œ,ç 2O€´ ¦=å †6Aç“CÕñ ÑÁß}u|Gä1™æ™ð?GÕë«øø¶t ‚žVm»?`ë㓶”ˆ÷öîö{å‘–å7èmîÈŽhgÄ#êùˆ~\\\\\\ +Z Z\\\\\˜£—/`qpypp‰x‘p™p¡p©`±h¹pÁpÉpÑpÙxá`éà™iñpù,d¤EÄeÄ…„¥¤ÅäåÄÅ%ÅEÅeÅ…Å¥ÅÅÅåÅæ%ÆE†eö°L°Ô¸Ø¸Ü¸à¸ä¸è¸ì¸ð¼ô¸ø¸üHHHŽ–ð&e¹U–›Å¶ m–~»,7 m™—/lÓ,·Írã,·ŽlÙ:ËÍ3ÃÚöh¹…–›h¹úD[I7­D¿™–Ûi¹¡–[j§xC†ô”ìX‘bø‰ØÑËNŸ„¡É¤añãØ‘âè¯=ëŸiù£Z>§«úFÖ?»Ís¼zŽVO]>ÐcY=óêÉ«'­ž¸| Gd­ËǯžÍÔù‡ðæ„þæðQß<®{`*Ð#OéÌžSyNäÙÑsLÏ=Uv0ÒÛL4‡T‡tÐïhÜÓŽÐs†Th;g÷ˆgýsdO•§ÈüY?²…cbÄÈN‘%Ò²ŸÑ~Å‹{–w-î[Þ¹ºwa÷‚ á»l{XgâÍø)›gîEh’‡Ž<6 G‘<‰IŽˆøŒžS˜XdB¡ Å&œXtRáIÅ' 'G#!Jg@\®=2ÙµÚ¹e'ý<:½Öû¸PmClŒC D‡Høƒô‰”Z‰r‘Hšq!ÆψúñU8IØ1ݹx È>ÕiØ„Š@‰¶À 4SzŽaQžN΀Æà8õ%xÑ Ø¯° +øÓŸÜfÙbëvz|ìÊÁjX×Ãœaµ`åw@§»3  4€jL`úª_Eü ža '…G4âñzLxFŒ#îahE­®®®%¢pöeg`EÈ•ðKȽ¯!§CÄ# + ñN䦸Õë"#‡®M ÍØ¡óõ8y”ûÆé†ãœ‰~QdÝIÒnÜJxªƒlEÌ_‹_$¡ˆ, +—e4”ÕPB8&ù 7$Ês ?€ ˆÅãkñË‘¤ $0” +Q:D)Ù.ûŽ¶4J“ UÒQ€Ä‹ÃŗƳ†³IÅ#”MQFEYÙnè”cuM çŠïD˜.†åá÷Œ0ë³¼ò ›Ç„GÂ"¯4`(öfÂÛ‘aì”påK°” ?Œc `E0â€!ÅD%ìtþ4wO³Ž4ç™f«3ÅyžÑžÕ›h¯ÝÑÙrDû÷èíQG»3ÚpCÏ U`ÙÇÀ‘µžÎgpL; …€KˆÍ I¨ŽH²:ëd­3 RÜ&õ$2”}kãí†ó%Æ—ø^b»Çµ`Zð X&^«x^b¹ÇñÆ_Âî·@•n—˜]âµÃê£+|Î$³7|.±Ùãr‰Ç%kÏ1á]rÌ%¿nÙá\ÖRòœõ³–¦ÂFÞŠÆ£ÖÏZzS¶| É„uð¬åɣĩ<±{ Gå’Ëg-á +]?[4Gý,åíÍù¹Ö)å|^ê“M›œ‡Ú¤Jê0V’ÇUê>óB1©9‰™!˜Ì;)¨ÁCtæû4Œ‡M0VôNöfÑclöø{äúk#ÌÇé<ëÑ,sÿXöŒd¸ÆÏ£~{í‘ßXÉ[jUa» +[VضÂÖ¶¯ kìfia[ [[ÈÞØf=ž¬.¤ƒªÝ…-/l{aíž-0lƒ "p:µÄˆÎ¼‡Îo¶È°MF­2Gb™aÛL%)«…†VXm4AÄXX2èщÒv&öšS±Ù°Õ†í6l¹aÛM]HÖ½ÄLƒÖõ]i"MQ+i!fI`=äxa³Q‹Ù@nU»‚ZØv³´Þ,í7jc`+ƒÚĆgC=Ò‰žx*Zá±YŠé€Íš£¶¶>¨ýhhHíqqeÕQ[ÄN¨¡šõ­Ùv¼ÙÝÔævlzÙÊý©hÒ,&èOµø€,²2AªÅ䥕 úS->Æ{ö[]Äö2|¶ö9  Çѹ¸==6yÊžSy{zí4ckMg³é$}¶’ÆØ~3¶â¸ÿDSúá‹u0º‰"Êœ …ËÂÀhþ˜Ædv? ¹`ðTž"¶Ï5zlÇ7,©ëôá¶Üg þ*þ§Žp )¡‡Õ…|0÷?ò q1´mÎñG„8öSœßïB¡Õñ?‰ãû|Ìv_¸©Ì¶o®Ø Ì)`»Td±|?¡‡š~¤{äwoo¾¾ýúGôW˜pîý{OàÓë*°Û'ðéõ ŽÃ'ðéõ Øíøôz†JÐ'ûôzq’…‹Oõéõ%âJ¤dÞ!`Ç3k³ÀŸüHcvû“èñ {[õ¸ï¸øÈz|¢žöô¸vCü„gëÌ`ZXÈô§eñÔ{ž£{žc} ÇãÕ³»ç9¹ç‘èqûs¶ÿ(̦8ó Ñ Ïö>$ºBqø¤{ž¼÷®=Î{žrÏSûÇìôW=ÖZlë‘öªÇZ«@’}¤½ê±Ö*`ÿ´W=ÆZ…v{8“k¹/ÛîáP…1Âñz¤~"µƒc7Àñ›àži'ÑÞÁ}pF´ë‰ÚÑ®4 íÌ| {¤æÇ=bOÏŽþ,LøÏuô|Ò»eÛ÷1ÕûŸ5{ÞǶ?ñÙôxßññ¨züØïÜwœÁ=>Ðâcé±üÔÇXvxÚç/¼G²ãÑAõ¤Ï_^º¾Ÿ¢}t¯—/>Eûé^/_|Šö5Ò½^¾øík¤{½|ñ)Ú×H÷zùâS´¯©Xõ§z×RH_ŸèE\Ke Ø~¢q-•ôõ‰^ĵTöòÅ>¹ì§Je }í‘Ë–ÊLêzœ‡±–h–¥’ç:k*ô8²±Ž¬±÷x,ÞË‘ÿòaoæг¹°7?àé\={~:¨àl¢{è?­øò)"ÖÇhÞÃç^Áò>]}ï=Þ«ÝüÏÇÚúgÄž÷ù÷=k'Á(àóÓž±…íimv?Ÿ…í‰,mPî~‚ª›û©,á*‹üõD–p•ÅàÀ{"K¸Êb =‘%\e1 +i{K¸Êb =‘%\íà¸{Ôî¼Øþì“›f*AP +9–@.SwÑÊwo;öÕ9§®«YÛ”¾«{›IOS%wß\"½ DâðZôt_3éɧDM"½°®M⢧ûšqOrl®EUŸésЪ{øVîù„Ç=Ð#j!÷¤ûèqÿ_óOy Ç|þé™$æ±B<~~¢)÷ÑÒÌÇú4?Nj»Wjș㠒ýÏ&¸ä§vªë„87ãÜOâÜŒ0ÿÉ!ÎÍ8AЄ87ãIi÷8ö'VJ¦Úg{4+Ù#Ñ“§áxê표ԩ¡GŽÏ:“§i\ú³³çXž#{ÖÉL€.Êå¥0iS< öø.•ÉiBS—Ôt&²ò©¥7qŠ“þhØ‘=Užb2˜Æ¤;lìS™¯²êÆà5*íìx¤Ún¹ˆOÏÉæY'ÐyËrZ?Gƒ§nž²| GÇ^?yð¤Á32jÌò#{Éèg>´ç<6’pC8Œ |lôà2âï)Ϥt’PË` ëüEÍ`”ìä¥\dyæ׺––ÒRFZæœo%$Ê®ÉýÚÇ=›4´È??Ä:/âœ_¾XE9k&zŸ‹~ºÊG_f¤/sÒ“dþ-óý–™~÷dùu4k4)N¥Fg]vÐJËêÕÕnƒòÏù<ÿùù<c‰výȉû,ñæý´˜# ¤§µÀÌ °>­†Ò +ŸÔãaŒû-0‹­¥)¡-!tž–é -t” +š, ”µ> ô¤K_ܦ€J¢ª¦vÉŸ–úI±•ÛÔϹKl\¦}žu ŽË„OI÷¤² ƒtORœlã–ô˜iž‹$OI[vT¦¡O[nIËFÞ+sÅ cœuZ©Æ–éÃ9É?ð§á ͇ÀÅÓ÷¶{È”ÒavåQæ i*!Ù4îèZ´vĺ·¡ïjî£ôÁÞ®"½u_þù# I¸¹¹¸¾|sð­€Ð’´=‡_?‡_?‡_züD§ÐG8‡>­ÇŸ%Þñ'dv>êù/Õã“ý||>ë_BëœÚçøûçøûçøûçøûGDË?ùyôg9…ÿ\²Ç'ÉXÿÀ¡>á?áÿe„Ì?}¡!óOßãsþß@þO í|àytvêCð—ÕãŸE»ÜÔ'|ΰxΰhÏÃ2ëÃÒòs†…>ÏÏ›añ÷OlIûy-’Ÿöü%ŸæŸ"à ¿ûÏ©3Ï©3Ï©3Ï©3ÿ9©3dr{dþ̲íþ$š®Ýƒ™4˶ûÓi8Såq95˶ÿåkÖ±nP迉x>;žÏŽç³ãoóìx6Ù<›lžM6Ï&›g“ͳÉæÙdóŸj²yYxÔó²ðs<}¡Ï! Ï! Ï! ¡=þY$ç°ïç°ïç°ïç°ïç°ïGš±ŸÊ0þIæúqØ÷s:çs:çs:'=ŸÈI>‰£|L? }N眞Ó9·Wü=‰þ)c~Žˆ…Ÿ%²âgˆyº•OŠ‰ðã ¿(:ú÷ÃÐ*ísŽÕŠ¡bqOÅ¢±£tŸza +ÆË c¬dìs˜vj†1Ï'pþö +;?J]Aå“]¿¨.ÙMµ¬gŸµgál¥å|÷„î‰ÝhE æî™»glf»×–¯}®©±Ò¿QêWu¸Z%®V‹«Qx£ƒF tNUúU ‹r@•ª•·B@DM™nô¾¢xª(ë:iju£”F+Z½ŬÍ< QxŸ0<6SÅaç>‘x,?x‹:ಌǢñ¾HN8Öµþ3© a¼ÅP‘§P,¦‰MU=õ÷%›‰¬ÿéwÖÊÉf¦¼}©¶ŸçAÁÐAÑPèqY:tY>Ôw<§/ÌØq(¨Î¸Ýç-ëÒV¦­M[¦Æµ5ZÐ-¬È±U,œå>v5µ+õ¶õj+ÖÖlEÁ° ¥Ö`±ƒ¼zŽ%[ÔRSôÈP¼©Öñ|>?ŸÏÏçóóùü|>?ŸÏÏçó_ØùLæx€ TÔç#þR®#¥ÿᶚT‘f´ +$jQ`j˜ËáTj]vjË}ÞÿîkðùÑcÕö×ÄxýGŒ÷ñXýtcÊØ–B¦¿¼¿eZ×~¨ô² ðâû囇‚Ï7.[8/–RνnÛåt^¾¸GÖy¨"ô Ý*¦²|µ|#œDŸú¾uU}RèûÖ] ²à#Cßr£yvö ¼Õh³Qg£Ï&{+… +Ê­J§M +/]qä-Œ¼¡ÕF­J¯p’ h¶4ÙüA‰‹h·Ié~%5ZjÔ¤ôÔ(ªÑQÕ†ó#ËA+50 ³ÿY +##‡*3ýæ/ŸÌ»æ1óš1=.v†àd‰´ruâ› d¹ú +ÖãÖnYÚ®hŠâBd^-¡2e?5•±éÙÁ’!¬Y˜)[–°/#æŒA7×3æž1÷Œ¹ÿ˜ëñâÀ>úPÇ» tužû^õ½¼1ãþž¡­]ë”â‹*ÅadÆaTÇz·x¢m4‘Ľ|AESDQEÝå†'tÓ1ÅÙ5‡…"fŠ6Êrå!_{HÐF éãÏún$}î å <Ì'}ߣ®í3ÞŸñþŒ÷g¼ÿ¬x_Æ0®¥ýô™zÇÂr³· ›€2†ï,þÙÆúìoc7´tÿw.ÀßÓò’–a þ>Y–ìÿ›ÞžŸ¬¸Õ±âVQq fv:t›ºÃv©µ?Êk¶MèìJ¢¥Oô‹¨MíKí‚"½ž´°4±®>¸œˆÈ&BŠíÉ4wGw“öþò]÷›H¬d=^…ËJæ‘èôÇr¯£Ü6Ù™ÎWy¸ ,Åâ£Ï}ÆèßF‡ÚóÚÿ7®ˆáŸåº±ÕŸðš³üA¼çë­ÍÞÃȧC_¦zÿ¤>õPJix(xeMÜ8Bžò"̽ +s˜µöòŃ.QÐÊÚÑÙ3€—ä¥ECmÍ1Á® u‡Ôr´¨‹¡‘ï~Q¥aY8l™Á‚زú Õ‘yùÂjɬ«Â,ë»,«µ,rÞÅ!ÀN+†iÕøQá´Š£àÈnU<F´“ûõŽE +Ú¸™ ¬°ƒÑŸ O´'É“í™í)ƒû 1üàI]>Çî‘NŸ­5„?ºÂ&èW¹OòHUô^É^l-yMOe2Ð¥Q±ÆE0Ë…¼³©Už#yŽÇ˜ënÉî®ðîbä~m+5¤KÌ׺ùÆnØJ/_›+“«‹¾ó›o?"Ç×19¿vä;!'˜„F-‚¡$êå ~jáN-À©5µÜd [j¡Jš$9¾”ß«9»-·’[ kÇÄA€?K¿Ø¨‹¨ù¹”¼6*‡ÓœëA ‡¸+ú³ñ–MgôAoÙÁ÷fwˆ¬q¥8îm†=á1ŽY#£ Ñß>òxþ—··W—ïN¼ÄS¹û„=ö·êÏ:ƒ~Ðs¶[=Ç«çHèQ¯›g”¬>Jn_$ÂCüÛ8~œrïIÖ÷…5zFE†±Zý=Žb¸ö?WBëã‹z}lá°G•Wz‚rKO_é)zü)Å‘ž¾ÜÒsŸÜã?(OcÆœJyQ1Þ’ŒCxÝÕ²'uï]P9+‹™L’F,RcO)¥þD”cJ­?"á¥P‚ýL …Ÿœ€(AÉöAÄ!Œ&Jº?£ÄûS§v”€LIøG”ˆ©ø…ÒñgJÉǤ|| G” X¾`Qdk’ì…Mó&IŠ;>æŸ#y*=d­†ã¦‹Z|Ljöq’'Ê£r·G%{©QyÔ@ç˲áNìÙÙcC8êj÷”î¥zœÉ@ß?iñÄÅV_?УÛ<ƒêœÛÞY¾|Aÿ=¹÷Ù=ø·z<¾ç9úøzäßžìzÜ÷S~Ú=þÄoþ×îQÔþûŠ üôÒ‰"˜ŸäùYË…ý¹Š{ýu•GhÏ“—xîñ¹ÇÇ÷hrÊh(Í$“4FrÊ_(y¡¼…’JX(Y¡<Å¥ŠPrâ"E(!q¢D2PòDZœHKia¢V.²•$Z$êÊ?ï =¦LäÞ<&ÎdzùÂr™úl¦–ÏÔ2š´d¤f{-³g(³ ógÈÄùMœCÃ9NœEÓòh™—²jRƒ5 lG"ÜQ—Q3SV¤æÕh^˜d×3>³›5Yøã\›*ÆËYrn0ësØHË6IÎÀÁ‡™,+ק’sB¹T;ʧ:–ÌÎΩ”¡CÏËb`Îò$yÔ ­vPµ·ŸÆ;¥3´4•©{ZQõeñõ…(¸¯šéyLù±ñ9}ÿ½"OPF³;Ÿú´yB~ñt}ý|=®Jj6`[ y^=yó¤õcYyýVßÏš¾hù'rú‹FÏz|>Y‘>ÏZºêÓ3Q‹(ëɨåúÚù¸~ƒU×5ïVÝYk—œ8ã̹Õ[êÔ:µ¼ ÉœXAÝV’­¯AìGâÐÝ©3¥‹CXg ÕÎ}ÒE"¼|±[_æ íËq»7ÛŒÊCo‚Õ–K ¦øØŸWûx@çÒ=Ô{c?‰]l™ˆZY‰û¡zìòSÆþTqc¼¾ÈÒŒ]ž‡øØbdË@·8!¬ÀVXâe‹™-n¶Ì´Ã`k› ­Úâh‹¥MJ=`k„©-®6Øú+Tû?9ìùÆrSþþŠÎÈ ³¨ñW'.VŸKôsç±¼·™”Ô£z€‡©f×|›Ãz‹ï«éÝ×Lœ°p– ³6&m’Ýa +%äx,{î=±¶GqF-EFM“Õ-œ§Cïcßßýí,‡³K´ÏÉÍwu÷Çtîþëwoß_þ÷ƒã«‹×D÷îâsŸy®Îq„éìXsILþ´³c}rwüijc}rÐ]ŸÓ:¶Â ”—Øõºíc“’òÕono~{÷öæýÛ›o?û¬'ñþ//_üæ{ú[à¿ýöâýûË» þ£ÿûáîòàwÐæÿñÀ{ؘôO*òŽñÔÉPÿíGþüOðûÿèŸâÁ¯þãNo^"üß~‡o}]ÛGêõ?ZÿÝGjr¾ü.}¼á÷ýNÖŸÀê¦5Òpþ™8èáœþ¯“vL¯¥>¦’‚?Œqª¤îé·×Ô|LÕ1$v_ŠÚ5ÃO¹@7.èWæ¬ÝÑoÔN_EêS¿ÕH ßÙ(®è#õÞÚƼ²õÖÒFL}t#!`úG¿ÕZ¯FLÿ´‡i9ⶊíË:›‰~).CHŸ–ƒn4Ò°§¯"cvêÝ:|·$ºkû¨ËLß[L¿CÍ´A›|k5ú¨ ­ßj$ÐÈCßFú¾~«q5îFôQÛÆmÃÞŒZ¿¬_Y- ý¦k½˜ê + Ñ ô[m€«A7 +áöÓrÐÛ—um”ú­Á2î§å¸;B(ëq7Ôê·Zë6î¶$×öQ—›iŽdËsÊA–ÃYrƒÒ µiD¢o#ý£ßj­WãnDBuÅmÜ6ìͨuÐÆD–«²dýTWhX ˆ¡ßj\ º ·Ÿ–ƒn{¬NéÙHÏ|V4»ä ¥#’ÕFÓ·µ5Ðoµ1¶qÿáå‹S9^áH–Ãu|Þî.®o?|suñ-…vŸH@˜üouÆúGŸ±ØÃ5ÿÑYIÚ¯Žsú‹œ¤_H‚ öÎwÕÁ=£÷ô†ëEg­ÿî¥m$ßH ©Uòtjó<¿Ä²c¬­kù,ÝÆ|8—à×íUíÛý ×ýv\©¼ré**Åït0ç sò1ΆV=Ö ÷‡¥¸ Ÿ2¦Š^~9ŒO @R‡>­>„Ã2;Ç2b”/6C³Üæ³w‡S²äRñ¦]Oeý &žS´/ vY˜f íÃ䧴ù˜§bóCÕÃyò°š& Ó[ÆÀÝ”å% +›§”óÌ ã&_~„b½ Jañ0N1À@fÐ96 V+ó:.‚~‰vBÀfÎÁm>&@ÞdèeX=t jèZÂçàJ´Ï@ ;:1fa|J0úYÛ7™@§°þìsÿNʸ`1ãÔÏ‹©P¦š½K)l?÷T b:) ‡o?w ¥0]L­mgû¹' +Ñø|jL›Ï º3 “&ü4µ²ùØÓ¶ux,ÈŸû-d@ÙdÍ)m>÷›Ta²ueëGÇTÑBÏEªwÈ=—Ô«dþૈ‹DØsR&"Ÿ°d€¶‚æ|{±ýfœ 6qðyÁL(è‚·‚î7Ÿ87 ¯ Ð +–äÝ|¤•rP /FÚ¿n?÷4d@"4j4cÂõçžX &`¥æÍçÅ®0 lˆ¬Özó¹ß +4f‡Ÿ/uûyÁ](ì@VY¹…|Tæ’*Gj§“~†æÑ»Še©ë†Ra‰õs·ÙÁ¸™*У&™ n²õgè(S¿¤@`ì“Ç9Mxè­?ÂTKÌ+$"qŠh˜ iûäB7É{ 8ÁûfCõó¼þæC_Œ¥+0€ä§T¦‘³ß|öØ–O +̘AÂ3¨xì­?=eG +¡ ØÍÉ ÀÃwý9“åKt‡ÁCÏ€²9f·ù<!ädØ&X8¬s9ÐUŽ¼eô£°ã³|„äø?no.|}{ý5 +ŽíŠèµE9xiv +D[&Ø.°>yNŤIwpôýH”{HªÔþ´ÿkQÜ/=ïòf®†³.¾~ã<áA‰ygoÉQ²óx2òä¬pºP§Ì#T p÷ ѹ,0°SX8I£lLÜÖ7n×NaÚáâËÀ]˜½LdõêsÓ ^ݽ?yûúýÛÛ›‹»~E° àèîîB~ÿEýåÁ«ß¿¿{{óíÁGÀÿ{ð‹ß_^ý‹÷ØÓÅû jûKþ[ÿEðêó›÷Ûïðòîó“wý÷ú¿yñõÕ%ŽHÿ†ÿ0ò•Rx-¨jHdË­ž®ÑÁOljc;ÐB™ Ø[ UÎAÂnžŠ[b1Â~™‰ÀБ}õí¦.— †tB6ëWÿ9– <õèfÕíð¨}¼6Εi1ò‰Ô”s‘#ç#oݹ¤e;…QwÎ-¿Œ› *½/ßû·ºPîr~µØ“¶â°ß=ÇåPØ‚°mú-°~õ_ÉøÒGtd´A?QZœ DcPLÕ©\"¨KYÍL±lÃý¤¼ü + +ž%ÉŠ­ßøWBöçø ýTž0|†¡‰/ùò@ÍÁväx–Y¥l-¨].¾óaYXþòÝ{ä>£T=ñ…yì+H¹o@ÒÊÙzüù…Ó•´l¨@ê2§Å·AYp^—iýî¿5ÂO0|¨Ô]/þ AÏÕ-±‡PÄ6Ê2ò¢¡µËþÛ ý†&=­Þý·Gùȃãšò¸ èP‘Ô%éÃZÐ×-¸ }ý:Ñ~PÇôêík´hËÅ»%í+pAÒ†}$~'lh9ÆP–-ˆ}6yH¾NÔ_U Z½ý¯„úº¤Ÿ;©Ö…¨|¼d#w5gdàêþÁ~ÑRÚåâë3þQÉ}õî¿’-ð“Å}´¢³÷Ñ*4ÇùÕ–²ý¢!go].×U§´ø-ÿ&w‚`[A +7ËC¿ÔÒ³h©À‰ë×{aýö¿ÕÍ 6˜ÅfPà‚ÆýfP[Ï¢¡$n‹Ðo†õËÿ*6ÃG†ÿéÃÍ·W—8ìï/)Šo9D;rŒ§ußム¸9i #sõw¿:ed³ýzÓ½P‚øÍÿÜÑ…9ÞpÈ×Ò+üz…¿JÈpØcrÐŽêCßûC#VzÝ¢±ƒ_üòàßþõ#ñýëË7oá×»‹›Ë‹›ƒ/ß^1ÒG`´ñD`+ƒ¤ƒG·—G›A"_àj-o’_õzÝÁ¤k“¢º×-`úÝóAmá46DF÷Çn/#C˜S‹º„µaºX)æg6Æw—À0µ² Jë„–˜BÐ èö@ß­á€Ú+‹‹M•a¥¬{/¾¶^)®C‹Û€à8˜Qì‰÷¼h8$Ÿ‹¯dDªaÒ3=°TKßð+ ¼Ôˆöí@GFw¯ic\¾ai?2–HÈÈSé:E£ ¨plvo÷°vœ§Ð†Yf2qeçV@|™½dæ<¯½ ùõ!×{Q7êuôvòEÏI`z¤à”a éËa¤×¨òM_ HÃeûšé ÉüýºìÈ˸z²°zâlH’ŪG‚}3BÙõ} ´:‚ ñ½~ÓïÙ×=l³1cí©öM¿²¼×=lH…’æªÇÕœÚ~Yì¡Å~2‚möÚjÿ}¬7÷·?Þ]\¿}C'‡ýÎé>ã.ë&Öþ'ÅRgl 1óý””Â;¾ûðî;ë뿹üÓ|‚sÿ—/AèǤ”ûÓËð—~qÇôÆÔöÙ\`°Hê!fõâŒ?›ç„æµ:øùîðh* —. öíØøÏ·ëÞž@EßîÕ-hY:WhÈ ]c&ÇÜlÆ$½œÖa¤>üâ3Œ šæš›¦ð‹ãã£×¯?\ÿîöý6^Êòø +ô=Ó„ü!œ¹N` µ0¼ÄšuHcht +˜7Æú° L3œÜ2TFU¹$¡ö0%8lx²¡Ì‚—Êߟт/H©  ©vŠ¬'Ç಼lš‚ôPüd#˜µ‡ ŠXw1J“ ×Wj`N&F‘^­_“` º°1ÌÒ8Lq ÅSÐåèZ+¼?ýÒÂ(0 ±êbiK0„‰W+Tw ëuðm]}žeðÞâí¬Œ:âÆH ÌÁKÙ…p0Ãîg˜æ,” +K»G§ ÔÈU¢r:’Îx•c2` I§3Ù*ÝFXs¢Ô)òy:|ãÓ¯]:Œ.+Þ`$:) ûsB¥0膥h\Åù$À<¹,ÀÂïN;H! ¼bÖ€öštB)h«!<ýÒåC¦Ô½÷ZáSž…uLer2ž’u/V]ºýÓ$[øAôÚØͳ"*ëÜu#ë¶JJ26ŠÑ•Æ[»x¶ý줥‡74ò)Ew‰K™1`7ê.™½S™…*FEÛ¦˜„I…ÚYÃ@UŒá˜¶À@øUÆ–3†C ?ý"î'Õ´*ðŒQq•ÛÈ@W«ï€6}`²Š«¬9³1”ÿ$RÅ%RFVÂä—!w¿:D;º1‚\¹O +³kðU?OÏ;@{.Æžðà¼Vx4ñ))´=Q –ÐÁL2G;¹g Gk¬Ži}.ÊP&f«$J±•ÖCE;(K³ÑsaÓï=:f4t Ó„†s{ú•.xDÈØ=ž×¯NÎüU1UÎÎ)°ðÚûÅypóžeš»vë†CxòYb<¹¼‚Žh™dÕÅ’FÎy2Úñvl ¯jnÈ’Çípô YËä3•fP. Š¹4…G&ZS8{P9²"¤6c„5ÛRÑzˆFQµ2CÁwe/4 ²fh=”) ±§IHªÓ®2°XB”¨%ȺV8@ÚF§â/ɼFÁzÞ,ðFô¡pÊã7Nsaø(컚Pêéd·!û’e~ +fHI”¼$¯ªQ鳆Üõ`’|Ò Z­1&Le]ºÁŽ B;¥•(gˆ¸"04V3Kªú9è¶LJ 9O^ ×™^(™8Kà!š¤^:ð²›'gÃ9pö…›©€ÙcÉ!ã©šf…p7«l‘X¾Éy`<[0ïÑý(˜hüØiÓƒšÂ@ŸT)¡L +b†o»âKë!ûIà¶Ga4³“wšYµÊ“Xy°s°ðxóºwq·Jc9OàuiŽ¬8v&ש[`ƒ Ãt®(Á ¨í—XKûz‰ÜÒ1kæQQ~ ·¼ÅsâüE&ܼ6…ê”Õ‰Ô‘Q”‹½6,½fe=X©D;€Žçb,‰iKÚG•òbV:îv9å_+Êyúmä1ƒV5ö¶‹dÙ…ÅF¥–`*b]iÍ©F¿D·­ËhP瘫 =‚§Ÿ#ˆ ÖÈBzU.<§ëëƒ3 +¡™û($±RÂÕÚêñæÛÄ×#xú9bvœŠdÁûÐ& ‡PÏ ÎÞNaÒýÏÍjû4Ù¶)¦æ“8|ã“OªP°‰0gbu× ƒXUˆÊà,‹4‹JŒ“HÙwÇ1¯©rªé“Jºµ³»ŠX À¦½t Ž¥ö¬'\©Ú8©P¯’ Z½|tªz¥ÔÁØ€½dxP‰%Î20œPÒÝجOȃLŒ!#¤46ei.L€ÄH~Zã¤<ßMW¬3# Ê4ŽÓDt=×^«lx˜kÇ)ësáqÅ$ÀÉ©°QÌ ²UÕ1rfhöhR”¶µyNœÅ-Qç–]XámªvO.Ù9Z;=ˆê™‘>[c£¨œ™Xi‡d†ÇÀR"&GV=\)}S^•UE‰Î»ÖÃdZJMü2ÌÍrf`Ë<,PžB’W%á ÇÇzT8mÜN±ê„qÍƈ=Û4¬‡8[Ïkß3{uc±PfIæ–øgãr¹šý¬ÊYÍRÝ,Ø2*ñº)¤ÖC2é^© {jø¬SV`D_9sÏj¶^€ÃÊëÈÈÂÝ‚J¤"](A1Í*6L&Õ(ëy› 5älÆ,dFz OÈP õµÁΘÌf‡¥ï”¢„áϨ)­NÕ–¬j_̳m!Üz*VÖüæþpÅ% +pìˆÄh{È×” &í¡[!ÑÔæÄöo~ª©ÖªàI#o•ÉesS8æE!.ÈH÷œ§f·½Mûz¶QížU€ÕÉ«ðƒö{é ' +icFÒÙÜoKb9^%»`LáÖ8EkœË¤Bÿ\g6͵?ë ä ÇŠ `Ó¤ëÐ:mf +—ƒ«lciô$@Wõ\ò9+przÜU3¼$Ý>!±²Lœ÷ÌhDÓsÐ}]ÃlG ÇÍ‚_’±Ï•ý»Æ"ñÜa`[L[ù‰Æ s¡;‚l’ï›{(A5MÅoÁȯ³kÆ$€ç¢”DŠ*̓Ƨ‡ÑoáþT5Èsâ:±´¯JÌ0p’fÊœh±ñlj´/z×Ö­èÖ[‰äé…žõ,¯¬Ç§çCEŽï Êîäe–¨3oe;ësP/fU45Ék0+ž£j¤*šœY›'ñî!P8¦:…} õŸ'§+)=ò¥* *h‰ƒ•èçÚiêê¿¥£ˆœ¸ÿ ´€;¿ª8OIÍ(Œ™Q™ÎfµÂå’”Ñ Å¶‚E{” gæ?eêdQx‘͸¬"&ꥻ=#{r» ¼"DÕõQв¹»jùì”#ת +1¸ûÏ„f«%“ŸÍVKä?ÂÓ¯p4i9l[a¬:Š’¡ z¶£ëp³™ì9£ƒI;QP‚ðú:ƒ­Ûå~¦Ö˜)±ŽkÕ"–âàA Â`mDgZ‚šN¶ý<9ÙÕ؉Á¤ñPk0KidkÕÀ Ü@4Š’†z-éhæ©ÎlÆ‘7n'5â‘KŒ[† ‚(¹õ òP<”<OœžÄ‹e®[ÿh^ž‚J˜nûØ ³$h®a¥€”IÆ{ÜMÖƒ·‹P»HÚf¸¨ÌI‡”EHAaQ¥/]ë!ÕIÕN瀶QŒƒhf—“9[¯â–²¹ï¿U…X€»¢“«¢D,Ê£6 êŽYz-j‹"• + (hóU{pªÜ©p +-ç¢<”ŒCÒl¼I âSˆj[hÆY„|6vŠ]1©™t“,µe–“Š¹Šc›BÓîøºˆÓÈœ6Äðt=U®³ÅŸ  ˆ r¯L°Ã 6J³‚Ö¹ É¡³\z˜´eßÃŒÅÄÌYζ·Z8å“TÔ$¦jáZÍCð¶új$¨Û“ÂhQQ¤¤ZsgYa‰¡‚zô„°1ÔÎqÝ™¿±3‹{¶ –iâ„ÉbV6î¡…÷‰Hسz¿%b•z³oTµÍ¦±I7n-®6:¯†™$öCxWµ0‹&7x6 ¼úÔk5ËNœgª­eƒêuŽ#R‹ùˆD 3N.1m`Y…à:YÞ‚¢.&Õ¥™Í1P-‘Ù¡õ †:WQ¡ÔÆI(¯<ÚjFÊ“šg‹éãƒwÏĆҋӓájàlr=×ÒqR:mr¿šj¶è³Èá¶W`—jÇ(ÍŸ+|*ꄪzæd¶ŠpË2!ö,§Sèû§°SáGð éáÈ”N(„³DFC6GóM¼¹§¢ƒhµ’d6ò˾|?uØô6g•Õ#À`º8^ãØ ±”-.Ge±ÒžŠlˬ¦]xø–{>½ª˜˜¬#äp­ðl§°oA@8Y¯fn2µœïéç窚¬±*Wqm¨Å9¡YµNãøÅpÕ¦‡°¢>²Z¦žRŒSu E23@[þÕžúÀÄ<Ùé]Ñ·pm¼Á"ÒIù:W¸R4Êüµ‡ÛI8)ësXSQmë)¸ƒáŸ~Rh²™—´«eRÂcN¥Ì´«„ÆE®ËË@…ÿ4ç"r×dÇ”¶ØØÿÃ9õ ‚*áò}”øÌ9f”€ÞlZ‡@8À£¾)Ûb'„’ÞÌ@:…] +ÓF \‡ê«õÐY9j8XÃD[ ñéWjF‡˜*úŠE‰„v²Ûò&?!pöʲZø7ÂIFnœ´3IL“Xs5‘âgß× ßDŒÂ©œ"6xFseµª˜•Azøջ̜{hÊuÕó½pÄ è²õPÅî)§¯K-&`æ™9Pk³¢Ñ_DxÄŸ¼‹Ë~ x'9s¶\綗[ì*ñj¶ð'oÑòÏfÝIdNÀ4D´ JÔ»™aŠƒ)U•8%¦tHóža/ ÉùVÌ$g=T3¦fiܦ&m$m…[N]Ù<•Òâ]XÈûñØ\Û$9JF§1N¤0ßÄJÅMéÏ‚àÄZ Ϙs„@„é"ý'3< |Qí**¸+ºJ¡(®°˜&ÈŒøÚyÞ8¬*{ŸÆV<ÚPœ…àò>r¨Ç332Znè·´tj.+iÒQËysœ4‚ïJ³’åì‹NÚOò\: Ÿ{0 €ãÁ±¥æ¦&Æð5„·šJc*]ÌFÀÀšñr5–Ð,°‘Lìt\~^øãlJ™Ô¤oâ3TÕ8p5šQ¤xÙÁLY‰ÂéÕ‹'`™\°u‹Ì$¨š›˜¤‡[Fx?‰É<ã)ªäCdË+¯1Y‘£¥e‘ ;äyrÁñ¶~Ÿô\õyÎT77f»#€ 7H|$˜CäÚª@œ›”ăl,Å2C¥ÏY­@µhHjë¡­Cf«5bGŽbeG~V¡ž6gP%O ÔxšW”î²YAa (bí”A”<™*•noÉI*¦91 õ“2@ è£ãÅt,²}´Êl”ˆyn‡oRB«rÂLˆ"˹äŒÀ2eÌöÔ&;·rgÎ"Uå\ásө퉫”,[úÉoЉƒºƒ\˜´õæ¡+óÕ×'1…l-×–DG ÏÌ!Zã`­")Œøÿ/äF‹0]ªgúÉd*G!÷ýF…ÀUcÕ9ún«’ÂJ“Á’¥s%¯^§BúHzrD‘¤¨ýÇð4Ç…Wç\ám³ÓIx>îæ©íÅãf¢¨®'Aç>æîÜàf¬p.³H„™ªÞ²ÇcÓí“#8DSÔ='^+|–<]ŽB<7°eÍ•Ïi‚Ȧ^:aÛŽŸ|ì©…¡Ò¹yè‹”^©ÇP(Y5¼”™âu)&Û¶0E‚7ÑeÒ£SS-JQj:* •t‰˜²)Ÿ‘mÖqiU.Á`¸kâèR8 +‰ì‡‰ŠX—’ÅÈDv¯ìtÒù¾AP³0sÀáHU‘4j¡$±ýŠH‰Eߤ‡JŠšóµûrš”?ˆŸ~Öø}ÊG çûЉÉT‡„vwòHêÛ+TPX ¡°h’¦Þœo®^P¦€)x;®XÚ fõD.•ƒÀbÉ(ȸԃp ¢FŸ”ˆž§iµÀ,’V-|Á i”E®ª(ÇÖÈ%/ÀÙø–‹QbKf‰”Lm7–°íMâÒ´(8`ÑÁ¨ÓhÙn‘Í ö}À‡PAäôUöùR3#qaâ›Í²Ñ"ašC$ŽRà=Cg&"¢‘$YdhV#†õª Füi…ªÚ뾡­n=í“Úè8ÅVÜ)nFit²…Ê+ê±QÑ€ÊÌ%O A]žLh )åWynbJÁkÈrÔcˆ¥Î°0`þw€·‚M–ÅméûšÌX8&Î&"SØq©¶³X™b Õ|TÊ\%¶RÂV­oqLÞ±xÅ5õÐM(,fˆÎ"€ƒ)ó¡WmÕMÈbh$:·€dɸaîjX$1‰„– 2¾|=›Ÿ#Rü¯b©ëCâÉèd“Ô¸¨‰%аÕà̈b—+H¡¬ºù%–žzh‘\XI õ0)%¿ªߥ²9éÀ›Ñ,ÜûÞŒ¤2à€g›RO¶`´-Wžj™0Pc%­6nç+,ŽáÞ[îg! ¤‹—³¶5<§ŒJ¡Û\-ËÌRë¦É:30FÍ%rÉwœu*þ“¿Y¾¦SNhS í";öfS·’ì—@uÆû%Bu¬ãHÇ-»ÍÒœžµ©ÒF@KßdtÚÏÍ÷ðP5»R§Œ–µ6å¤=ÌA}ù›á–ˆ&ñÆLiÒÄrŽDån³M[–žª-²W­.39uËQ(ßöâù6K;Ö›†¦"5ˆS67Á4¨áT +³"$ضÙ%6•:ðÖ«Èw¡t㢅·ºÌ•"”Z™c1"…û°™BcSxíF¶ø,çRc­M¡æqå².¦ +¡àg)áŠF¼m5hš YXX\s§6ueá+‘HjÜYq&ö a†Vx%?ovP)žˆÀXÍ.E_ñøàή±õ0;×o¬s•ÀÝlÕX¼¨ 8"‹TçøjÔ4¨SÒ(Mh…)"çÜ4œYC}f!Ò‘ôD "ET UïÔû˜­¢GÐŒßB7çªCÒÕ hq”@n=¨9,pH5)“•\EdÏœ,ìÓ‚¼ðf…N¤¥lî¨gR’˜yŒrvj]RbMXœ›b"TšÏzæp»ˆ·½³~Ù1«Ex[•=R¹ÒÌ#0Ì3Øò®"RS –yk]¯òIÃï*S7†"[ˆ"jòSTB¨²²g¡¬,×X€8¨òDÕÃLê.âëÅÚ™&VNMÝL®/v׎d´H¤bü/¨M£xÓnSU«ÈÒõä¦. £®”'îêk…«ÝÅ[ñn†k–*Ù¡68ˆ*z8Ê9ÑúdQzâdÙ¾ñç˜ÔjmRI#ͽnÅÌ«e•B¹wwá_ÔAní|ŒJƒ%…Ðmð-þžØz™Èåļ6¡³âŸ"…a¥;o‰ÓJåsç…-p6eKëãp¯VTAÜøÛ!<½vî‚\sótà„‚2*âúf ­ÍÁm‘¤ƒ~ž|Af<â4 K[ñHgÏuiLÖ¨s…k oÔBh ±ÝS^Â|Q×ê !|Njš¤½‹ÀÈÑÛ”ì>jn^bÑ}§C˜Ìö\EèÊÅB^AXdÖ—³ ž±'s‹Ï\=ƒç–[L*'I}à7'±cJ9ÏBe·Ô’[$wFY‰ lç +Ÿ2wâ}…À>h[ò +DK‚5Ë !ð¬6©Ôu쬠D4‰"àG­±x\Ô¨¼NʘӨ£Her­çÉìUAä<,*˜5ì#p1ª+>ò“žžµçY‚!ñJxK¡”º«lÖÛÜ¢ +¨°¡úäD)ÂR…E1Åt’ªEîE6K¨¦€cÌÔlmÕ}'eKhcíLiXO°hc©(„@MNjXÆò›!èÙ=[DUWã3&ek-ïA%XliRb« ‚ok,C—¸«s(u—xÞv²aA푺s…kjH2›&¢ÌÒ̤#/qVb° oZ ëYƒxÁjcŸT”‚wYr{‡`¤‡ ò§º_r«µâª…oQXi#`spG+ÎK»Àªáh +•'4ó²c{ +lDš=3ÙÖ…¥Ñ¢ö<;ök`ÞÊérú½Ë"D)(ÓÆÖ +«è2åVJ‹[±N›]3òÀl !(¹«%{KˆšQ†@µX`Z]á ™êóßÙíZÝI„ϵÈ6„õÌ«ù–3’äm™ÙþŸCW]);Í%ni™3Ul,ûzh\Ã;MÿAx«ŸEæ&æâ-PU³VrîB ½;±SÒƒKWΗ^”r0¤iOÅw=Xœ"f;¬v$5$#ÿ&@3¬†¹‡õ¤L@×,™\8¿gq5Õ[hA:(| +2÷›,ôkeye«Š¼MâA1äŒ9ÈÍN,•áj"›®ÊŽkìÊÑαXˆÉUñdN¢ÎÞÊäDÞz„ O–ˆ5Bœžc¥²‘Lž\LÃ" “zJ*ž× +ÏN³¤(È÷ÜÚ‹%5j%¾‚•ž‚"IMØXJ<©w4Î-ïS\×"ýXc=kèM–¨êBù Nš„KÀ¤[øs:ÎîÉX&K¡OœªÁ,ÎÒÌ’&çP8uâ“J€I[Òué2M,+§‡lp~ˆµ§¦"=¨hnžêÄ>%éa¶¦¤…«¨ug¢VW*ssDŽ!–ZùƒÔ«“X„Ë©lÌ¥|hÁ%r$YHíœÌ{GÂg®ÅňEÀ„Í&> øUM «-.áS)[ñ<›1;2;2x¨Æd¬à$á£èÎ×(H¬&6æd1Ãв2\åFdýV)]Ž$¬òbŒÃæ l*¨”¾¤€5¸"KÐ9–„ Ñ2wð/ÚVÓf¢Þ|‚@@£‡°ø‡õk²†—t‡VÚ‘$¥¤e¬¨§-Y±S;™0T³á—É +G.Ž€@o‘󉉆M}hU}®õð’V()d3ÂW¥´y!‹…n„Ð\lªÐòëÄÔX$EȵH¯-Ü­/àŽUŸÅŒ”?foCh®¿F˜d6QUG¥?² f™ƒÔQ\^ßcÐ"/Y±gG¦úI·<çéÙš³z‡‰ŠeWSw +ÞÔÁFfd`"'öˆÝÃÕ°ãI™·0 €iËd6ÑÒÂüSŸ(°ÙÓÚþðòEÕ/ñ>L¼êôæÍòf¨ÇÝ×7Fñå½ã{£@ßåéöÊq½EOvf8DN[HÉÍ Úz:o·Eñ èÎ(LãÀ +ñª.ºUÌÃðÊèxkågäîU7‰u­ná®xŇ +J·¬IñU¬äeäŠÄ¶µƒ×j K˜œ5FB¯Ôƒ6æ­$@4 Oæ|kðÔJ!~Ç6 ´á&6ÿ¡ÈÉbÝ74«Üì£ö@‘o¯Õ*2%:¬ðº%ɱÏQewºƒÉ«­$Î$%ädI;˜4l&GÊågä‘rChˆ©ÝSmkôZ½wâêÃ;Ä Ž¡ ÌÖR3zÀ5'RaÄædqI{¦«‡èضƒ@I ôvß…"r×ÑOì/ÞMß­½Ô>C©ã¢­8euo¨ç5m^$¼„×½á}ÖHv ŠBCŒƒÖh-Ð6"ýƨmñZ @—ˆ¼Œn±<àÂÈÀªV^FA±½L§ÐJ_µ&´,ù¡&IH2je^T–¤ôŽjº7Xâ${möGkÑiÁ3ö-¼¨êZ`v m1:‘Z¹Ý02Ç\ü¼ZeÅz•uÖëUîßV “ט®åʵE^q—õDÚÍKý¬ƒº‚:ô$[º.ÉMf›Vá¾­s[¢h„²XÏ9ùÍ2£ÆØ-s1 +,¶x¹9³/T“ÅÝÐr¿HÙj™.Kén*L<)P"% h4<£ã80X³-1Þ”ÍgXUX]€Îæ&À׺»½­q£T‡-ÜGÖG±°!“¨W‰.I-Y*aG” ëÛ…odS‚ ̈Le2i›c¸öí¹ánåí¾ðƒ†Š!­ ©r é}´1†¸Ü‹øÑm–s°ÊÝr”1ï1ª=,¿«Òš,²Q¶Â7{@¢ñöì—}{k¸ Ç[v´¿‡œ`Ë66ìı{-žÌÎbÏs´×nÌR„ÈS(DaKãbbBìûl[ohf9cžy¼ +\“I×… iìCtÜÀõÎø-—XÓIã(õt«¹]úŽÔŒ÷KÓe²JQöµ¦”Ž­ ¹å£8«PÕþ†#Þ7½ÇqÖ×ÀÙC¼}ïAÛ-¾_pÐF)Þ(»‘T7‰Žþ*ïoÙX«ú˜:ªö:Ó¥£Û‘9 o³3’…©d«D4·hW¼H†zQ¯j~Òb܈{¦¶ÌSÛÚ÷)ä$ÈÖ´‰AhÂJ›¥Ì}Hbç ›(¥KìÈé6a[‡í¢ÝwHn÷÷f‰Ûþq}fÈŽ†ŒkËå6kÜcbH&#zÚG{CB’ô•ûð>\¡ÍjnyÁ؇ tÈj‡|yßÙ“j·Æ ²¶5î7€#ýféNÅÎÊ2Þn2ûó×6̃}\i&0ë6*í µz—°tå›ÀƒVÙ&%UÖ»iÖ6ª´Q Xz!/¤.$‚Ý—à,R±[̺H"îwx˜çR„¶kAð‚mØoÖíþnÍ‘«<›V´år{8â{ùì–)˜uCÄN†µü†´:¤ê!*÷á}¸B›Õ,rw yþèp#û5ßY½óÜ»Þ0UËà*Ÿ/¶€a±]Bû»½%óXïBía±eGª/šf]ÑܤÙ2“ÙòQø¡U{[³ý¡½¶ÈEY{i•fb»¸$ý5TJ(èëvÄÙɬšÉA¬³Ôzcºc—€ØÁú++§ÌVõÂI5n4rPà†Lj¯úöüD³hkÙ¾]øuq #ëÊÍ5EMQÒvõž7\ IøÇøöˆáû0ÕWÈÄi03Rkæ€êŽLê€á—iÏ©^»k]–´æ&‹ŒPjM†ãT®3Œå ýÀ˜Õs5ÜClîE}èØKÜÈb=–£^¯36¶;– íJU£Wuƒ·>2·k÷˜ƒ==¾©4mw$Eº}íìZì¹^>ï7¨*ëË­¼æ¸í½e,tð}¾„õv¡oŸ«áYËè8ÎúŒò“Zc=½ÝóÅ0[Èh£’¦Mb¢pÙmX†Îÿ1G¬p˜ ¾7ò­=‚”G«‡ÛÇ ‡|s/“qä!ïÞ2úÁÐÑÙ¬Gô?Ú*û#ÄrA¦½æ^ôìpÒ’¶Èµ¤îò¾°‘eˆ ×—]£Tõ*v+Ù…Fe]” ßÚ³Š‡Éœô° žÉþÙRÃ×WŠÆ|sC„d¢}Ö#`ƒtì;™©N{^ì+D„ª÷æüH‘Ù±~fô`mx§žQû8­Å}ךJtÖML˜«…Åh\M?“Ôn0 ¶AÛod SE#-=kÅ‚[N•ÆåÜ/#l‚ƒº˜˜u$Ñ0æèžø¤ QÉw8â}³Û"á>|m"™Æ1O£©ý6¶¢»ˆ˜ºÙØä>–„áœãƒÃWs§Ô«f#ßÔ‹¸ÞðÍÕmŠìvÐܧqwðÊ*¥EØ·8=ZZÌR±½²ˆošì0ïb¡f[¶5¥/[[©®,'Ô35QÝw·Q +Üì+™³¹ƒusΦ[si Þêöüjáî=æ73Y‹uƒ#hŒÌ!âG+´]ÍMlÈ"šjp6 MÛÆ6yDZ–«Ü…n‚¼¶a›U^PðTÆD5¢ÀotIWLß< Ûbx–ì;x†§Ôð<Û~›­» ïáþrŠ=\eȆ¼j¸?ön¦Ñ¶ÛîÑͪ.NüáÁ:>‚Gçõ7OZž$´Ëê±N±ùâ;PF8ô¼ÿêØ}ù§„X“9.šjízß'5G¼¼ÝkÒ·”2ŠÅŒièþâ´·X¹¤æN™Nâ>p¢Wç,Ó¹hÆBrvDŠZ¸ÉýÄ¿s2¾íЙµJRwTG»B¸;Ô±n"^^JhWd· ‡' åÏ\.¨‹NÆíÀ×*-£“|i© CÂÃdb¤4^g;Ø"~¿Ì6îuœ¬TÇ?©´¸XîGžìe=[GŸ §¯¤b,sÔj=x.ˆË*Ï&Æ£'²Ï•+˲d Ÿ«¼-iü7(Õì‘Ùq& ~-Ù1$˜¤=‹‰vm)Å÷¥"‹,é=9Z5¤¶ËHɾ8 ÛõAݶFt@I9ß5Þï“þéϨUÌr+O<×þÍí"= ä,>œr«÷Š,“o.ÂÔÓÉr °to&ÝSUg®Å†|”3¶(NKvRÔ¤@³,H=ƒ®qCr k¸\Æ.pß¼“Ý:aÔ§çi+2·X»OôÚ‹áx@Y#ÜìNEìî· 5^ÒÑLöM{Œ 528îÂÇ›a´m†{l¯1RY†Å¦¤·<öa±‰™5©“¥Ü1ÀîN¤”´9ËJVßáZž!j6)E)Éë‚’«×(îÇh}£íFÇ‹ˆ±-}¯qÜöBÛ4=&6h»W¹ÞnHÃò`÷÷ù†)ì”)¬°®=–j¸¨£©ì›÷ã˜ÅëG ó!vµŸñ†n¡{¦@ÛÐupîZ®C&`T ”‘# ïª~I¹sŒÜi5»°ÆWòÀø#,‹@,6Gd–»ÿ–¨X6uð>B±£Ù ØQw7ͤïå››c|s°Ë†ûq³ywÚà +u¶©x®Èh&û¦=FЙ/B%ÇŒaÄB†üfŸßµjÝ9³YìZ…U©= +•4šn¼ÏF;r;äÝ}!ë³ÅÅV.ÜfQþzÅÞ&¦«#\+Ãâ6Ñ‘!s±É®B@§TÆàkˆ^ÙÚ5ne=кêV\¦V¾§NúŠ)Þ€—¢‰!*YiÄÉ Û9ïî Ûòõ̆¯lƒsV¨›à´çXuʶpÛ)>f­¼·ÙQ›ïöoàÉ +’æÎp+yô¹iÝÝ=»­¸iv±¢V7ÄêªU‚R$˜W‹du§¯?¿7”hC£·êÙŠ¤RqŽv‘ô½Åp%4m9·V¢|K~çªEkngµéÆT½o ìÙ/£µšóþ…Ä"„‘jº-"`(¨Ÿ¨±s*b2ß¾Ú¹©Gkw"Íü¥ŽÝgRžÂ‰ÿqC †Äq9òçY5_Œ$ŒNâ&èbG³‰ä2‹a%´häDeZ1d!ê)%zü"î¢30thl¦ˆW°°[2²±g,¼ÑªúwnжÄ}TŠ-ñÒ»í.Ë®qò†á.üz½n÷Ô­QÑìVk¬°»] Y¡5ËVhkúɆ†å"¹>—|`/Ú—F'Z¼Ñ©[é!M ègï8›œDÑNùÌìõb Ü•ÇÏf*Á"ÉjШZÀ“ÚÚõŽ¨órQalœ´Š¢˜wæœÆœË­Ï«ˆˆÄ•Ü$|Âi ãFigÑSe‹AYP°6íIÝ8x¿/¸[ ëp“²AˆȡåÌB'*—„ÃÁ(ç±òÐX—º$õ­Öá^Qƒ²kµõÃù”&­A!%˨×Ýì[5¬8Æâ]6$Õy"%0K 8‰[¶“eÄŸ‘ËB +<Å`•q&-´ƒ•|,A ”cÝîÜòA¼^õ±t`VIkqÑ]Ä›zΊmq­—× ƒ³ÖÜ@ê~Ô!GçňGíãgCη—MyêûnXõ€…¯$“-]6Àž½2ØVÃý7ܬß|ü‘Ü¿Ã>â +C2ä5CòÞ·Æ»f³Å6k’VÃÁ¹9:a‡Çñ* èÃ^YnàkÁ:×jVHV—´ âAÛx»JQʿωµŽ£¥¤å<±`e¢œTØÅøë9iÙ]1¡aìW²Â½’®‹ÖŠ8w'£ÜüŠg+̽E 6Î!£P13êUo¹ÕŠÇø (ú×d9¹ k|W“I=ß$ŠºÓ¬§¶ãKóÌc{­ø”»²°:‡\ã‰^¹!ÙáÕ|Rm0¿_Ä ¾<’’Ú•]b±?‹ï<äÒ]'á]¸Æ°“' +Ó#+“\Œ)à; sì)S1aÏÓS©CX"Au£Zá°VEY§ÁCžF„%¾&ö[U¦[n, [ÎÞÒ2“¾ÏBUì¹2‘LäZáÀ|œ.ÿ¤ý¥žv¦ra +ÌB—¢‡íTç“Ë ‘VœEÁcnv(âÖh <½9ÄÙÕy†°|yÎ[ a`øWh¶ký– ²šô}‡îv­Š†„1$¡½Y,Ê +u6“!ž‡+²É=Ó hƒMÛ +û6¶1É~´Gö©m3 P'õÉQ;ÅÀ‡?݉QìSê%¨‹yl©è\߸¥¸inéx·gÈç÷IèÉrö'¾úZ%÷â(Uø¦U&»JZjAðÐîÊ¥I•ëÕ¬ƒ_Î:¨…œŠWs¦6â¬Uf;àû<†{×oÓ‹E ^9Üf&d´Zo ÇŒ6c¾ÇgXô– +,=a¶VÔ«P>UÕçBÖ šœEÊYÍ«$L[^F—Òë)Í| |k­{ÕÙ5Gw(T»ql=à{=Û^ÌÉ2xåxl«yØüVôió[Sòà7»c·g¼÷í=Œà[´ÐòÕ®F‹Ù®{q­B ž³êD¥ Ô(¶ªåôPÖEdÐÔ‚Nd5¼Ž1ð]Î*ácäÄ<V¬”¹õ¼óù}ûM/FŸƒW¢ˆª7Ùಞ[«Îë™Û SÑû”ȇ»aMˆ»=#>ŒÃ~!kãªÈý–˱¥îJ.‹V[ÔË0½•ûG{¢Üz0õù‰Û7>ä¨_uc¼3ö·Iéè‚]-¿D?^Üòq¼c1ë…‚F¥CzÞÒnϘ÷/Jß<,ÆÜkµžË¨³¸É¸±Ü-€LA$y +T˜$½ûÎW~‘ƒ0©S}æ"šQîxa ÒØéÐîYÃz¸æó%c]«jÂ÷œâÝ€…M Ø8«œ’¹’Çl{x¥oaö+ÃÊ™nøN›U®Z.V‚š]Ù,^wóVIv¡»30ö3½Äe…ø{ªÛ©\Ž6ÓÞ‡Ÿ%2 É+Ò­ö~¸ÇòF³¶±­°¾BFßÉý‹Úmïi‚„(þŒÂšª]bùæ2öLZcfÄÑ¥®šNN³©(]èeÂËÅr¤×?QI ÖýÝ^Ú¶ )YÝ“TúFµkõ­cŒk0=£C ›{ðEƒžô€éŽ.ª¶•õª”ÆØ«ÞÏám‘–ToÎñÕ÷Nóh7tâÝ,¬ã£å&YÕ„óM|豆#ß6 ‘ž?ÃÕ¸O•ëð†•À¬Z- q qþÍaíèœDs¢8ÒïY;õFD³hb´“&ºBՋʆ{Lö´L˜¯‘ŽÍ\» +”™“sÅ"Ï>ä§CλMoy:r–Ìód÷íá.¥O0lt¥j£k§B°¨äÙn×ÚrMã¦+z3B¯ ¶ÈÕzžªHQñvÍóÕmèÌ I1/HÃ+ã²ñãý˜w;ñvFŽ°$—”?îŒ N㌜/©Œa‰.ud5|x\ÁHÍx­%ï)´D~CÑv¥†k:"€oîß}CTå>®µaqCf¸sÙ솼 ÙÅ€±ì¡¢‘è…zOÐrDÔ•ÅfÉ=Fè˜Újè€N”ª¾=9c±8}L +”í”ņ/–#Œì%S@/iœÛm[+üªv]k  @ZÈÞ®V_Xs=ßuÞÛ}£Æ¬ŒÄQw5Y”³Z”%D˜lÏêÃßg§žYÉDZžõB@uØí ¶ˆ-Þw÷J^Óúõžåk&÷Ñ;‡£Û;•‘}~%1¶û Â3¹ÎÄ1øE¬Íù-í¹ÂÕp?Ûe·xy''ßaá ,÷ ÍÌ$6–ÆÒÝŠ×[«íÑ5 æ ;EgŽ.&6ZF +fÀÕ‡Ô§%Ÿ”./×ßmf}Ÿµ Ù”äÙ¾ëózìn°‘•~=ɸ[ÔYÏC<‹ƒ„îPe¾·òNÙò–XôÑ€mŒøˆíö’ù+°g­† ;¤‚1½lˆËƶb^Ƥ¶œnÌG ôIëx¿®G0®DàÆu‚Ý¥3²Ý@O2¸‚ΨXM†vÑôÔ}QËp‚œl„ÓÃoTGì!À<éT}BÏ"ˆÆäÈ ¸`³ýh29‹=úÊ«=_™+=q% Žõö©9r¢cc¯»ÒTÅÄpŒüÑ„”y²(Ê ¯Ýômom¸¡ a’ÍvÒ+çé!+†+—Yb j‰?ª˜XjýÌ1W{:ïg[8*j–ªb6]¹lo?ÀËÓù½%–«mº”kF¼‰ÌåW{:·÷F´Ôòàfg™ A€a)ïÌ9.°Z¡´·ÑEŒ’æ;ûjÛåÕo£<}Ò6ÒÛi¦3³12ѧPú{—ŸW"võD6;&3ñ–Û8¹Þ¨@ÎÉ€'`šõ˜%U˜WUá–l)9$ª¢1–M¡·PÝûM;ˆPðÞ‹je¥É‰*n±b…÷Âa¤ª¶Øƒj0Q®õ¦× ,ÕQXÄл×JœÔƒ³Ðë‘`5}øÅ›³Åƒi¨;ŒÊÅö3^ ܬøtÍ-Âýd!ÎuB±2Tœàd†o¢u^Ž®‹ëãôAÐÐ? 90ƒÀÀª1rO:œuÄÒ%F‰ª JÓ$I#€Ï‚¦|Þï@—}æÁÉ¡ËDˆxÚzžÅ"“8¡z#YW,´@"Ô˜‘Æ d‹æ_¤H‚Æ$ºéöaê$K©ñ'ŸCG‹äúbUÌoA¦°<>̦’ ¡fÔ‘˜¤–M8†.‚‹Ë³!|ž£zßX(Â×QúºpêX£Ð„Ý<€±C´p8 X+2Rxf™/ÅÞâ á¦J±ú8•žGÃ(jÀyÄiÖëjÔ”I}X€n£)ðbPF o´qWnö–ÍRS‘¹©€“ñ)ÊVöQ7ê›tï8ö½kt]>M¯Så€|§4M©LzPÃN"I  Ã@šq Z……‚Ž'}D‡ŠJL-ÈÁ”<½äYQà0õ¹SEK¨’χF¡”Épf t +§Àr“Žç‡vÑbÒoäðò®¨éTxs¤&’ÍrŽâÛìö­YxŽ ‹X¾6)™îôÆ9èåÍXÜ’‡ë·¯®° š9òíϨúø\”צvQm;ÇÛZ‹÷#_ɼ.øçxðƒnuŠl§QR\3Ssž&×PUH_ÆÄÑ“õ2òè‡2ÿ ‹è0 ± 3…rõˆÞJxïvÀÚj˜Ó2ÍVl–*6F ›ÀtˆL‚bøL}êºÌäÂÙhÒQ`½*^F½d' âwhãÆXã}F] f1^ñáRÐ%ØÆ&È"QE”ͼð-Cgð¼$2ŒÄŒäíŽk,4I’|t…m2 '‚Ñ +“9j`»Ø?Œ‡Oj‚ö9ž]¬L…ŽúÚ¤5+¥> îðÊòÐ’ÙWBÆ‘¬¸œÇ°OÌ?Œx°Jú–ö‰ [±À>gÅÀ\A"ž1û¶ ý$î„S?9Š6â))VRï8B‚69‹~÷¸#(ù­\$î‡ÞÓ”Õ ¹Åƒ±—Õx†È[DžÃK6HÌEæÁj%¬P,dØÁ}/YÇéˆlÜ“Ø_k>PÎrWa˜¯Å¢#ÛÀ{Üa !RæðàÉv³³D— 5?ºÞ@yõôæguRŸæ°V˜õ ZH/"¬9,Gz-òas¶hhÂË?Hvx'BÐP~Gb;~ÅMœëÎÀÊىШÔ^€aŽºPvŽ;ôO0QGܤ5ˆŒ@…&$EÏ£E9h^È&PxÌŒg£ã,ÊŠ«‡I‡$oH HýûƒƒØ&·…mš€™BæT>[ÄÖƒ›µ%œü´±•«7×w{|ÛÙ½Ðò—šüRË_ZVZþ§-þpssq}ùæÀ¹µÁ¢«öùh˨xdÀª‚І6¶‰­5pŽjðó%<ãíAJ"týìƒ÷ýÜ´¯¬ ÷`¨Þ°ªx­8Ž¡Ò°} N…Ñ©-,3A¨4·Â¤:€¡—RÒN_Jc”¯ƒöãÈuˆ ˜QT$`ŠÖAäËLv¬s…gd5üBJ +ºÂid‰*&&ÖuB’Ç4ƘñFŸ,å¢>2Îl“€Ò^¤äcT` e`Ž\ÛFzN„#„ãqÉÀ€5æè'íV»¤Þ$Á¥[bÖòuF©LÅh +6;äGˆ¸Bc,ºž¢D‡õœ³¹0¸´‰ÉÎòÊóýX Ã;ïÄ£ø‘º Íf’ÃiÄÇ-²}L|[jÃôŒØ´šh€˜NoÙ†ÿ‡ŠhïtîÂ^´†­_„ß±:å딩>åŸPZ{ã-8Lv€Ør¢ ;×|JEÎ’~~_Bö|Cϼ#1I ð™.\%#RcsX”«'}ÖBÉ|¾±EÂËúRÕ¬;ŸÛøÓŒLŒ!7q¨|ÊN_ø +ÜÖÈ+Î[ìâҢƒ ÏTá*râ¾nŽ}kê©gš$6O8çÖ](œôS¨7~\ñ­sªÖßÄcBw>§ºØðÒ9äMÄT`rhq*Nâ°Ç¿u,~YG»r‹ª:´ž“ØíädÝiàÄs9 A‘ƒmN§>çÄÙŽÒ¹hž6ñ¸’{‘g‘ Þ¸ÓÝ;ãG×Á‰#U^ÞºL£Á—)TÎAq :^;×.¹#Þž¯HâTkÑõw”dr°{t ‹‡8qL êZ9¾ ¹ÑŠg¼)6»<5C£¹›ïiö +¦QzS!~D¯|Å’v,ÿ)h¦ S0ýŒƒ˜qð­)æó;à÷ÇÂqp@œÃ€©¢ÞD܉9œÄ…ô/»=É4‘Ê1d¦‘ò{ áê³›‘¥öãì®G£ä%Ãc±+Ó©Þ˜¤ô`‹SËUÏV;D<ª¤òœ—£Q»>î.`p¸J#qÚ§“A¾œŒTWsûòcˆ-{÷FŽ ~\D€â%­Í'Ìõ¼ß–a¯= +Ø­²á'î¬4'ÎèÐ)ï÷px ‡V•qX½í f +q{ÙŠ–Ó›+Á 0 +ˆç/K—1wgLð¨™ôêÊ19ÔœâxÚEuCùð–Ík¡·kMM«£ó¼l Að³ª@ÿÚW$RŠNœÃ‚Bu²bK\‡™˜¸ŽŸëan ]à5¤c ]›ÛVlEĺ‡ãNt\ U¤&·GéaretM±§q ixþ,„EÚóFüGwq$4—….ƒ`¤&qjæü{•‘˜°ò ΨPP¼­Ó‘žá~2Osqb¸O*’ŠèáÄpª-rž¦FuþÜ©’ËQ5°EÛy £e¡¤Oó½èCFlP±ïë»/ˆEuj`=ÓAëôÒ+ïõh§È:‚ë²[ ™ßÑ3MûT÷ñ÷Áäß +\‰®h—LâiÒÐk»p†hŠÍ+@‰U:åwÙ´Þ(W¢SÙÖ‘sUD×2—áªäÈÏu X÷*ÊÑùf œåÄãHœúØú +ÉH³ sË[=h4ÌÒª Þ'KODOÜ[¡É:].¿sâ¦?“¹bbÚëuÀ¶†@_ùÂ!wÉtbíˆ}îó/ë°—(…nÀò#z­•tøJèõßN\“\ +£cZLè`Éé% qXÓFäCf†’K—e¥á%ÄSÖÛ¯Ù¨AÌ fÇ@ã§9üŽéhï°{[ ¬èE5ÍÔô sÏÔ¡'Ïg %¼B~‰5%c ÒZ§÷tÐü.ÚÁ“IJ‡j¿·¡Å¨Ç°Ä"c® ÂOЫ?ÂF/8·NŒÈu#×î¬ïs):3–nßÝ 5¨O~}$…12t#Bù4¢—”<-Î’5'À ZÌ;üÝÒµ‘¾gÓ“M +6]úµ•U®ÉÓÑe3t+Õ¯d|ð¤[mëjäàýŒ8ô’¬‰;N#ü¼¥] –ÙiA&,ÜÜ~tsÑáíô9M|¯÷qÀqæ0DâìòRœÄó¶J/3z‘?©?à€wÇ•¢%îí‚®e²/8´Yä¶= tlÀv=!‰ + 6#ÖII;åéÃ,è12/D1I¨óÐ:Ðk`WÖ ýàÀxq^Ð.'“È kó½°¦°ü2[´¶ì­XGx~I|? ,.ÓR<ʉ‹Þ¾ŒZ}ÆŒƒ¹[¾tÎꄹkU{“ßÒüq…h蜵¬!¸+*ÈGkù©M¿ÖW’yàI'ÑT§ÁÁæ^%ôæ¶[28¸÷Ë’NS˜ûÖdNçÒb1 ®ˆ¶ŒÄ£êX·+êðÛ5÷êÈ•@°pΘÑÓ¶[¬NB8È|úklÎ)¼tÓÒ¿µ£§#OÅÀÇç²ó A¿'áµåš­bX—Ü5ÐW¦Pq¯MLg?\G}ÓËÒóbÌl,ЭëÇ_ŒÁØŽA7ëìè²NOṫ;I+ÃT»¢$š¤¡>1u}ŒCxô3çÄÖú’Wü>†«]Û§=QÓTjå`YÂéݽl4*ÍÛˆå¯H–Ý Z¹Ú”°ºç©{PŽ\ÝoæÄ­R¾ ẓçI’2¶˜b‡CL£‚y:ßZ’„¬Ñ)‚w©ÐÓbÌC4ÉòÛ/îÁô'Z2lÕRYf(TˆÅn$Ò0‘4bNzjJß6ñ óÞázh¼¸™?¦Wâm{lV¸ïÐØ)Ï ©…Ògø?í\S-÷ÚL}éIX_âÐÄ×tÒïME`tëzF„ÅaDêÓ÷‘]}êòÔ#ïAÔåì^uÿ¦×ðAœ™øÝô°Ÿâ¡¶Tìp˜ëý¿ýà‹å„ie‘§ÑisÛní ŒhVµ £x?­BŽô5S¨2Y&‰‡+Š>nU®1o{b:âT‡{Õk{o©F$ùøm¼NŸé5ÞEÇü2‚„X3Nÿ¡á"!ÄÞ¢Ö³ˆ-‘x±îP*(zÝ’3[$#?Y‰ùþÃCÕÍÔò¥§ÓRó~kyW µ…Ž™Y‹¨w«\´ž2B‡°z:v’Sök°rÍV“ÚtÛá½ù#ÖíT¸R¹ÜìN«£ÓõpUMÍoKÕ€™c†{ßšð´Ø MºéÑ‘/÷8†=¼¤@§9tó‡âF·ð;ˆÛíå$.Å^¢38 =äžèŽŸÛnésán[ õAq2äÕ4UÃòvç#ëp¾Í"&hý  Xþψã“Ëe/ÏI<-6˜ŸFì2VÕKuë<}£êxXWJX"f,6®F1zèuâiB2`ª´'~AW¾XÖ|’XãòL„F=@I9†VðäÇÊ"SÔp߮Ÿ‡‘+ƒX•Âv츱…Ê¡=,ϽG¶jßVUÄëØVddHyU-å©d()n ;ùeß]?±jÌèbÁ:yÂæ4 õrÿT¼´,âó:‚/í|‚çÂ;wÞìîÊõ‘Ö‡FÆQ7‡¾”¹ ÷µÕïÌÂd„Ú!Í­P&GÚB/ÇæÐ%öð‚RÑxúŽÆÕ¹ß}´[¤¬ è'ÈÜ=0!ç9€”BhÍHKfŽƒ˜cë!Ò;RCHŒ¦go“ÃNˆ(¡uŒkì¨4âÊ!¢cŒ?LaOκ¹Àlµ*ª¥ ÈÈáe,ÈBûŽïy â+ßø|_~¢ Ë͈]ìËO(ƒÅuóͪő6ÝæûŸø ëñ·‚§eDL«;n­ÎEņXsô¡;Š´q³rYMô6x‚Z‡¿Á~nè#+âi _—áé‰4Ãÿ({xt®ºèg~|*8ƒÄ#µ¬Ã².ÃSbÍ †ƒF¾5¼ÉË4ÖÒ!²öG¤¹h‚Ë¢"‹«±zê®s)Fbt&ºXc]hæ=BOF¤ß̈S“gtZu~Àq¬L3E,žaq«;ª$P ùjaœE÷D-~[×Ì@ДÊÔÑì˜ß# inæÃoNm^QkôÚŵ•º6y'û†ˆrúNP’îh?WB×`ÈѾ„CðÐñ6ç±D§Öpq&;‰ÎzoŸùå©t]8œ–h½á÷vô•j Ê8@<ŽËÛ ì!™ô©oÏ…Æ{xŽŠ%\ÇnpÉ!/Öb5#ÆÀtK3K+—aGBˆ²´·ˆ)~ žÊom½Â[æË„¹lÐÕ OþBD9Ø`öá¡Oç@yÎäȆC£‚('ßÏA\¢Ìãg‘}žû;ø¶’Ip sVÔknGѼcê…^ŠWÔ&‚aŒt‰èypDíÑKPÀ*oéݲ½+ú¶NºcF‚ŠC™¶fq‰9ö=Š‘eæ;²‚#ã“Ý ÿ$þÂín +ÔÎüè26ìµµäþý÷…•B]휨؆sµó2k¬ìýûýb-¦§ˆØ#ýtÏDszÚJØ¢‚itXlºY£O»¤ßø_âkÕÄIŠ8Är»wzfnRÈáÈàoA\¸j­êØ}Ӌݶ.D‡>®0†<Æþsæ|Ã~É¢ÁÓ¹Â/çÀE8#gýûÃ÷ßV5NE‰@¡ÕÑ÷ò•'ÈóKyÖøD™n ¹êÕ/ýý–·èKáÕ:¥‹1}Àu–oî@òèDPêB×+Ãb–ŠcZxw]ȯlöä-f¥XVŸS檕Q1 ®†Êksô•UJNTL½"Œñ%iÓ$vmƒÇhÇ° F\¼{˜æGä™àRï°ÿ¾E—uÓa—?³ÙóÏç?yø=wÌÿˆ Q·v³¦åy.ïI4ïÆQ@ôÆœ¿¥–vÒ–ÖÃ-C#ÊàD_Ôâ*!0½õ·8/&ãX¿4?,ÓÒ)2 ~ëQá>cû² NÆéÅ3PŒ~.ŒÆÀB›ZÇý»¯CìáÀÆkÕ#JlªÔjóiHÓÞ^6Ïéub£%¹ð¤~‰CFÕeÊàA°âyÅdp'²ÐÆœ¾9Rý¬‡v$cÌ™i^ÈÇ·\†è qæ®M²v©ñª)»ÊúÐHYá$3H†¨Ö5׬Yèªñ (Ôm?G -²@l¼œê'ƒóù.ûÑ"ji=L(2¢«éØÎcìu¹gîBÈ¥v„ðˆì{‰g‹Q©ØŠ­y( +ÓŽ0¥LƒÀaµúÄ4¹8}{§ì8Ðç¤,]°¥£ÍRkj¿§«yâÕw–¦>û¶M`]AÎ0"Œ¨_3# ×3Wä@†J“<åÒêñä83Û$ùê fXdˆT¨r/8ñ±Œ¯!7–¥¸´²y†N5”&a[ЛM™q›Ù›ñ¼¨*§Õ/šO¶¿szfFŒmî«6¢’rÞ–p_bS‚;ÕÞ™’$©÷ø6!½¿EOmñ{/ÇùþÀçû"«áÀV8qlQííœZ¸Iz¥šjD¯A„ÊfÄ‘Zl_eÈ×è +yC›4¡¹ÈiáÏÂA5gû¬»#ÎP[*pëZ£ž&M œX” •ã)/µrda:5zØ|RÖ½ý¿ô6<1„ó-\\K²ãkå.ÉéuVU«ð²Š[CM, œ‰‰DGi›Ñ}ä]Š¹iÙÕÍx%Ì•®…vÐ*óù(ûåoĉdïX ©ô[h¹X™‰æÎõ]#º›Déy4«ãµDž¢sÝ›èî4¢·Üà:xLÞ×!‡Zy_Ÿ8™õ«¨ aûgÓ³jÆxƒŒ¸¦œ·>Öz(ƈmhéêq© óâÍoÑKx€ …¯ù±Díql[ò&×zUJ‘ÿï mšªû ]”0ëÞ¿TrÚù4Ö+¦5ÒãâP‹±„Q ›Ç‹ ^ý,ç~pÇ;$ÎRh˜+gÑëºØà.È)Ò«LþâE.ãÖ"ÛËla–”èæÙôYžPë,Èb ³8"Çàþ} ëÐW¥5 ïüˆ^Û¤ª×é±íK/zÄZáãkÄÙÂÙß*û®êMªBæΟTéP!ãÄ“9N¡Ó‡»0\³d∠,wL‰#÷Yìœ]H×ãÿ}%z-±b&®4‘’\Œžxô¥¿ßζó€O~ÜÂÅ,Ÿ¯Å§,£_BÃ’ à §ôë:Ê2Ð]A²ìp©j/—‡®ÎÚЉØkP€P,«JÝÕ=V ¿$z”ô9'11 ¨ÁQ”yÞñÞ±E~¸Å&ð²¬#q;ÄVÄÔÍ#ÁÜu+0Yò±&ø ½í÷T¤/=,VwÒ#hÂ44+G8Fa¢–UêÙL.÷Žáš^5"Xn^þÍ?KúŠ¸ÑQÒbtFþŒ* ̃G¯©U—e†û£f“Qq¢å:o³”hïZy•{ ß­ÙSNn=«õ¡Ý`ª!œo¾Ãzñèû@ÄöA§­dg´I‰–ç ƒºyKýr%ʬSãÉ´{’Eô‡‹Ä$5Ü¡@‚ë°Ýˆ¼F‹œÒ™ºöÀV8n™´NÇÅvQ|Ï‹ä”7ß‚îÿº?y."–a†Ñé¹…[¡Ê€µŸ³ ¿[ ùXæçã c㥠È>CCÔÃ}1Í°h<%%¦°ýí95­í›l ±W½{±*',Í›O¾)oðb.}<ô䣈õ`ÀøHe¨=¢?„¦ù:w¿ -é-b+¾lçÿf' +K@<ða-ÕFgòƒ—LðÇJDÖ²Òg½íoå&ÕÕ*=çu„ê$³rÎÆÔ¾­©Kå+QßœkJr×”Q.˜(d›T4†õÝçS ³áD¯c9$äÞlèçPg"=ÃcXË%([{Äm !ÿÝÍü-Î*4i‘wb=o¦"Ü¢ÜY16E‡Ìywƒ³ÕÓ(±n^3djäVws…k –h­Ñ7¡G†íñq‰oóï+öbyD4¡šÖ&÷£ò;+[/ó@°÷$öHñø/q`1… W»’§m0Ô±1ÌÊ÷"ŠðÝ_þ¥KÔ#Š–wNd.º$BÜoáÖá*³–,}Ï^?¢cOÎRÈfÅÊDZ_JØ‘Vütâ±ÝJfA¹âhíØ +”æ|L-ôz§OÚ³w(²UV*xj!›j·Ím`9Æð6‹¯‹f©‰dþÐ,ó‚û;‡ÝØ +»QÖˆcñË«fYä:°®¸=T@(þñ„êˆBÐM3Ú`üÔÍÝTÈv%rè#8¼ÍâKþßÄ°‚ŽÁ÷ÁSD—u¢;ì÷‡ï¿/\³¸Ž´CÈp·Qð >¨æ uÒôx[/ˆëéåó2}ãßSÌMø€ð8ù‚Ž•8äsÆSS zúhê†(½«ŒÛSL¿?ð\,Ó–‹y Ó誯ëȧù’“ÊÓÒÎV¿óÙftŽÅ‚.L?Açƒ_¢<ÛˆU)Lrÿ×Hmù.±Vر ß%~(rzÂ×utU‰3îµåè,‘1°5÷ï_&Êž1,o‰ªR°EaZMQvÙ¢ˈ%²5ƺÌTe—Lªâôß2²ênwÑ®å«5‡+ÝŒþ®•Ú +¢Þç+•g$òZélDs(Ma¤Ê·T;F”­/{l˜›+,£C36Ž’ÜÕ¥c1^N"rkì·JDÖm1efö5–Æ宜(KD§×#7)ª¦ˆÖzCšÊøm5r-ªŒxõ¬…""ÊF\x§sƒÃ“uá9±äÏM&¾ÚßÏQô÷K°äé€ Ûß3}©òØ°hu$½š¶|IÖy¯M”fêšä"6Ið4þÌØIôÙòIšŠåã—$53Û‰9ñHÑj¥· ƒ#Ì»U>íøPƒ'•-[Ù©ªú¶ó¿í,5%%+Q#ÛeËTgóŒ4«ì Z^(ÃÁ€Ye-‹‹2ŠåiÉ‹¤'óZÄì·8ŒC¥/7+žî[Ã8…ÑŠFå”1õ>8ÂYfƒ4Y;Vr¿h_—$2Ì[é‰7á’”"Ð#ܼŒ©Ð+O‡«¼¹Íܘá’t»ØéJtu¸  ¸7—oÞ +°Í[È°ÇPõ1t>'ÒïtŸEL°LÆWP|>ºbÑ"—¡ß¾ßâ8ü›æIkᣫòh8}E­)¾‡\d:‹C{RKîĊˆo¿¸7®2}ÝþÉ ~6½ÐwI"¬MñÛÚOEax Y,R=2$ß"S{ï?öuY”Ê¡×Ç8FÁ8 +²oö»N'ÿÅ×mŠnâÇžGÍ /ià;ùØ4 &0ÄÈ”©ÊLM«™êζ +GýX¤«ñÝÈðM†r}v᫲Íê‹ú¬§4pf¹QE7ñ¶<ûùó¤MVn®*=Á$1«Î‰²äbûç¡(ÝØ\Ï+ž×ÏóVWÑ­øh‘êžûÅsðÆ'~"ªù˜Ô®~D÷1§{߆ïø>ZyFK†'>×YèUtÞ·®aÇ+ ·[Ç¿½Ñœf&…ví¥]ª/ð\¢ïß_6¯„æá—`UCÉpÇzìsÆŒ^â:Üù쟨 +Z/†å~DïÈá5:kÄí㦺‰Âࢗ”˜‹/ÅB#I-v”+ÃÕêlQAuÃõøÊJŒXñ†uÚÈ/:Ge\jÄ ¨[A¡c)EhȈ]JGe¢ü} _×½ž¡=ì@³é$Uõµz?Lÿ B ùmïòç¡|TzTr”JšîõÏ+ho¿ÿuÙXé·DkÞµ­e§<Þ¿¿Cþ‰—ö•r9Ñh¡eê܈«ìºct0_vÐÛÑ$ 7|à¿ïe ñ\¯­?ªJ¥¦7¦$.t—;QîÎ;ȉîÚΰ"ÇIÏá¨+«³x³u9ÈÞ†pµÂPtëN™Hr{¦†O&†p-±tèmFd8»FièÓpBA§iQ3»Zúë3¦8¼á:¸å6Ñ´.ʳôËÒ%¼™-2HÝì†C¥)“Û&^1ñvÍŒvo& +W[¤þ:ƒÂ_˹É,~Â×ecõB¿Ô]š;’jÚ „ˆ%1‰²±vĽœ‹ù’~šbx=8«~¶7?·’ÎËۮÃõa̽ d /D/'òßHdèr"!¿8ñBÌkåØóÕG÷9û'G|Þ¾•TÉÊ…fõHŸwú® +¹ó¹þÄl¿§y_îSbFªg‚«9«F›ËO¼ñ‰Ÿ(G(|q¨Ñט1 ¼(5±—¯'#YF<ŠÆ¿{z}6~,ï³ý½~3’úïcøº(rX1j¼©»ï¹rë[ikº„KÀˆ£u®çžP9*•úSžjØ–…̆IÙ4ºÁÙv"ý÷±]B–òLÛ +¬ÂÓcû3!=6·àŽrÈ“¢@ÞÅÊ”*ãœéPAîcv©Û·1ìá%©bÓLq®¼å¨ê.ì{V„Üè¯|¾Ÿ€°X/=ðÏÿÿý³ÿa\¾ù3zðÿ÷ao¾Â ”…žÍÜ€M* Ž<ÒHõÿý?#4 «öϽ¾ÿ±¤•ôÞ'µáîGto¢ úAh©ŽŽxNì™xLôZoâoýq#^?^ÙqB®l'Ô=†¯ÿ–yÝf@7²ùd­Ú§iÒ‡èð¯æú ž€+sòÌ‘gý$¢c$"”_fObÌ~Æùúñô©³xHâ†Ð5ùÛ¢Ä +<¬à‡µ~Þ˜Ç]ü°ßo«Æ8©NSKû¾„\Ù*\xë€TYÕâŸ×î0ºÖ±¦Æ¯{²±{oóuü"þŽ•˜ÒõãeI–KlÅdÈÂ8¾ …]\Û÷%ÿã‡íyÜÈO»þxDÓûªieÛ©fieoKK[|l­„ΗVã÷âkŸnÇt¿ƒÏp`”I¯æ/b«ùÞZo™êÛ’“KîåL¯Ÿ«ë8¡W¶V •0f7ŒbÉø¿9æ¾oQÐöóqç?“Ç3õxúnë¬Íèóœß¿ÿ°¸Ü KŽsD­iúùì ØINì˜U&ˆC_F7=ÿÅ\0[C÷[¿'ß4¯Ý^o+@âo­atÒåcKÅì•s„ªt÷2G@ãô5»† d¿Á@.'6Æè«@·ã°·ývvžOÙ§#ùx~Oúmç´½Óé˳ú²†ñ@<,øÓÎ|ÚÅ„ÜÇ÷(Û/àÇšsöüYËÊd³Õ¯dGˆ ë8£:âšµ¢0XuŒ«™?çŠÿë$HŒƒ@D±ëÇ¢ýd h&¯[tÀ¶×1Xi88û€‹6Ýcï˜Z[íÏÒù{< §Ù·ËXÞ·+oûéÓ²<.àçÕvL!ȨȈNLÄ +´ô{. €6¿ö‰ñ;>ˆôÅ7x]Þ»ðí}&-d­µ±}ýÔª½ñ\1-닲­v¾ Õê,r^¯“ú´×ãù=·¿3”­÷]âTB·¼/Æãª}\alÁ«e­/åºõrlm/pËb¬p²ÏëÇÈ£¯lŸ—øq7ž¶ía‡ÿî¢Ê7Ãÿu¼³çP¬gÑ¿V¸mÅãûûÐVݯïtþ@’¥G• +ë~ÀM a +èé—YèùÊö6æ¯Gmûíg¶Qñ4¦‡Ñߦºµâä⸀µ¥ñŸN¯{ Üib«ëm HŒ%pdì×›´g›u*ËÆP.ÇòmiBã}\LJÚž›¾}[‚ŸÐ˜ºcâ™ßÉb{¡Õº/ã¤gvcÁÉ„vR\rýyci€?ÿòq•² w§žZÂúRXr·%‹¥yXßÇø´m{üxnëö¤rß17¯4AwOê·d%¡A+CÊ[}µL»“ÞØ͇åQ0'“èeÑIü­%;UûòöqA ü•m†s‘cÈc¿d^ƒróm“¶™pßÑǽÿtPOÕãù»­ó“Ê}[Ü©Óü­Ž©ú;€B Wµªu¾Ë¢uB¬Ji²æÕÐKsNúD ÆO Ù:ÌUŸßV€ÄØ5Onxý8 t×Ùóú^èc èÀà¬G ÐzÉ™+»—™½}8‹Ûy¸ìûíð<³Ogòñ?õÛÖ=éÜ·Eü ;ü¾âO{ói#Mµ™>ÐfˆTÀÕ5[?×2°ÀÁÖ—ê$ Îȧ´¬bÓÿ¼.*«Öµ4ø­2@™‰qêLãíc‹‚;¤ó•­¥Ù¹áuIþBl׎ÐílZFñ§ƒôxêžÏçÓa¾iÜ·™Æf=,Ëã>.õXà“\Îg8Þ ›wÕ¤Þg¬žÕÄÇV xZ,UxY^¨â¿6{=^ç)b¼oÄq¾~\Qøʶļ^ÇP¨ý:`o¿545š¶ûñh<¢§wÕ¹o“ 'ñÊ<®Ý§…ÀÕ<éð¨PÔ Ý[2Ú‹+b,4_˜ëǽ®^Ù~ZèÇ]yÜ¿§Í¾(ß–óÚ|à3³4|÷ž‰„H!¤gµRuw`…Ò Wøc‡—ãûˆª5!­$ž2ZÄŽC2T{üz¬åÄðšÐ͇áÖÒ㘔ïZpÍ[&aZµJJÛð]pTK>8žBgÏœr-É£jº§ŸŽé?ŒóÃŒ>Mÿq­Wõm bl§Ð5k÷±™ÖÛú¼©B@ŸVç/ÚËŠ=(Ý÷ŸˆeåC3¼*Eþ-K}š@ãîI_öhv¿Ïfwfδ¡ãŒ/&YeåJYoU^I–³çF‚xŠwnYžaÜ·2Æö¸ï·óq_–Ï x_èÛŠ=¨Û÷Éÿˆþ°RkzÛ€˜üÑ\ˆYëJ+ûÖ²ö Û?£}ZÐçQüÈV4eÁÊz£rg‚’y'†õaôíû·WvÍÅâäF\H‡«`'¶¾Žµ+5Á9w^›\SlØmƒƒþpÏÍÓ!{W·ïûýa×붸_Zšcº=Ö/ùp¾:ÉÊmA/erÉ’U゘›Ö1JpùqìmžHo?1V?>ئֈiä¨ÒØM“K“1+PJþÊ +h…ƒÇž¬$[S]·Öfò ΣA·¯èØêDä¥8qùíBåpiÜOC,Îýè<²§ù®lßçÿ{s_¬Çeý´©tï[Ÿãe5ÇZ…D±~Ýß#Žmó$Ô.žtKbHú˜. o‰Ú±²V<ã ëŠ;åѳ £§O~}krƃc@’–”•ÄŽ +bážnÖ8/˘à‰h]çˆ ¥çàñÈ<œ­Çsø®lߦù³×é¶$ïy¥Í._¢Ó·ëýX;Öܪ*‡5Q†‹ u$Ö:-K¸U=ŽÀ:oº¶ç­¯ÂKƒ€³¤¶ò‡[ýx.OÐÃi»¨Ú·þh‰VãqÝ>¬qCÖ ›3¢ÈÍÒɱ£G(¹­£~*õ¸ˆw>²0ºD;ñT®ÿx•·äiï¶ù¢f[ìµú“pI7‚°H+ɳn—d¢uŸ}o(!RÛÛÝ ŸOãU1E"P+ý½´Õ‰Þú·Fâ.)XÔöº‚8¦_{»1îÃû˜ÔlƒüAl®OŠ¼ë‡o"Øéqe‚ÜïxÝs9@TÔùñ׿Eê㤞Wàq¹>,ìÛ.plÖ›Â_}{÷â©š–ÞØhþ*ÔîXVf$@ŸVíAÙ¾ý +WÖ€ä\ôúmÕÍ ¤¼½ùœè¥hÎcÀN4h \çæõ'ʘl]ít¤pŸDëóQé˜hY´ Aiku iÚ÷­üÖYxØ÷çr_–køaµ_×ìAÛ¾/`,¬ãR8Ý{ÊrøÖu §`íýÌÃl×åÃ"ZHÏxE+>#š»Ômjï*X×e®ÍéUÛÚÜøÝ:à[szøÛÚÛ8Éc)ɤ9z”,ž1á÷Xûá-ŸèÇ;”Õcàß•_FƒS§{#{øñèE6nM¤sõÀ¶%…»ó¾½ßÚÃYx:5Gì]¾¯À–ða¹žÖõÓXeßHÛEz½F—´ôˆ‘v% ©A‹s¨Ãʼnýîħm{ÜãÇÓð¶j_ŽõÛ†Ó±§ƒ!$¯Ö ?íÁˆU'xª™ÀkÁÀ aí &brV46¤S{e·§ã´÷ˆ Ù_mÁW "Ó·,Æ:j8¼nkñ†Ç zÚÉç]<"ÓÛ²=y×okk»¦$÷uü쉾ÖÅéè¬ê X#ÒÊ)ŠLåNšzÔ§Ô}s`7 r€.;±u?¡ð¤µB}´\¢O •þm6—Ê·èo;ôÇí|ØøO§äÑz8|·U~Ð÷ï+³½¯÷e|ZïÛÞ|É5FÒé`u:èÞôµý7iñã"’½QEB-)8ÈIHÇðs¿â¹‹G¼p¬…FYØ…²“a*‘ðØò蛣»^‘Ðíðl ‹Ú¶ðÔÕ8jNj¨Ýı q ]`ɥ…o:yÆD¿@`=´M8”§÷é¨?øÜß&ü¸÷U|^ïO›ãPR´bNI+߶²^†õ +”g4 +‡L Lì2¥ŒU@h>«>k>c¨Š–”R"ÿ¤Ÿ‚‡k˜xÌã4Ý4°Ü¾FµEü%pH£¬Ý17Tñ7Æ!˜`3´$Ÿ¦§‹þ'äñ8=»§3úàv›ixt–åq?­ö0eTÇj§)Þeð\©‚ÁˆÝæ¦óöñ…ŽNyN—qÑíùxÑìîíÔ©âz"s8ÞéEè%Âx&à*´4ŒÎIJËÐ6@8?tv™2ù¬+aoÜCôäýà 8ÎÑýÐ]}ï·Yþˆþ°$‹÷i¥íuìž:â,0ÛÕÜ‘ˆQiðU‹Ðý/â†ÕÈ€Xc"'.F¯?®ô㶊7yEíD$ƒ,ë x0ÄYr8WQ‚Îðˆ/Ùì‰NakÞGûû®ßåGîíZ•–¬Ôwè`¤Iz§ËÅб»ˆ²à {)íàRJ¶ˆíô´pl E•k£Ãóñ‹aÉØÒÅap_ÄX™ÇÜ›ûÒüÁ:>-øÛ²=¨ã÷5¼D5Š² +ê×L8±0/ƒê±!øhÖz¡¯tF-GG€#Â2§Äô²T@dê²¹šFS¬cਛa~ˆû’‹þ¸?;ù¸ígäÓiz[¶'÷ûm Ãýî0\)×(眧d]•ô>šD-@fMŽhRJ®RŒÎ¶”NdPÀœ!)ôq¯˜CÌÎ ¡<‰µyÊÝ() R‘ Ö^‚T=eù@9ÎÃûùq?vþÓ1ùp¦Nßm™Ÿüï·µ Ï ÷ýuzÛ5– 8Tt8w%Ÿb¸ý…dÄaª\‹¶“1¦\º÷¶Æ$¢èúºŸ†éåï’ÕCîÔÇûVDéqßvøÓqx<;§ì¶l_ÎóÛü(æq_®§u½o—ܹ§!&¹xD±Åp$Ÿ&žõõ .Z¨clŸº¦²U×æ\¦NÝ%â(³È)`®"îÅ[Îï€VÇ‹h~“¥À€7!óð¾å«£ûö$â6ˆ/YÅyCný,°Ýœèq¬o><ª¨‚õ/­ûÛÁ yñpÊÏãÓá}W’o+ð£r_­Çuý¸ íÛq<©šH™Å;‚Ø`ýŒ‚–"áTWæE…‘ˆ1dÄÍíãÕ™u2Â7iG¨Rá%ódS~Ü÷qÊi|^PTÞÀùvH†£ÉˆØ3iª’ýƒSðpd>®ÛA¼úÔoüýa5×íÓ"w3GL]…ùbÇ`qFvÖÒ¼Y¿#ô‘&Sa˜«ã蛉ó£ä?Zâ‡Íø´m·=>—èú¯¶Lû^lÔ#³Îp·wÍâ^§›þËýýžåïSþ&Z¦MȆ Ý´=T¨×&ôŒëD‹kŒ–_?n‡ô¾ÍõB{Á…¾‡»¹^&v_™«U:2”*«ŸÞ%½ÐÊÌ-Â5_<JÇ~)¬=CÏÚ#d¹Ôh_ÙÄf4À,j î@XÇB€+Š8–rÎÍðcι¥É¸Nç\vP a³iá}uAØ™H§@8¯œ˜LÇû*ðdý›—ÓõZêô¼nOìò£¾Oñq1>®ÜÓ2?oÈãÞ=oóãø°nqºKA[œã *vj F¾öÎÌÈ05==êÀÃm8¢À¹j8F×Q*¡}·æ—Wâ0¶­dr°iT:?bA,grñ/¥ªŽ)ßâÚÍa,w·ñ㢣t(¡«‘áÓ:<ž¯×ꬷߌV1|œÊ§y?.Òãr~ZûÇzÜÒ§y\¤Çå|ZûWùÅÚ&G4ÌQÅ6‚YƒXÄ/(±Ãªµ‹ÊY™ä¸èãسdo/0Õ›FK2#FnëÛÉJ'Óµ M‹º€“ð‹õ™ÞëÇ´‡ãPí5Û+S)┯šév‹m±$§JŸ—öêmö ‹3u›éÏÿ_fúß¿ã÷Fuÿ/ÏÝúÎý×ÿÖîs瀋àѽ]pRÇÀäm¥ó…þýJ·Ï®}¿òùD¿òAoº-ŽÐ¤Îÿ;Õ6€w$¯îd/N‡,c¯À,Ú9ÿòNék,Õß°Î@Å4˜Õ ¾t¥GkÓÝX’?@„ë´ Ú —" @E%€Y]#Rì¸m­;¢¬z– ÿÞÊYGàPI¿‘ñTõ&ˆm˜aŠ6 È·  P>ÄÊS·Õ;&“£Öª_ú¼b±[’àP»Po„mÄF˜= Ëâùäèzl` ‹l4¡²¦2Åè]’±¶]XŽ•ðÜ ¿ÅÀCû¾:§Õ'pɬG€fz¢8Z°š¾«yºEOzâ-¦ýàÇ#£;³+µ‰ý?½Õ&qê\ºàÉøe­ˆ-XîgÖ´´5è=G3ˆƒ ,è?ò¨‚¤´H‡“ØÑ‘ÝÚnžöøþóQ›€˜,ìãçëiUx­ sj´;;[ý˜óBŒ½‘ Yr¿úëhdÞ¾7/¡Nz‚Úˆ¼D?·ÜÃA¥îÐ;`Áp/P¾3«kñ¨@Iñ÷禢zÀˆ&%Õ´XÅn§ô`#ý£þtÁ c[õšItw8:$_Ðr ±÷P;åàÓÑ—” ˆoŒ´ÄŸÑ›j`Û‘·bx"&Në`~*ÉíŽ+[¦n;ÑŸ#ZÁwY"–àЀÊQUŒ4°ÑNZDBÇuânl™‰]½•„%tëv°;ó"`¤‡ÎÁR÷¦ó„yò‘K°+ÇWBËßEGvK6@D,Ĺ¾\Ø;;ÁU£×ÄIô.Īö†N+C"Á…;t!'o¼Wø1Ðý€Û±%‡¡~J‚€° •j|<0^˵+B™"Ô[ëýKHð8*À‹¬ÿìXÜïêfÄL ]ï~0䤧8 †Ü=kÛŸÏ *s­Q=ŒY*ÃXÐrœÞ3°’œe +û€GÌAŽNô\‡£®D†§ÄgN‡•0¡…=«EhfNŒ½TCóŒçè[ôóV' ö!6>>N™ûÖâQNÈì +ÎT¹ @ÑwØó@ôÒ,sˆÜ#p;vÎÄ û¥¼«bÉÌË9ðjH½´L\Ž¡·C-®É×ÓõI øx7¬’’ˆ‚°[˜pµuu>î[0Ï]goã¼îTïmCÄ6/ñ¨M pÄ6†1‹ˆ+ëÚw^°¤=#¹9è«Äö @í…êÌß’™Gª¤/¼àVžuÞU=-Qvfn„øRæXzTõzn’±§NľҒzÖµñózÛ·êA½¨0X· J^¯&”to ƒ³`+ÎU°V‰ˆ2]`“+"÷-›’8%!Áâ·4ÊR3qc—Z[—Àx®!zjh›á‚ö›HË0ô1sÆø1T‹­>[ÿðRLiMö‹ Ͻ—F´vï24íÈí`ƒ›„Ÿów^V!(R#Úx8,ž¾xÞûþܵ2ïÇ/ y[¯¿Gü˲É*•3+×l’1(ÓʽçX¤9=¥)„t‹é'$€—1ÿ¹f3:tý]³Ãⳟ;Š æì‹ &7ïö`>àSò˜Ü™lð‚YNC¸èRÑU‰mÎWâú  Îu0©SêDцÁ$àysZK¾¢Æ“ˆo{ñûOÝrœëJ´î -Û=†€× ä2³qÀó&ÚèQêÕI°$:Â8&¸ýR/·jØÁ½5a ñ¥*KPóŽâ˜´ÝÅ:ˆ!D6¼¢¥êÀ*z…-Œ;Q™Sئ'ÝbN÷Ú7Œ/¼uöei$úfý‡óNz…­âßü˜ò­8ÎÙÕ}D‘žÐZõeÆÖ¶ Øj²+&2–¼—ªWÍ-ËÂ_sý ûÃ…t,ú.!5’°ž¦ÿßÚä™3Ç0'°CŒHǶ‹pÌ]wú‡1’šÆ!/T¦‚ÝÔŸ+¦xˆFæf ü˜sU{^œ~ç;ÛÊà½)ßsÇ”g¿8Îõ'*µiw€ïµCLúKÔíß"ܵc£š†Øôºï/·_[—Ìê­öm÷¹ÑRæàÇ®ø\1uYÀ1ƒ€ ?†“»´3ñ'ƒE½ðûñ¢ÌRá,¸2¨Ó)èÍÍ3éÚp‚t!yºRÃ%L‚SYŠµº 8ª°@½Tü] ;P_ÚÛºösŽ¹ ›ÓÎ@\U¢d»³üc/q¨’€i…íz?Gºxõt×Hs‚‹ê'ŽJktñõ mh,d„ÓIËg:ø„;ÍÊ/|ry.:<+¥˜yØÊ:‰ÃÙñ·8´îY®À$±Œƒ$®&ÏáËRÁ2>àÍ žYL  nF”o<Ù]F9£ªÿ·Ö‡š¼ÑpuâÖBÒž´ +ˆ-ƒÎQ?З¾­tY \èܶ꓾¸ÛVü黽z€u•üHI\4b:óx\PžÜ'ˆ}ÝýÀV¹'B}é9öô ÿ8I0´ÀVêèe‹ûºjžÞ\« :ˆÞ€Ò‰}bù_ˆ¥„:¶Ë¡^9´) î‹ÌX7¯;—Ãí´fÐñyÏš:ä¤Ðì«KÙÜö‘ÑŒí +Ÿh¶"µW62õY¬‚p‰KXc +Tõ:µsÂÑ©ÀãÊçðCt>æGë$¢V‡ÄEÉéÅáºñDwÂضZõ"d Á\ ÕðÎÙejSßÒ.·8„úŒæX~lä*ézæ“RXÊïÄzHü—ÚDì„ê*?ÉB‚_]o¿0®+r’~¹%e¾ºÝó[zt« ÌÁkx¾½Ò_í§‹kNÊ.‡U LîqçÛ|X@,ñ˜oüênÃTWÃR7Û„ŽR:%@7;ƒîÄ!”öäžË“hFö$‡pyŸôEG©ßl_†–L ¨í“Z§‡ë€*²P3÷;‹Dë^ðD€È/]â¿D'ÍÅÓy‹- POº'­€˜Ç¢?È^ ºÛdÁúhuVíؼ&£vÃWyÉß¼×ÙwÞÞIÏ2 ŽÆæ<¶\¬ +ÈQsSæÉ+í”èæµ÷@ò*BUçÃÂëk²‘1¥›týsøÿ7Økõ!þ߆¾fË ð*å9n’ÝH³´YÞ€þ™Àb/\ßÁØì¿ìQîóo=ÍXNîµþÝ2Ig³Z#6Ï}0âÁêôŽ¸‚sQª¥3öäW#dGqd‚Wyféã¾TÁ™Y‚—ûGô‰:+#Ûù¥Å‰õøãѱÜÙ~3£ÁB1ïšs5·PF"ç}{t]Y†½4×eíØfÔK +¢‰¹ù=—ÅZ_@Î1b­‹‚ó‰óõG'ZcÏ—ºL££¨Óª<¼îôU:ö½ÕØ|à³qõ’š–¬Š—$à¢Ø¼É ÕvN$«RìI8Š’íj„i[QIxg¼3«˜hYÂk$à o£ÓÕðî$óqg†Í¨26‘ÿ‡ÓÚ#‡È'4¶Cê>†Xus8wáÎ%Kcþ½ `Yà ÝÈv¨×Ž>ÓYHbĦLØ7¾ñ“Ö³{êÎÊ€k +ø9½Âz¹ÛDm4;r²­|Ší ¬ ˆRVh5×¥Wëâ>u‹1Zïü5ˆì§!ÄèLóG½ÙQåÉh‚Pêè" ',v=é‹eÃFD |cÂBï;ç¯Ë!zRþl2Ø à†“8|g¬ø¤ µ5§T›tÀÄq÷0èy¦Ué"Š‚¶¦ÆÓT¯çqűn æ¡/µw®ú :§=±`Ú tY£ºÔ˪èV”Žµ%ÌßĨÊkK%È«HšLš˜¬ê>¶½Ð˜ÜÅb}lJ¤ Oƶº›:Õñ¾eм칫‹~E«s3™Åó`—Z”M£÷*6µ<¸áõ¤®«…¡neÒ‰áñòqŽþö—/w2£·&É¢¹Xzs~`ûAÜ°…šK–Xz83¡¶Ÿfñu‘À„:°ƒ6£¢'µ(2:Ëh]Øf~ŒÌ'ž'è¸Ü‰Á×Þà¯+Ù éÜñ¾Ôíÿm ÿ°hšÐ¸!@Õºbª1“MóhjÁ‘³„²ºÁóãØ•!ÉtÚ’–qbþ@2•Í:Aÿþ½Puñ ±šcíVÁe u¢çt}Åßs»ÊùcÓ+‡Œ:mÜc³'²\·Þ$4>ö—7úndô™x™ÕMÕˆ=ˆ(»3Žß´îŠH£>ÿéØ:”Ñ‹ÀXQ¿&ÊVì1c½’/Î¥7»E_ì—?×™ñßZzYËrÀWÔ²üò:±Ô³îùÜ¥¥Em.¼+áR¼EøÐSŒn‚yuÊŸ©Ü§³9õdó¯¨MmèëQÁ1.tt™¥þAgÛ;㓦J ÙÄÆ^)¶÷0~æjàè¢+ª‰Å¥gîØ…™]E…&ö.EßÖ×¥s ®R}òì0\àÈs©ö _k("íËuv £•hÑnŸ£µªÿ›³ZQêˆÅÈÚ^Ééô8 i mì]†âÇçÂu+:Úà‰s9-k¶‰¥ÝPöP;“LìÉfËhåu¥y—1w +öÆ¡eÙ$N¯,Uq¹2m¬Ð¸dæÏ  fK¸£ŒŸIÆ·M|}ƒ†ˆ§MAeèÒ;Ütjô!õe‡º±¢«¤µóeI©/è9bÕùÒ]@§o¡6D¶Ž™õr„Dœ¨âh2ík°ûL™°*t%jñÙά>‰M«WB@p^´W’éX”´Fé}&ÑÓìhÂ溰ÿ =´=8a§š\í“zØê.Ó÷£@âBÓ%‡½ÎüÒ}~–¤bÖÞûfË΢®öµË[ÃF1ÅÑf‹ÞD¶Fl¯n³¥o¥¶{#ªðŒ—Ujg)[-ª]ÌCþá–WÌ2œ‹Û¢E¾ûGx™øº´ë`%çêQt¹ +z8±±˜ÚöíUÍ•®Lsm€çØXW±zThšùmÙû 4K —o™¸FuF~Pmꌘ`Œõhžê°q&©Ëeh0Ö¾¾ß~…—g{©1!¨–çìÆß³£ìjûdUš[v7ß{Óž*lëÊ4÷Ìxÿlmè”XQß»”îÄZt´3žGèkEFÛ­ì}H}jÒKcð¦E¸ólL1 e,”æ†î@<»‹™:Ð@F75óCM0r‘#ÁÁk‚Ã(rÙ f̈¨ãµ‹GŸˆi0×KSTs(à}^»ÞT†¸ý‰Qº) ‰¾ŒV§ ò^™#ÌôO¨lçI$‡´¡L¡âþdÉèS0ÅõI&¢éˆ}NS4rç5¶„‚|Q·[çÐb6L`ÊÀð0ZMB_—¿OMÞÏASQfî¦gÚþ‚›œ±ºû­+(kñgB¢so–Zæ6 ÛÍ°±‹V—ý±æ–Èu·í/ÂP¶Û°_¦UÝvö6@î4yª‰*h,X »‡º°/¾i;_b°à¡p'âR—ëËÚ¬¬Þí×L86ø!„å•£–õú€Úkã]%úas{˜GërPÕêÄû+Èà•®flóËzk‘Þ PÏÉl¬eI«u RÄ% ݵ™®„mG§Áë9þŽú@æt°—+†±µd:¤xúÁwpF~E@ 2&Œ!¼t¯p•_ZŸ—à<ª8+àà +RýüÞJî·~Èaƃa×-Pó1è)ˆÐ¯q|JWè.mÕ¬¿ÕjúXí˜G8³ía\jèÌ6† :4 Ù.×·è,Pve×Ã:‹ !ÓÚ°t#€˜mëĶ /ËdínMX wI¨¿¹üñ– ½±F[lÛ.Ÿ7lê£) ÂÓÆÎY‰®x#zxÚ´k p/·gŠtK} Ždpà“g/»mצ¨ ŽÍã€~fm‚ºÎ™úýbÏy˜¼ë9çðªdt^ñ5Tp¶u1§ùâú¸¡*O u:#Z›…”æAg횊=m×ðqyá2;Y焸EêUnõccP[iûé7Kz‚£o€H4{ÍG@ç1¨•ø3è_J4q÷*ñ{ü¤âjõuÑ漩ÉöÚæÂ0…*¾iL% lvåÄaHWñ"ji”)oÀß)(7wz=o·“eú24`½o%ÇÇÑÄÙ_mj,¡pl V¿sš§ê`?/Žæ&åòÖ[œCÏ7ª¨0¹´yäƒf8²ÔXº#B‡ÛÁd3<ˆ­Ü }Äõ¶¤âŠŽ.}°Ó{aåäIÊnÚ XÎ;%£¬•ˆ¹Y¹©¸ t”Ƽ æ€.—رÓ}àOºL„‚tIrè¡-Piê/šÉ¶&ŒÃT ïà;ieî€K¸nECƒT^ΰ&¬[.l7ý‡fA0וÕ'¤…¶\òÅ[ò®˜ï°É!Óh.Zw¢Ûí-ic'díx¶íD†§ÎdWW+U¯®÷.—‰Ê<£îú“<ôýå¤a%|CS’Â-bg‚$Ð,;»Ç¸p î¶_«%akßg½Êþ®ƒÈ¦Ó~7ýœ<Þõo®#«SG2[<;ø&†´*®½ Êdð1å²5]¡ÆM§wpNº¹_à·¾ WÐÙ Ó¦Ð0ûÿДíö‰Á¶ž$Š:vÙu+¹ßˆ®a"è¸Î3…r/ÿRŸ Dv=ó[Î+bïFWtG¿Ðé]¸§tKó.5µ>®­Pp?ñ”Ûs…(Æ›ÔYàKiÞE½i.r¿4O²¡€Mú¹´½4-8;þÊz3*&A¾;‘ˆ„òy‚HÐcÔÂూáD@fM¤ó~®Ù«Å|+<5äЂƒ{ ²I½«jÌ}ÚÝR6T±5§þâJ³QôÐpÕ4ܺŽËóœw¿î¤ÉÝÝÁ˜Ý¥=ÂÀÍhêºêžrv²®Jô?víZ—Ó5ÒŽ}·w6C¾þònî Ô‹èäl*„8+æaeÀEVia/c+^]R´<€R˜ò…=dðs@ÖXe·ÊK¨wÅ0Ò y²È®pt¸ÀÁX!Á +ý®Ö¾hûðØê•äéó¶_âÀ¦è+3ÇÚ‰û¸/f5ñ)õ +ª»Â2¶çh%Û‚aÝ™`v²‹3ÞÀ=k€ÚÇJs2_§8l¶qT¢ž úK82%áÍ”(ͽû(ˆÇ¥ðRL"0›¼.àЀùÏ㺡TLk=Â&´ÙQL8ÃeHI>Lmçí˜tDº“ž‹Sv“¸ÀóÜ°CŸÓÝg%ýIúD«Bˆ;&­]ЮپÛVÂZgÉ%i3EŽöjÙÑxÃÎsÎœ=pI\‰J=ÃQ+ã|—9O¥¸YÙj)•ßºF'^—´Žy™Ü¸ +;ƒå–9ÈîKˆ§Óɇâ:59„{dž6g¿±À¬¬¯!«?ÕË#à„Úb߶Ç _"¤)㜕ǦÃÕÚå·pi­Â£³/W ¬÷*Ø‚&¶T-ñ¤Òcˆ/GX]—øIµØÛ$w(Ñ>¹^–ì$–#Üâ PÌ¥9bÉR ¿¿ùd [éÑùˆÔu  ˆ«K²>5SI·kŸÇ¤üd Ú0ŒähR‰¦Û ‘g¸SèïêFX&5)]ÔÀi›†0gm¢+ˆCÜ—x©1à:²ûgul.¯ Ä*0ÛÉæ°æÕ²°‡{‰Ëô=hT|ÂËñ?{‘JQÖ%Kû"¢¬¦0‹¨•úX— &$ê·¢ÐÆéÔÇLø±¡]™’©6¹CéR“‹èAüyY™tïðé蹕ù=QŽDb&íSWÝ)èlkÄU´`>±us÷vü‡ó• ¼èe„“ùq„~„öY P¥è]ØæNa¢¥x=í,üÌ‘µñ6müØ~ûøPNÉ¢˜6¶Ý¶CóÐxi»“b´ÍÉeŽ¸Üôs4«-¯¥'müÎr/;íÊv®ë´» Ðv{%é`Óßh¹*³é îë’Søpügæáp%­hdò‘yöýq]3J® äõ›[ +à³ó$3Åó\CzŸfÈÂÇž! y´¸í&—=¸ ÎÌã§Æk˜hð³Yüt&Ë,ÒZóå¹`J°Ñ×6⑘ ”BÚÓ>´¡œ.ècÙˆ(…?B£Jð´@Œ;b“·¼²ò‚’)XµêÆš?6 ã€^`À +ÄÊCÓÙÊ؉„Õs(Ÿk¶¾õGŒ’V‹ó²Ä‡'­¨Óê8àñ9“Þ|…  ZÇöQx,Pô—Çȉâç»™{#]ÎjäÑdåWxvU%`ÊiÃeçx:’]"Á3å¹BSi ™s¶Ó oö¨ +…[ý¶XFQ Ø~‘K'VÕPmµ„‡ZXÑ,—úg0s’˜ù é 33™¿mì.ð hjdÌÎð¢õVntÅ}I§2ó*nç’ß{-Αyþ6®kªd±l‚]È°´é~¹¯:eˆ)K(;w궣êóª|漋u”gÓÜêÊyðW±u%™Qa1´¨¯­[#‘_M&ËÆs432¡Î4#“¢e™¦°ÖÅÿH™”lr€{Öˆ*ç½íë +b©,FÃ؇Ñ&]‘ž¾«µS.aš;Ÿ¨\µ<©È¶uçîèû'Æ.U3:ão¨ ¿å[}àÿýrÔF$g˜Ú˜m7Lï¡A¨Ö®Citž$ ¯” ”nå@ûÆ•Ž¿ûB’¤!¯ÐYjbd„zHËÇr—Ì$b‡œU%wúpèdj}þf|Ã+ìL +cÓ5É Î +¶rÙCMS÷Š8üpyY/yg3”ƒÅäpKRC¬ßíëlçê— =ÖINû9N}DÚÅÊÛxI2'¬Ötò™üôÖ¿-Ã×¥Xx„QâJQ Ëd\Gä´Y€#ëžÏmAÜø\&èÝ´ÆŠ6â@D™>ŽSxTYT(’u/Ô¡ƒ®©KÎÑóù¨öq1••hY +)Çt¶úúR2<ƒÃ–/‘Ht›E¬¡u~¢;ÑWóGŠ³hoBQy…w–¨"¦Ñ›µ.ÊMr«ÙjÏý ºnÔD‹ºý=¢æ·ìp/Úðjx©¤9ÝæÓ´…æGŠçͬv„ÏmwÚTÂ{Ѻy´¡-DØå•tÆWT&µq‰hÉ l4OIÜAÕk·”øÙµ~÷Y¼LUá öðÜR·Š2­énê."þ^-éíÕÕ“ãùé!/–’© ¤qõ¯Î‡)t[MÁÊO5°ðÛ©%¯Ãúú0·×Ř-y_«˜ûz­ó©5o— #é~¸;ÞÔ m÷4˜WÁp–œqVJDr1T·1ï­¬z‡íŸF ²µªZwt”KÚƒ#cCƒ[ÃAù¢Ñ¶ö‰ó—8´8WVœWxQ"«^ +Nå5~äüwZ¹Ž&ôô¸zÌ5>–׎49ç¾PS`È.-ã¾q¾®á‹á¦H¬![̹“ªšrÆæŠèŒi¿ž°×8ÎM¸t~$„¾½ºá@¼ áõ}¯QʧhÊèòëHº£–š•7Ž#ø7>ßE¸/Å7ý!ÿ]þê–ë·èlâ4©"|àóz-O£åHäè¡S!ÍöYŽ|&zµb5²ù<»ëC¡r›4KÊ=D¢X~ŒÇ2ÙÕxs¨JÕƒ¹F– q‘ ói ²ÿ‰p(ñÓk§ÆìØN.°UŸT}¬jù¥ —ýÅâºÆLAÑEŒ ÌŠœËYì–W¤c]3wZÓCçÊ¿Z óû<åÐ1›í’˜Òs‹ˆt$èx|«°³v^OCL‚™cŠm±Ž—/=À5ârªmßËà .$×½Tï­Ç1ü]\`V¾%ÄGDW¡iBœ“D&¤PI[x›¢¹;³­Ö® V}Ì°¶t©¥µx8¢‚ƒô†#|7ös^=vÚÞ§1°@ßfÁTtc;”  FY÷ux•O[Ðy»›X¦.]ÒkòË%/ðHbçJS¤t5øØäHŠJ„UöÔ@å"MeÚ½ãÕqq2ò”!fFåË8GŽqö¿Ø£RÙbq-oúŸMO*Arí;ž©2”Dv1Èù\Í:‹h7Oý8ð kTÉåh\Ñ‹A¥)Ó‘¥½žÞIµc]³Ö—Ò@Z9”÷θ¶D"ºa8Ò%k]I;Î~®ªœã +ÓÛÅÁ1£÷†Ž¥»ûAtØ#LYYë–†yCÇX›A/Ñì.mFQ^¥÷˜`MH•)ìôrßøÍÓ5/Üç’ª¥ò¦Oú òîÆug³,GJ{®¼~aìâ°Š¾5OQ5ÌŽq\òSÓεá­©®%g«*!-ýªénCù7E™¡•¢kPê¥tõ–ÎÈt°RÆ»òà-Zeª~™íwlÔ)´eóMêç?ÜkG3ò>¬Wqd&s20ä‘?ˆS¯`Ž!_òBY^ë÷>¯ºŒÒ?TižöøW©Æ”óZHæ­AlëЂÕ(ã¾1Ž£ÒDÜý~ÈÕc#ŒŠÇKˆÞ0»¡Ç|éóÆçú-Êœ= ?¡Ú0ö{!1«™¢‘þtåFK3‡¼«ÛÙ:ÒŸgåHÊ{à=ttf åÆe’Š‹ÚYb\4g`6¥×œêZ´£(äÝ5h5=0ʃyßóÚŒÁS/Q1Þ¢vifqð¼S,}d_Øâ äí±¢Ý[ûxŽxù–QÏdfÏdnZ3<£ÕÆëNî«m¢QÚŠÇOD¯M)lÔóL ¼$‘ò®˜F‚…û%¥ªCÚ5Þ±â"gb~€Sñ¢†Ç¡ç-ˆ3;fêÅQ™)ÄC—ž tŸ­/${8-„¨|å]PqSE*×.PKqk¥òs Ñt²lÀ´dö³-tcˆrxY)s]š1xú$gÕô—ø»AS¸Ds‘è|å:AÓBQzÚ îÿ`qÔ§"CU ±õDâíö5OTßæqÙU©&Ör½†jÀ•Üú¸¿ñíçIbº-}è©õk‘y¾GSj¥JÌí׋R›ýƵQ¯5Ѩ'5OHôP­ètåy×Jï{UfÒuwËwÊ‚žQ@â©ÖI-&T•ž"œ;“Ô“”-ªó¬OûT^¶‡¥#TxêyJ ®l´—¢Ñn€‚íüŸª¤}ux½È³è^VYL`DtNh. î~}F›ÚÑhIã‹›vnËT[èZ×f ÷èçÌjΫrÊU cl³¶Ê#{)Ú¬Õ6^ÇàžŒ^þIœÛ;´ž&ù¶~ð/râ4•ÇˆûµÐò"Bº¬±³Ó¾µð1T†ã%Yoª\ÓÊ·ËNÎÃW aT-|H¼11WûHBõóü*‡*­ò ¯ ¶¢òî$f4.´¦:š7‰Àç½ÕíÞÛ‘C†uÒ;#ïÀðË…DÄ +üËåÐœçèNt¬Íø“FG)…@ÏCrž)s/]W-U¤ïÆŸ¯s¦‚¦¯D«ê ëfÀ|ÆÁ͇ßÿŒ ‚žfêÐÉãe5×ÉŽã€ÇûK¸¼)¡íˆëpØØ”‘¼â;æ…AûzÛÈDß4vÝkלié@G4b¢ñvG‚îô02ÏÊv¶µ%×Û–‚8Ëâ—‘‹êôu4ÒÑȉŽ0F·œXy.âï×cêØ)0Æ£‹-zC;Ññy6‘c(†vhèæļ’01í8uì®Nw)èîµx!R(\æ´ÅŸ+B±õ§¸šm”v'ÖÒ`jì…÷qìhæ:˜Ñª€°G#±ÏÚ…yžæ…ÃÑ }*7pµ´å ¢›v ¢€¦¥ïè GNDîvµî•ƒ™ÕÍÁ9“-W"‘ºë&8TsšW(•À¶,ÐN@L¨édú¼gFQòëljŽñ¶–ÌÔææŠ@®W ÄNk™ï8Ýîöt¾ÓŠÈ CT`ÜÈs® xX‹À¬.¿ÂŽû9†0J“׳QÕqÃAt9 V¨91™‡/ù2˜²ó’äƒàÅf˜'±Wqb_Dš“ƒ§]d‚‡Z Èž:[&4­÷º:‰iäÀ 5O9 ød/v¶%ác«ä˜¤­ØÞ V1„Ë·Þ¤ÇMØìƒ=¹+õXµ\.[ !9’wpñ4 ˆn»7FFrX@~p˜‰Dm2¡”Ñ)ÔöÃ|1 Zö.f‘ÃVû Tlûí2ÎÄ?\NôD ÿÒëOÉÁ¶°V<,…ÌéŠ×ÆÉÍÌ1 àŽÛU#웃N¿p0koD­ƒÑ×Þ }ܸÃ:ð}Ö“‹ƒJU——edy ŽðiØ€~|˜k}v¼ùÍlb#¼äÙéÃ\œX,؉±p/¿u1,œžÍJp]d^ØÎ  lOI™ ö™ëå0pqìé;æÛ"L¶Q9 “ÑKL8ù#fP ™1ì›n¯Y’n ºŒ!Ð+‹PË=lÞV|ÀÞn =ºÛï?þµ>‚§®ÿXÜßD¥ö?ó·^tÜWÑÒ^RÞŠc¶ŠsÂÜÏLÍ4^v§W6nxâóýçO"cW5ðóÃÌ*ß=¯1 Xú# žMoÖýúñ@E¥j ÷ÑÆ=°HE•’Šöïø—ȈžÖjD ¬ŒzaK†Õ1ßî A—ÛGÛÍjLŸN¡E5&ÄÌ>Å–<\‰®¸<®Ã×_ñmUtzÁÿÈÕñÕm åˆS´¿v×ä &ê"x‚!ñ2J)¶>Ê +kÿõóë0o\þìašVnÝfö©‰qvÇn7ºW;ÇåsŸ•Ó½Ã÷>ÿ#Ý¿ì ªFZ¸Fïß—3HœÁŸ½õƒ/0ߺŸwÃŒwÿûþ}WÇ'>úšÆ{`RGÑaÜ¥Vø~»åûN¿ŒôÎç¯&’ Ë-ÿi¯m€H^ÖØ¥–Wº,'´rþv 47×›f³PœŽ•Nôèü•u¶‡©IP˜Ž#8XÔI5Ê`ËV}S¸ ‹mÉéàYÂ_š›è—áZ›ú:^•£û*ürv€±÷-}oOÅû÷:×|þÜóîÒøFr¡ã°Ì¢ ô#ðþ1ŠœØ9ƶ#Nw}jIJÒz;A=<1m·D}âð-º˜l]Ú>G—´îûìþÔC€±Ï2®zýècæ|™Ó·è¥hDhÊäÄÃq^'d¨éz3¿ÄaÓ/ëç^ˆocûÓg?Ãé÷zzfø-/ýúq×mùsñ:‚Nÿ^CbÚûǨxß=K³³(ã+òN~Þí¬Šf. ¶92ˆ^ôþþå¡Þ5 ¯Ñ.ô‘õ>†?w–ÐÃê‘/Æwhò‰Ê(¿8ÃWûªñÐ/üuç€ ÁÏi”ÛÕ‰|ß¾—–22ÇågD_c^‡ó}§{ƒøï;ŸOtñù³O£lórÚØuF™òy÷xñF®Á…j͸D}ePP%öôcÿè³S¼qX…ëÉ“ì~DŸmÕWOWèäN-”†:±åF×``Jœt‹~Írq%’H/ œŽ'Ѫª;ÝuzuÒÏ›==È€Û9‰%~–ê¼Hëˆ0­‰AdPPFåto ã´5f¹8›A,]>ZÏÀ‡i[¬™Ö £ Í'ÑåÇ‹û\7+ƒ† žf !/ 'b¸îvbS~ÚÃmã<;Ò.8qÆÇ‚SX.lpeüCîXlª˜„uÇ“osK…1‚•áx-†7 _*óT±ÞãnÈÚd±BE¸t'#èN–8ãDO‘|á¯&_öî:{Gê—I}øþÏ~kGßÂ`DU:£I$C2¢åˆ#KÁ~Í$SÖ×ù1›ø;½{Gÿ¸·ƒ£}¨¹V“L 1emV§u ½%‡3ðòÜ×qåݯÞ'‘Çâƒ3øàX« ŠÕ4²†Ø 4ó%$¦òìØ•-}e(p5ý±àÇÁ³ê- ÈÀÐxðæ)ÌëDÙ×Çø¾ºç¥kë2Æ8}©õ¹%Ç÷‡ÏÿtlÿðËáós>.¸‡¥cHfN# þÄùO_L¯Ù¦î4Õ®Ïé#†3.cwtÞ÷h§)Â÷¢'zàsÏòKŽ§†"2öuò·¡ýÙ“¦jÍz ÚcòÚ™{|þT„ü^ /¶i¯Åx’½Žú$ÎÈ…>­±Nqçý+Oyâ]VHô¼7#nr&1:jô‰ìq§{õ;ˆë±Øfˆž´O^/DÞZÏΘùW$Tïæ. º@±g}éŪÁÁ[ãûÏy2ˆ³ºÌðäÏ<þò:ZÉÁ<÷.R^ìà·³VÒ¦5õÑ{@:Ñ[gÇ2«f8é9¢–]‘Šáºˆ-y³^Ïz±ó ½ -¡Àç[CkH|ñºÍµ6½T?´Qv‰á ÿEoÈå¾³[˜kŠ8¸üeW˜½›$»•vgkEn+ió—õÃk¹ÊÜ8‚A<쵌î^×Åô’Öˆæï“bÍ££´Ú‰ç“_ADÞØf7¿¬µ„ôÁÎ1JwWÚ”ÑÈý$&lËI,’M>£Í~Pƒ]DâÖ¬(È6¢_¸FhkÖq· pÑRÆ°ä£ÊV³¡”:B–ž3;¯û"mµ×Íl&€ù÷úTÙÃáMê_®ŸM`‡ñ›LE+`ãi4¼¼^.“= ÿ¨ÚµÅÏ×<¯çcm ²3 ‹è‹±ˆL%Ëy¥Øñ¾”£QUÎÁnó¢0óøîoq˜ÐØNq˜¹†Ö%³½‰Hë-è>##F—ˆë‚›œð©[=觉ÜÂ60™>ëÚû¨÷«`mÿ +>îD#|lßHÁ>¹ó®¾Á:5%Øo=N(="6]«ëŠã¬\®Šî@F´äbϔӔh^Q ìeìªxE›:ýð{8`¼ †5v1×Ë36á:[o õú¶Y‚Qáëè@,œÅ0ñ[@ìÈg[ìÆ⩼“è¯=9˜ã³(Uò¥é‡ÚÃb}¼J y~k<ät p;Û±^4|Y{¡o) Ê®ëpqDy H–A¼x­ýçÄ9!Ejä¿X³{¤ÊRþÖjVF¼ý:Ñ‹CA\eH.,8ã0µ’{~Uo­¾·‰­$Ë}ÿŠž[jk9â?^Ž½.è‹ 5b—ÂÎ'Ãt·Å 9&Ç—\èc)¢àÝ¿CÞvyT^Xí7}ZnË’Z±ÔËm³ôKío¨$¼Oö¯çaR„"ŽÍReBŽ¨¢A˜¢U*󃞌Èx×V–Woœè¿.qWQF[s•Ô~Q{"OµW}ŒF’H^-îåQkî_—ŒÖÐÃc ¥Cœè•á£Ü¤ÿ Z‰Ôs^‰uÔˆòÖ”ÜÁÚÖÅvÎb²Õ­íAt<Ç¡ÌX: ¯HioëVôñD½û;‘Yʯ–‘û.E—µ}ÛŽdØ#]–: + wª5ˆ9 ¤¨ªòõäðo#±‘Y££*©5S],;Ó5Æ/YøúëeA¯¿u9…[Õ|Ïw¼÷¡ƒÇýò¢ù× °ÒÈ6ï)Ú}!nszg~ ©û-ÁzxwÊv¼/G,ïãõ>X5˜©Þ_oÔ›š5å1Óçí¥qKlyĽBº\rÝÅ9# íu2”"®²âoc`àHh˜š›LøX¹ÅqQ.: Ë—upgÁ·èŽAàô¡‰„ð¾RCÄš;ÝÞ '83Ã_g48'Äæ¼—glÓþ8Kl]‰ÊÚ0z$î¿:,CÿÁu5pìLÒ‹À—%M +NšÃ0waáÒj÷î¾ñ6Ø—ž}NŒÍtùXøkÅkÞÊèeüçýús°ïj\¾«ejïÆÙëÇ•Ñ»í*¾áOß«Öw¥Êß&oøô:Æ+1 øúE|™„ÙËâ½á¯)ÌtI9͸æûFû¤Þrßbuw>zÙ‡µáÓ/»ɯð*å²xߢgDÍÞè7>ÿz\:ŠÌªc\.Ý+¡ÄÅ0vb÷‹εŒk³? +Óm']LŸÛþìk3ÿöÞl¹®ãH}w„þ/ŽPG\À5­' ²úú6=„åöqlj +E´1ð€ %ùëoåXµÖÚ›Ø@iÜv·I& +µª²²²rN Æ„4B +¿8–³œ(%P²=V_¨ÃOz Œ M "–cí`âÍ\H¹[;Èœ±Z£÷3…-ûÑøÄH†œ%»)’xµ¶Ì”I¬àxÚÊÚï ²é ü5°:çüFÃquƒ»®rá6´…%±y1#b§à®ã±kï^š NHlCЀ`‘·¨'­5ž¨¢•<ÄD£F{å`b¢+Ð×2¦’kÒH*è¨ aª*C8‡)Œç<¤*UŠÀš2SC#Èvè)‡ +ƒ½Ü[€Æ¹Œ·ÏQ]É©¹kq~Özi…¢/gBúu¶ ³ñÎÈTNÍÐNÉ”|Ç`E@÷϶Œÿ%Å#~ ±h^ +ûrOÂ:àVße´‰B‹•hÚÓ«"ðP5p¼}Zê žƒ>¦P¿Ã²‘ZíÑý«Ï¾ÇnMyeµâ¸€aU+5#)ØR´ÐÀ‡adÖض‘ÁeÄS¡¥Ï&»HÔfiz ¹m¤ÎÐ9LÖ ››NŠÁíòd ;О´X]áÕrÌ¡2ë‚kMP6…3óXß )¡ïÌš˜§xœŒ­v¾ßrÔÕ"š]Øä ELl÷0˜üƒðN=92æ[ó;lVáj¨„âžrÛ½¤K-nÛcKàÞµ“WÿRàÖÐÃß"í…(%ÍD„ÌYïm¯¹³ôð3,>÷ß¿˜ yÖç¦*+pøz¦ædVMdý϶Ìóè2¸ÊÚòåK•©cö³•ÎÆIÁÚ4Ïc?^¡ìBý$/ç¯Ãç7Tqý~M|›r:†?›óƒ~ó/n¸¥¥×¿Y¬ùCÙÒ™ÿ¹xYôº ›8+—ã]úè¯ÕüÈä½)TXh"àçØŸÇ XÎüØk‡Å(|{X{WD†eNLÙK §iàéžúyL&‘ÖÉ2$&o¹¶¯|òÃ5^]Ä’Ž÷ã=‡b…|^œÇä1‘ó˜ +$ý}œù’D„WÈÍ…¨LÜ€÷É´qЃkû7)§ê‡µÏóf¬k¿ Žóür3,xëÀ†.ž«ñ ’Bk»0 ß¹ïrž_RE:Åò$Há’XÐ:•úçðé4¿Ü)88'=æRÀ\ŒröFŒÃÑm½ÎêÐcuÙD-î™jA‚:Õs×?/h +žäÅÄË;Q4JNí Dß«™SpÆø*J îC¯PÚ€d½•I6ÎüèÑ¡¦ a ±àRà+•¤MÑ©$ Ôw¦¨˜Ä5· ˆ]:èKÔðtu@X…å„Ð`46h|)ÚD[× æ¸p×DÇÕ…cô^Je`) ê(! +ÐÃx”£GÝj\-Äã"îÛ~±|éáÊÚ€˜\ÅkðÐÝ0A3¯¤ÁøN«øÏÐøئ&0ä¤íölïÅâI’bŸí œ¥ð-,E¯3»r¨1t:ñ‚Œ?Vã)àì½=)Ê/EÍ£ìr Tóm +hƒPGκè`ù½u¼,,Lª3°¿4Aÿ˜4³§LMΈÉ)ˆ…àÕÞ3¬ +þ{7ÃBån ›ðø¨'5«å /JÃÂXƒ»ýùü³ß¤Ç-þ]Vü;ˆ&ªÔ·5´£áF¹:íLKàg30¾-ÚTgÙïÓ\m.üÝ&‡^¶ŽÛawÉK`§·ÔW‹¢fi=T©¡£Á¶w<ÒJ46±pÍõ÷¥¯V¡/U0ŸP&ûŽÎêׄvj¦êÖ‚ß°‡w2nh@RrJ;óƒ¶p+m:a½okƒ{još³ÔX•MZêªgìˆ#IO$xMÜK:K;l³VHÚ±‹W&(±,Æú"ý¤lÔ%̶ðèHrTR„»I»¨Ñ¬+—†ØPúÙJ#-ÊŸA I†±á}’PßE—N¥QhÚœ­L;] ô´QZ:!d#‡5|`´B.ßf¸X܇h +¥xä9,aÉ +·•zBë*}°~Ý»PÌM¥ù<¨+‘á€Sn£V„ŽføqŒÑ†Ô\/Š}R/ü¹“YÆlE±W|P ¾€™÷M%®‘-¶c ž„Ø£Ûõ«‚j1õç€Rë tÒ¦“êf 0P#QlY¬í­¡qA1Ò:ØS_C¨âe¥Ùq{ðé²5AÄX&iã5Ýl–ÖÜlJúYi䪯d±Ê°G*ÏäL°[5„¨1sû,0°7ã?£OrT‘îu¨¡ÖFP 9˺ÐOË÷=Þ„ÚÖN0zi®›Š¼^AÑTyNœ·Òº>±¿ÁÁ\.]P-&)"9« t.Hß-mið¾nU +Ÿ35r§$Ìp¢Ç«HSTÄ|>t…Å’•´/c¤&n"¦G¦V@¤‹iî*ô®€rôÌÄ«%ŒB¸¿ç^™ð¿i,„x'y¨é)¹cs¿}‚Ÿ´W&öcfŒ‚€¥ÝiÀ~`ÄDWn%ŠX ­Øt­KFÏ$hË"ÝkÈx.Ôš‰g0²ã¦ç>ƒcŽ-™mæ-—ˆ gÜ…F¯kù=ó\ðŽmV©ùÞ¬€sEÊ$|8.$‡pnQpê2•1SºÍRñ”wî2VÏ'€¢Èoܤ¢xÊÒMÛé Òãx†*kpQFLdËЀ™àÚ$–šÒ–½Ì€¥t6ááÑù}"µ›N½·†ƒÒ»Ü»Ê[ãk¹ÇSHFî½q¸Ù3t? Âï-¶ÐVºZƒ[G­DÑÔgÙrÕFSfàÇ>—é‘·ÐNfÀJ8äâô”f$—+æ»Å0[ÏÀƒ´ŽÚ`ͪ?㉭YH®·U>Gm¥,Q&¿ì«3¸^ªÎE®¶éÀª Ê~s,D5 }S­WúìxÁ¾ö5d¥cnmÅ N °·;o-ÉàL’ WU2—z§‘^Ú6SeZš3Wá-©Ö;L¾Ç¯¡K@ïtl©Ã¨ Ô|€éQÛŸZð¨Ê ÒÑÎÆáÖQÞ£—[çª +’0Ø ™ÙcýÜà¶5DŘ4õÄ­Õ<#ÈwƒQ‚äþx:ÞÎ2 $-Õ*hiB8¼Ù®ÒÕH…¬5ÑJªéW2ÑûÛA@4Ió |4‰øRéNe,J@û²¼T Œç«ß‘LÝ:y í_IÖ5´ !‹Lå6ˆÖ:ª; +´¨ÌÌÀ( +Eþš®‰\Ýg`Ù fL…ÊÑð²¨ë"£<~šà†ð¨ø¥~S È1$šsf[I cy=é ö"H—åºnÃ(ï@îê OºgÈ-J»àîãÎPŒ}<­—ÁX8’ž»¡Bt°t€—Nš;¤…åç, éOM[tpfí¦}Õ‡vŠŒ.†¥IªhIxPåŽAÄ™Üj´!'}Ç +6,wãƶcAð[ÐF °öÎÕ‡±ì’VZA8>dÇ2Kól¬¡þZ…RŸê[â<÷B­–zsÃãÇ]„!_+ð¬ ÙÒ…òLE½È”äY +3žå¦¬Îð¬P˜Sz_W©1MÒ‰Î@õqÇ%déé(,hËBµ¼[]ŒÒµQ²?‹·¼cãUKÙ‹uˆ5°CÒ"tm™EA­¤«z¢Ý¸PÅ0âû&À[ÙS +±aè±IÆgXWW»øá„å¤KÐw6çXf1ìD:2d\# jÉpÔÄ3 îF@f·šü„@f·`¶ª*ŒAõy(xÃ5#ƒ³ ÜvWT?&åp|:C¨‚#Ö³:´uwÜÞ@ ¢t—GÁþTfØ0xË´ÛÖ°qÁ·¶æáxmêJHm1”aéC´ âˆ/æ±XE—©T'A€$îW½®†kÏÐy!Qñfš™»LcÎT”<õ¼7]‡"‰Uí£haçµ™Ä3dÕ*5ë& ¸3j¯¥‹pÎ+—èBä5$ª•·¥’żäû !ŠšÆÎ5ØE¶¢q£Q™÷k-ËÄ”:&S•7°Èi‚V(öŒ uÜþ.ƒ½ÁGäˆnT†D¶b½äª6ž6jz2)ý¦ùj¯Ï‹@ë1˜ì(^—”8g…-á´“ =»ÅÒDQÊÄÛIªY¡K-5D j:§2ƒ°…¨»ƒÏ‘^¨À4Ò•àU"-̨çi çµµùRaE,E!Å'·”DßJ$*À®ŠÕˆ=Ex5 U}Ô‹WkCIÄ æÅóž|Ó n¹Ú$% ü¶dž¤cíÏìRŽŒ>.ú±‹Û59*uÍ{À%$0UWÞʦ‡¨]¡+‚&ðY\Ëòƾ‘@ÂÁ¶Oj²Ws[ª$JÓΪ“h«\iÜ¡„¢½þHÖýÕ„8«‚ªLb•Òd$ÛZÊÔsŒ8E|ßR&÷ Ñ?6ºÆB*¥cË #ob¹O ÉýN@ö2Ãj%„gê5ªµ€†ë‰ -×øÇSÇç4Fu/0ؼ† àºXxËhÝÁicá­5 +ðŽq®µ]ÃX˜î¹èà,l‘kEa„xjXuƒî Q+D +Ô.»ŒfôäF –%C|„À#±Æd~‰±Ž3Xc |©ñ^n1Ix‘=†´ZðÂë üéù—Ïù(·˜Ö°$êÓ%³Å›P‹ƒME)ZªË”Fbqû‚ðIòŽë˜µ'4iÃ5ÐíDBc¤›”ÎbN(†æpÆ ƒ 99x9†@Ú q¤,õ؇Ë=(B{ñ c,…h96…Èݲ㾄Œ¿{¥âO9 ÅaPý_Un ¨Ñ~nœƒÿ8Æüø¸m EÛ–h8CŸ— Å!иfØ@¸^øNA¿vº’ª‘V¼;ê¤áT«E[\>X$Ñ\@ +ÿh":?Tì0P‘ ÷ *êzÆÚ¯8ÔAu®CŠb ‰?Ö]e›[dÀzàºɈ2b¾K.öT … è;•dgèUV]™5<[»™L·ˆóß2[«,ÔC̶_lÄãß1‹Dès4cQƒ€i­^šÎNÛʉ‡hË«´aï¨{}¶¿Zzè '7‚ïÉ1ØàAB>c•‡¢Ý+`7`íeNq0;‘¡x:‡yðM£ê¸ÃÏ!™ºéžC÷îÆ‚þ0Ñ3<blÐÆ!é5`¡'ÍGyNp±F²à)u±1WˆJ"óˆ¡Da—Äj”U$ðY•êä’„#¤@­VÚ´…Ò©V“YAŽ …¼{{²x#¢Ç®2ëXòÈrc$:b! +uR…*¿RÀµBÂsƒny  Àµ8>ø,@ôÒ6î†02˜ÙÈ + TÉ`›!ñ›¢%€{”ê6²Ó¶†ï·éøæàw¸º=øüË?Xspz}ùêúÍÕóƒ×/O^\^??›åL ƒoXo™ƒƒ‰-ðhlVmÐ4Y|œÛý)û<lN@>DÅ7Ù³»¢0œ°]¯DÇAÒ;õA¥CbdÑ4õAžm¾Q$‚"µø5špÄp’#Ÿ ßUU•äö°¯©Ç½„D‚è!ÚžØ+å ä|! F#~Þ…N!(‚¥ÛÖÌT“MŽôL!£$Á>õPyÀªQÁgêgˆLÒBÿBw°bnNíh =„× wSÑÿ, ?Œm®}J¸¨¤×ádÖÕ#[9ˆLøñµNÂ|$7°Q2εÏѯ{ꊎØù+ÇÇÆÊyŒTÐåÅHJ p’ŒOA;¼îüûpE‰ bH,9ÇëGpõQ*´a#ÛqC_¬AÄa€Ó:C•ÈRĵ© ûþòN·Ì…Èð` ‰Î0¦_Šw&Úà òKÌQqÈ.\@µ =<:ýu–±IÊJ­ødWjÚ@Lb†…¥WNÅW‡­×kºH>ò*×Â.QŠ®ea›c论œ±n8¾¢ƒ@_ËéÖȪxŽ@ð{|÷ññ`g_ÿPÈ\„EŒH 6$NÒÂiMÄ8Ã{IIÜ^hpâ(3˜Á °k 3,ôäÌT +Mcròq*m\5²„œ-5 ªu ÄI:C"ÿ.JžÇ²*-œ€HI(&`{§‚vXÂH–xhåJ¬ÀêÚÈVAb>˜ãÀvÉ™Õô¹D¢[Dô³YDYÕUNÐÁF¢í…n±}Ø&b>ÝBäBÿð +S,W€¢J‰jà“ }^! %l_qÏM¯%ú)(î ¢ÊÁö¢þ,,àÒà\â…„fÑðÑxFãÜCD‰ +\¸]°Cÿòxš’Še€`+y9ÖžöWµu+=® ˜c¡oY} 72zð‡Y‰ðFÃ[kÆÃä—2¤èÅX! 5uµW”Z=¢‹n%§ÿÅ.î\x›tËzx›²Â»Ò§Zl€hÛÅ2Í—ò Gzp†.@2Äc‹ õä¾|>Ä@.u†'=¬@—ýbËWuA S¢ØvëÁ~¬' S±âBlb‚ x_ôdš\•£0T¾˜Q>˜ñêú¾À¾HûS!”ü¸Ø²He jÃ×.Pˆ¥Œ^²~oÁ4*ÛãR”(%bð5c +„Lä¶m4 +Oj÷¶($îVÐá¤'n²~!VîF…lÐÆ÷®‡{Ý}í +—ˆ$Š?ÅšØɃ^…ãSøÁt€> ˜ígr:á²Éúú骡7ÁUƒ}ÛêüÃqy øœƒ µJT»ƒœ­0zF}àˆ%T4•x‰Bੇז/$6¬Zj‡§B9pðÆž£Ü_Ì/W8³ñ^'ÁO䤙p‰gÃhæù†îuËý*Ú2Qò H*ÏKdOGÚï)°ôoÉ>‡ÎLT K”®ÚYó3n=ŒEMÙJ IYRãeäÓÅtÆPɉÌ-BW®Ke´Îö„+Že¼×ßq< É<4›:Ï‹TöGiSx^™mʲaˆåë¼ =ÛÚÑY7ì +<_q± r²ƒ5zº›ÚK@Oày|(*3 d¸Åá²û³Ÿ ¿b¹9ˆÀ¢ê˜€Ÿ KíbÈg$J˜Ù\>‘å?[0ØG“ÖÁçPØͼ¸`øÜåü òfF‘m ~{÷ЋÉJæÓlþضumÜÇ‹i‘9|$Ù +§œU@íp`® dò0f.¿.ó<Û2?¯bŽ= caÌÁîpˆá3QãŠv¶Áežg[柦À[Íc~ˆØ:€I’p ýŠ3`#xãÚ8«¸>‡ï#hˆ.OÓN3B‡ ;púõ–Ú'í{šo_}ºý'Pê¥t!S÷ ðÉná}á ÂÈšÍ5†ü SÔXn°;Yè÷=Ó*p¶†¬ÓŽ[[àát{±ÐÙE©¨ÛºÏ“ªZ_€“åuø°—>í°ë¾†)Š §Rˆe68ˆ?N«ÀÙ:|X°N;nm‡ ("B¾œ/NCúÒ:yÀ yðNÞäJÞÃ畼§˜ìC;ʇ9;pº€VÛgÕ]ͶºéA|üÇÑÍÇÑŽáv{"ó]1EÞ9ý—„Šq⯽ÐÎßjÛbþ셵騭ü»M÷í#bÈ´ +3 •„¡ð  ¶¼„>&é FAÞAnt*°¡p d€€¤gÓy¶ÁÇy®ø$¿^ šã÷ó{sÚÞY´`rFpàÉŸ4JÊû£’ÈM²x| =Šs“ ÏÅ7é(SÖGˆ’ï²F;C…ºD^¦˜§ÙMŽÄü}¨Õˆ1.ROCbz;úð²Ñ¼B¨³—PYm3;çÄ1å* +e®''p’#‹rŽŠ%°«l1ž=ÂÇî*ë*Û—«ç8‰D%¶È ßâÀG‡Î&›¥ ¸GØÇ ¹K•âÍÀ—iP„i!/‚C  ¤2 +•ñ'f(Ã]‚*Œ¨Bž3Õy l8A¸$‰B@HE]IóR4€VSí¼—„G_4@ÆGI&gŸ#›~[Žµ¨MC9Âî#†/S^ð\ƒÑ.^«qeX±q~é{€Çábë`3.À›#4)qBàrVC.x(t&p¾Ö!¹S)/"°Í†¨—… øq(¢’€«Š 8jàhû@å`¼èÄq&A&cG{%·1(ŽÓàá[”³8çÂÈ7鋆¿F(!A>‘Òã7]î6ÁWÀ¾Æ/p~A¿Û $[X•.D3ã2ªè@C×û¯«$CDP%7 +ü5Ò9D R¤ðŸ!Ž‚]­z‡Áî‘xcQAƒá7"݃%BMZ ÐÝÌD“¼5"' ,bßÊ`Ž`Ä𺛀ç˜HÚÉUüÍÖ|ŠC¬~Þ.ÌÎ5jÁ3 ñs\¼K–½•ëœ¬ò@Îm„À²êØÉ9Ó`à|9 +œÓb©–‘Š5›éX?ÿjò-Ù|uqvõ¼I/ÿðµŒùÏ­cþöó«e@¤äÈÜÂ[Ѿn¬·@8SÜ‹\6§æ' ­q΢nOÕÌ)…ŠŽÑÖøb¾4Ž¢çà!fæ“Q„ +y=$'a-zÛ^F Ìž‚P +o/*©¬PjLÀèwáLb +‘'£ùQù&$"S®pbl;“Œ““^zì–ű³=;‡DÎòÁixŒA)s°ÊjáËÈ[ëÐ#Æ\£8J‚ÛQ*ß4ëô:d<¨§‘¨Ä†v¡]r™ ðwC·Ø€ þŸÂ“0ÇüK¹u…²w€›Gý4WJƒK)»×Ð ¼Ìçä0ì¿’Ø'!ó“’*K´\+ €˜WB—YCÛ¯œ†‘D™² ™½?âœtˆÐ¥ìH¸Á=¹è”ân}¢,"™È1‰ž¨Ð—3‰„f<*¼ØƒÀŸu0…ŒÁ­KSDyè‹ ¹wòEƒZ†•Mj:€`=ßaÔIõºTX,¼$0P°¼³YM3j8ÝB%ïÊ¿~ÁòËÇ,ÇêâÀð9Öí4zB)zùx•»ÇÙ(a4¤¢6‡'MîBѦróp´A]ÈCg8Ä †yçó{. +O% §r@Ü>”ë~+†9auÝŽ-0ƒ£KkƒÅ¤…y°¥’o<8P>)^W©o•ä(º€@‘<4}Zó0s’¨Ïš”ëRÕ\e·§ºŽRE5æ6"NELQÈÔ(éçí–Ã}$¢ìÕÚEå +Àä!R¹£¥ø[õYôhæb~.Ú=@E a=à” 'ôÀˆÅ¦ä +V‡ƒUËq–S„JJ +çH]H©b&‡*ViHº” „…5 ‰ùí’ž^˵kÞŠ¡®ñsOðÂaùðP¡ÃuõÞŠE­RR»©Z#É«mÒZˆQå-Ò®¡>Ç‚HF5kIS×™9îÒÃ6 ÃC츮1ëÁ„Œ¶?Ç)jY/Èòp—~€F@Ó€ö¤<&v$àÓ¤Ú‚  —T†¤®öÙY/s Ê?¶©&‡Ë«e1Ð}%³%2X• ¢¤³b ¸+6WF‹ +endstream endobj 219 0 obj<>stream +!T`U KÊLÑäíÅÄoëçêq?`Ä+]Îaöªhµý" I/óv,•.§±N/2ˆãTW¶)Òtâ +‘”Øn»#-²4 Ž“Jç¨×çÓ;?Å2ç¢>ÆÀä9Ïo¸'Á깞“TvŒlÌÊ×Á\P%tƒÉG2! Q2kœ —œ&›¬t×D«*뵘„e%+È’ žWÈuy+“pmt`}â‰Rcä!à‚ÝÂù +VóÏÁ”à±h#šÜŠZ«ä3:È&çÔ Åšþe.%—ɪPçìÞJÖ‹œ»:9#<穲A8P?Xz¨Åö4Ÿø¹”ѪëIf¸«©X¡zæo„*$‘“R«<•´ðA86Åt’ÉCÿ³:¦HQ“ze5ÆSE‰w}ÑŠž×o‹PÊͳ/ÐtC-p¾h83SLL™êqá’ñ^¿•}§dX3–\ß%’ÿllÕ³}4Te0b†[©>âYŠl²8jPAÁ1‰=UÅlÌBCžãôŽ·ú²wu… +Ê\9sÊCx4[L’ÔÕz-aà ’ Y¨}>ûÈ6ç5¤² ­å]Ž‡YlýqþfuÇ"Bïâ‡.²êËÐ#híx ÝE¶á¢§¾;NÁ„CõA=Y 4•Ü#æá’$ïœó“@Uóˆ•Ï,j@ãZ*~¨*ë¤âd"V.,PÄþ˜Ëpª••T"xC£‘Âoœ3eN8_’“8kÄP‚µ¦=± €]ÇN’· ¬ÆX1 ²ê‚é$Z5,«d.Œ½Ž7$¦ÊÙ–&© Pˆb¢¥ö5êçFäÛQ%ã žßÞù1rv,Ü7 +Éñ!½(Ýž>%"xW`$PSfÖž +ž,pTÖ#=*É·h78m¹÷Žçf}@DÖ.ò]ƒÝ—ËqºJŹQ Ùˆª¾'ꎓÀÀÄg5ƒ>ÇF©@QÙ1i5õ%ˆœé>«\Ž0 *81’(ÏV¥:G7Ô•Ý”•ù»{+ï;²áO9;³§O ;¯Wtm¹pk(ëu©>72 ‚¨Ë Ô0椰1¸p¡‚-g«¶‰§±]BßMЪ•Ž‹ùÚ8™ ³½"‡•’ëè>æºýF¼™¶‡-Á TyÃBDxj”y +\!ƱcŠnT‡u¾’ +ëP_-L”ÓA3`õ¯RɈ¡ýœïIöRÛ?I먜¸F’s±^¨“Ë›…²õTë^46Ý@y*O¯ǘ¤õX¼«H`l$ïN¯Ì¶<Â÷÷ðT)`g¼''Hë6T­ÙÄâ?T×IYÚr4\Iý5Ê@Sª Xg¡*r’¥Þ(ЃÔØw&ÛŒÀ<ç¤VV÷Tƒ™å(ƒB)´‡dÓaó.—ÄS—­ ’¡Ë­½Ÿdø)bfG®ô‰¡æ ?DBØ"ì®Ò6›“z ¸c0¬…_Î@ÛvHú“—fo[Àš(5™óYï?Ô´‰`,†h*r”´ ã³"âÒPëVõó¬M ~!Ú<ñlGÒ8ª¥2'p˜ŸB& *fÒtÿåw%­­ìõŒ±“u\©ãŠ%ØVDC¹ž1~ñ… ðÉ<‹ÄB,—c`  VÔµä +Ä£eÃ6/E‡b8|¬›åá!ôa Ï^Ð%óhí…àL]À£‘â_R*Q£wc±Î»Ð‹_\¡#¿Õ•bÿ‚*nØl뾘g zï¢ÌD …Ò“tˆJ0‘InJ|¹×PŸÀ¥îü@“¯Ý…+¼ŠT–zBŠPí…îã”ä¤ ÄZL±DÓ‡àf‹ÜÍûq³˜µÏ”·P“k‡ï‚ÈÌi †9H~ûÙ–YïÇÔR”ÊÓÕ pK­ ô0‚Ѷ²žÅÉh “³®S+ª,¿{%a;Â:Í…õÒ¸›ÃëGxŸ|>Å=ïZG¼\æ^Ñ‚[)L.96;§J f,#YÜ\ou淔ʹ€÷ùßͦ²¨‰£à´;÷A›1`ýÂødž_æþ-2ƒë +&»ñ +:?¿SÈ´:ÍrNº€“2_ãMÜJ×I +;'h=’ô˜ +ç2\ãùp¼Ü´[湓TmV§ž’¤Ÿõw¸ƒø Ñ%ôpàwKÉÆÇp*PšA7w:ϸŠhÛ3ö13y8ÑÌ%‰0P˳ªŸ<;Å"HÇöGâ©Ÿmùä³Vv(>b‡§<Ùý+n,0íæ4×Í~…ò yxH¯«,™ûa+ˆÊ½• X‚;³w£_ª~Ôë/ß@[•.‡5¿ˆÁueIpa9·H2 4:)Ú:yð¯Kwö$‘Xí·9X˜Ž…Îñ7‘;²@pŠ`oâÂÞË kÝC¹¢´È¿T8•(ƒªQI[.%#ÕdðÙ<ÏôŽšÁ¡»;].á˜ÙB̨R¤ ¼Ç‘’H¥x,œ+“éMfÞ:m/ãcÁ¶'8—2%iP5È^¹Œ+¡'hœI†¾é>zƒ–Ù掇}sŽ$|=úóM%)äWô£ÜÍJÆ?Û2Ï’oõrC% ˆdZ¨€Nkäz8X[_ûï ¦°Ü^éH³! $hªà” ÞøñŽg‹‡ÞHLÌ È¡a’*¬›LÅ|ÆÃtR2s‹HZ•ÆÞ½*´ÒtV£ŸãŽ.ü9ËÅvè!áœA¨´c´„D +’f“r>ÎKT9Ò§«bï$t +'ÿ±HÄ|õÆÁ‘ê–â`)Æ #¼2ûŠñ IR¬ o›ˆS–p”Š£´jÚפÄëv,o0F[OW¹>P´ã&¤€›ð’÷-YÙ†5@õUB"´z0^ú]ÉHO)@Ç¢‰ +~N@î…åØ(Š#{EŽ£h¼c±t¸ôHÉ^Ëmzá¶Y“õ¡¤iÖ¦ÕAròÇ «¢ôä(á¤'a{iì3çùŒ±Ï f6B9¨:7ðñ¸±a;ÔØ­±ÎfÐz™Ð¡(ûl&Í„¾`|”P§9ks;ªØˆ~²”–ÃD¢1M‡æ³F:6  M-—- +õQo e‹ð T±¤-Ÿ¤K›`·Á0¡Ø@tÛ [¬Õ8Å,Hoâ‰gèí+= ´¥l¥™¨v䪒q¹ŠÖ|ÿP¬:Gi²ÎÎ.‘¤q©™tO¯Nú±ñŽ±!¯t`”§aò)Œ Ö ¨÷V"©€Ð”1îØ'7“*à÷äß·’g +M×8{Xm”‹|$JÉÁá6‡ÊÒéK‚# ÝdòÒ{Ñ)¸,Z vÁÖ-È×¼v?Rbø +\Þ§ÓT‘íiòÃç,Qï!õH´R™Oò(É0n–¬”èå" +œ×JIëÙðî#õ”Oõ¨¿« Šñó̃î%v€£ /êXÏab++VøKllLS2††ŠÜ×°“ +”î"ЛÃG!%{'í8>#ÙYÌôY£Å÷§ÔYQ•œâé ØK7ñ ‰{ƒ‚·“Pæûõ**öxªU£¬ˆ\xbåÚ˜ì"^kì@…_ÊÀÃ’ðÚx½ó8( +Ëý£¶JÏRv½Šä¥ì”s„Ro¯'­C'b°tbI"ÿé£ÂÒ:ˆÜ. +ôÒk`ù¢?2RW7ÒbªRI¼ÃÜôžabÓ`qéL-!×â3Æ¢0ôÀË–'@8ÔQEæÌàã Ey¡(%¤`éÃê—KP)E&¦YEkÉÚ]&©cKˆ_%%.¶•²*)بPjåã­› +Ç…*8t¹2@ᾧ½ë¥7PìÀªâ5Á¥®Ñb7PÀ›¢ &>Tû–)™à˜q k`ò•N§‡"÷û¶Ý;ÝØØá!ÖXUòجju…"sËØ9ÐCà™Î kózn¼täáiù8“ÛE03ýÂOh6—¹[x¯áX£ôŸtÉçnHæ +Yg‚‰RšUØp8’C²¡2:dz> “¼œ|«Ð£¡wWàN?‡=c™áÅòá´* ð`oÝŶøA£·@¶tkÃXG:>Ì1 Tꤌ¸uÕΖª$§à£ªWÞJƒ‚+‹D“†‰i“u7.aóà ³n[H:ADk)Û&°\!Ÿ«L°°XÂrðÆi·®*ôUB¥ˆp!‡é¡×€gr^àée7¾&R4›R‘aßkòÃYó.ÌfŠ¸˜çæãé•iØê€nr[ªÎlÇû&DYûméÀ4®m¸œ¹ÏÐ/aé—0ŒÂ,ŒTö*3‡ñzË´ý¶,¹Æñ3ªÖǵ&ö^:ÙJsÄ&P$iÓ¶>+ÜÚm­ÞÒ€AeZÍeY{˜±¯C.ÚàÅPþ„Úí›L[û4ªb18¢T7:w*‹\ ­a‚Cw1¹#†V8NúhÇîìËÚ +„ºçª' Si°ïèèÅÜúU°ê‰NY”‘?œ5CÑsË=ü…Ž¶ØÄ&«^n!ôãI^sëWýØÃhÀ%ôÃËYP©›‚W<šÚÓ+˜º‡MÁ‹¹õ«+S¤ŠyÖôuSt™`åÕàö2½Ñ“2ý\NÂàªç:‚!>“ÚØdʿؼ]£ñÃIAþù¥Þ&³zä }ÕL•cê_]Ì-§m­èéa£6ðÀÞGÁéIU™êP2+*ÁÍÅGŸ$?F™»!úCÊŽÕ&:}ÅUÉ]‡MAéZ h +zLn3Ø6]ƒ´¤+±JÀi¼•ÖÈJÀ&©+qzYõ+ÕòYOFû*Ô ±9::èæ'l#:½xa¸J±Óú¸Àõ²Ü°C´Ávp_ hnYÚôÛ»8b!Ʀ±H[ìvŸõÆHß>< +ý¬)r¦àª­œj_;4÷ÅÒ­tÌA:9mø¨é´£ †ÈTTJïèòš6sZreµžDôr+iÊéTŠoTö-ÔVV”U…`C†ëÇÃ,$±‡I™/"jsîe@†‚ªº•#ØåuxVf?¾[1ͳp­%=~§ÃQìÓ=I†8ð(¿ô4MÿªtÕżË-8ï,=ˆô걂ÔJ÷»âÆg°ßC¨L¦IG/ä11=»á5Z|².¸ôl AĈ€ç§zdOPN®³'½½ +L¢è‚8ü"¢âÀB‹h…vBÍ• +¼ Í O°>ƒƒØäÅÕS%a„W0©+|¾S¹,›ºùAO>nUU.K÷®±£!qù?¦¯Æ,)*mþ©tºãúW{8 y9I@¥¿O³øj>ªb8SOôKù0×,Gëµ~Š/ÓP#R‚‡ÂçYà> ¨/SSÖiªÔ~„KÙ׉ «_J½eà áK¥Ú%|l‘g’¼u“»j +õf§«]õ£Ö +: íu"›P¼œH£=¨Ç}±‘Šãú +!›ý)èÍYÊp€óñc9¾s`Ôîï\ûÃı7vüÓŽK*øÑë@IbVv c׊”j&Ä`c<áaã! ³`lðÔå²*q0qäAÃèÁëÛSm£ ÅÖo:|xg‡mO›ÒÅ8"cÚ¿̲´mh<տ䜜ûdü¨|mƒ[2>*ƒel£÷c(ìÁ™€'k¾x/Ý5ô»?]_ýåæüêöüê‡ÃC†£N;þä³ßüéþÌúá·ÿoÎ/ÚTŸýæwúwÈøÝ?þøìO×ÏÏà@ð&ðÓåÅUûá!ü¨È˜'°ùÇoO.ÞÐÏíÁïþpu;ýé­ÔIüÝ—77'?/¿|ö·77ß¿¹8»:=û…¾ÿEqúòüâùÍ¥Aünš@1Y&Móîå}~{×NúRÝ£.uÖÈææüû7·g¯q`ûIŸvÜÐ÷'¯Ï¾¹9û¿oÚr¾sg湦=ÍV?ßÜÕ›Ë?ŸÞž¼…_¸cgnMÛ×=ßÓëÛóÛÓ—;¿ØaSW×ßâè5ím²þùænÎ^¿¹¸Ýé‚­iO²ìÿçÝãæ»}qv|}ùêúõùíšYÞõ«³›“Ûë;_…ÏϯÖt&}Ùs´o]gßÊ·×onNÏþãæäÕËóÓ5íêüjÓ~¶1¯õÞ\óÝ×å>ôJ2Ì]ßÞÍ ¯¿ÿŸ³ÓÛ¯®ß\=o£¾ºþiM8šld~è;¼Õ¿]Óf6¼ÎÛ½Ö-ü´ÜÂçÏo·½®}Ö¬k#¼êùf^žÿðòîÇwm»‘e/Øâó;·òå¾ûòâÕË“ïìš6 ¿ƒ5þîë³_ìU¹®Ê½¸9i‚øÅŸ®Ï_?AenUdÿxº\XÓ¶öºÜÇ%™îu¹½.·×åöºÜ^—Ûër¶±6íg¯ËÍu¹UI¿.÷'o^¿>?¹úêâÍ/µ‚û¼¯oŸ}ööœ‹™}L +Âtå÷”¢¿ß~:¿¶}?y ïòšvÄÒÚûé~ñâõÙ§÷+^›Éë÷ñð±ÙÚîË&]àùÝráªÙó ráó»Ãuíá§{Üòo_¾¹8¹yÂKÛǯóPÿåúüêöÙ.b߯Èoî&éÃ&“³&š¸—ºsW¶‹ Ï¿îÞ…[ÛaüëC™^¿¹yqrzöíéÉÅÝnUAMW~Oòuãa~³mÜþyüçýõíÏ;PÚ¿)‡§××7ÿþãËíæ_I{ùyÕ½æ'ñ÷?½º¾:»Úűª]-–¿mƒÇ×W¯oOvÙà÷×WÿÞBÏÞÝñ+¸;>If¾ÂÝÜ×u³ZÛÆNœ§ÌNnÎo_^žÝ®ËEø`Npq~û—“ó­ÏÓÇÆ +žœ/w•¼Í-÷òÏmVšÁÁ·¦MÀz{Ø&‚­uvÃüÇ%hÂz{¸ûZ¬k÷yÿxvóÃ/õ0nú4ü{Åïò¼~>‡{¹žÈ‰¬Q˜Øí<áK»Ïò¾o·®Œû™»W¶‰ûÇ÷„umäñ=+ÛÍÃâ{¾:{{vñíË“ç×?î36Yf¸¾yõòúâú‡;ùÕÇ Ì??¿8Y—™üÁŠüªìEO6ºç}™Òºöð|u–ÇI‰_ÕSòx)ñ« +XܧÄßÁ³WE„y@×õøÜó]Õiì`j\>>_Ÿ¿~uqrzvyvuûÇ“W+~v½!ÛÜàûòè7d‡(–•]‘M,»\ôUÕ&QùõNÉ «ÚÆëÍY ?¿<¹º:»øöìâìt[Ø—kÚÔrõóýýô¾ûûëšö·\ý{?9Ç®ðÇ“6îNçñÚžËwíbEªÎ®o窔釼«bt÷•.Wu›\ÈÝʘ9ÿ,þj']Ó†esïÍß~…²“œ\_nw­·¾8¿¸ØÁ˜tvö¯U™‘hÙó×qÂ¥ßÕ}w1’_eo³],¨úæúòã +Í¥Ï÷q{ýqÙÃ`½ó=4±ùô×wGŒ~~q~uv²ªÙ¾ôù¦žßÍ+?Ûžš_ÇÖÿfƒprzúæòÍ;|ó£‰òjUÜm\úb[ÏŸŸßž¿½{S7ghMZÕ¾tí¤âÛ“›¬.~<ùyU´§+Ÿoé{hès·@¶ªÍðšï–³î#j¼Wñ¯U=`SükU¯ÙC4ºu)C÷ÔèVu÷ò<•H”ÓUÝõO<e]‡qßH”²¦Mì#Qžp$ÊSí´·®J?ûX”;¸öª\»xBWöüÜó ]ÕiìcQðLVå†zÚ7d‡X”•]‘{Æ¢¬‹¨î‹²*qy‹²EYïsó)Æ¢œ®Êà÷·s]üúžÒåªNã1cQì>å×gpŸ¸—ætUöœ‡ðºu±‰{òºUƽ4é}‰¹‡4[ìäš'±2§é'|k¬lñ8!# ðdߟ”öv¸²:s÷lÙ³ª=Ü¿„¡]×aìKŠÝðß___|µº˜÷}ýÂ}á¯wSȧÓ}Ç¢S«²÷í^rê^BãNœík¤ÙïV…—=_Ûóµ=_{/¾–Ö´ñµUáeÏ×ÖÅ×În®·š}ölm lmUbÉØÚïd÷ÒÚž«í¹Ú¯Mö{a푹ڪÐòñsµ§^²ª†ExɯvÆ£þnU8Ú_£5^£¼&Yé5ZŽ>ökôDÊ^Ýœ]^o-´²¢²W;ÆÛÚû…36¶ÿ5íÿ¿ho~Ñ~p°¦]!¶(ïEäÿ×­ˆYcm¯ëI½~¥Ö´³‡Õ“²ëªƒ³/(µ‘î JýúÓ,(5;Wg'·_ïÀίžŸ½8¿:_—CrXÿÝì^Ãø•5Œ{UëZYGó}3Ï•̽’BŽ¯/_]¿ÞÎÔÖÀvö@l%Á•¹vÍ“³ùó›mƒ?6°º;sϪ+ö¹O œÜœß¾¼<»]×Á<*7XU¶ëCRCWÊáÆV(àl¨*óÏmþšAƒ[Ó&`½‹=l»k݃ݰ‡mA5+Mn‡õ.öp÷ÕX×îóHîó§œ?½Ö'lŸ7º†¼ÑÝœ¥9ÿéìâ/'?·.ÚP~æ.ÇÏÐÅÂÄU½TÃÒï¸{ÿïÞÿ+›ÙÑÿ‹Ô`g¾Xåï}¾wnî©õÚû|÷>ß½Ïwïó]lgïóÝû|ßópö>ßéVVk½—þ¹¶ƒ¹gIý/Þ¼>{…iÚ6özÙ^/£íü|vqqýã?Üœ]}ÑHýì‹öžÿpýÅÛó닳Û/nΞq}srµÕ ú+l|O¡ú÷šö·×Ú¶hmûÖ¯{­m¯µíµ¶§¯µ}}ÞÎ÷êöÙ.E™éì¢}g'•-­Êä;¬{Áéþu~ùf—"šeU;ÒeßMj÷RÏßܼhlýÛzú¬ªTótå÷TÏŸ“µ¦}=õ˜Ì»UóÊ&Ÿ0Ôn7gUîÉÂò:!ÿøú +ªkg‹ÅÏw·“n8W£×´ÃùÞ[ùöÕÙiÓnö6£½Íh!lƒ…ˆlFl@BÓÑÞf´·}€ímF{›ÑÞf´·ímFOÃf´ƒfwñŽ-¬F¯ÛÛ¾Ö²£5Ù¾ìÓ3~½fUpe){OÜ&XÿýO¯šÌ·‹…e]”·Xþ¶ înBZÓY.¾Á÷4"5½yMû{°éI¥˜?±BO¥¯tGO4¯üæO´ØÄÅùí_Nη¾Â;Xq¹–û1„5:;?á"«rgÞ³ÈĺÎá~E&Öµ‡ýãø´ÇUÝú¼Žk|ìïû0®VðÿtÇuía_i{ØW`zôwù=Xýªha_}éc¬¾´á¬Æ#»¯¾ôkW_zšåVEøûrÓ­¬ÖR~qçÏ/^¼>[sìÎŽäuûpsö|uGóÄžßÙ[x]Í-a½‹=l“¢Öº‡ŸöÊÍcóš®ãºù̽ž€'r2k÷¿íUÏèGP=¿}yòüúÇUVýÝkh«ÒÐVÕÐ}¯¡mz žÆó¼×ÐVNlk»7÷ÔÐÊšöpO m]{Økh{ m¯¡í5´½†¶“†v÷uhão×´ ÔÝoÔÚ6ñÓr?ž?ß%%3˜Um„W=ßÌ˳]²“?·~]»‘e?ÈŽ±*7âÇoÇøÛ››ïß\œ]m-±¯Môtj½_™žïO^Ÿ}ssöß4êØö­±JÏ‹›ëË»£+V•èKkžïäöz‡}¬ª¬x¾‹O¶jÒ¾hÒåû¢I[6õMú¤* ÝGþ uß›7'§·'º>_WncŸvR(k¹bµõlñó½]½¹üs;Š·;DÏ®«Ǹðù¦^ßžßž¾üÛùÅ»ººþG¯io“õo`ª»¸zNW¥w>Àµsº®@€Mùjw›Öu÷ê&õ¤2V?–Â.»Üu‘Ö'YùdïPx`È×íÉÊ’Ç÷†Ò_„aï¤-ÜÞµ“XW0OTYXUÈ^W¸û‚­iOƒ¶°—QWr&’Q×F`{)u/¥¾_Ø˺îô²®-Ü?äŬk# yYÙnòÒuŽï¶ÕÚÙksŸ¸6÷´}?«ÊO~Ç.ÍÏv1ÞýŠ|vÒÀÆÌëJõØðÄŸ]´µìV»sUلúç[:ù×ùå›] Ù~U6R]öÝ÷^–Á77/NNϾ==ÙA¤[b¦+¿§€°Â¾¬{3Ç +e¤ƒÚíæ¬*÷u²ð…åƒ|}…/îǵ³Åâç»»`)æð]tßÜ/WVöa¾þ÷–æ¾å~›{qîŹ5QÈ^šÛKsŸª4·ÒfÂO\¢¬ÿþ§W×Wg»ˆ «b2ËåoÛàÇ)-W¿Šö&üÆ„ÿôxúJwôi_?eNprs~ûòòleY̤'úš6õÉDôíÀÖh¾ÛäÇûç¶ðƒ!6M›€õ.ö°MÔ\©0 +ë]ìa[ŠñZÏÁoØÃÝ×b]{Ø?ŽûÇñ#x׸û¾‹«•û?Ý·q]{¸ßÛ¸®÷ý~oãºöpŸ·qßkðÝž´»YýÚ#ïKÿɬø~Úû꾄χ>ÜÇ(áód+²®Ë{¶¯áóî}ÝÉ,>6lµæþ{4.®oþxÒÆÝ™î·vV~ù®]¬ˆ‰ïxmþ£-äõ;¢X>¶[³N6°É’ Ùá:0üßMSÈšv+;{o6±/òñÁÚG(ò±VÍqÿÖ~ö”jdüyÕfŠÛZ·´é‘Ú¡ÄÄáªÔ¿{Ö˜8\›¾W‘‰'å4|b±up6píÿ¶²: àb«Tîë1\÷[s¿»§(LÛuµŠ}qzä «JWsX+¿»§ñê’þvsrõúůUó›7W§]ñEÞÉzv{òýº^ŒÍÆ3\æßw3иƒUù &KoZ"û=‘­ŽÈŽò9XÕ–Lf_íÉl}df?jfö­Q ü£½ýߜ߬êÌŸ¨·7}¬ä<ÅôAæõ'tuÖÊ –Z¸ÊÔ¨}Œè£³¢ |·öQãïÃÐð„¿¼¸XRvovÄì{ ÑÞíºzÕÜ« ÒÚ6qÿ>Hq]y`¤•íæa}þ×õõónNÖ%ʾG¤ß~ùk¾ûýÕsm†„° ïþt}õ—¶%,„rÈð¯Î~8¿òÙoþôJæÁ~ûóå÷×äþ?;øÛÉÕÿö™9øò³ß˜ƒüøÙoÞ ÿgþ ÿCÿý'üU¾þñ3üëÿkûŸûñ üñàÿsð¼Mõ¿~ö›Ã\j9r>„ƒ«9²Å¥ƒK‚ç£T=ÀK=rÅøl±,G&ùˆÀtdcu42“Ni†x|Ž4³s™Ç#›K®?ÓàtäBE¸=ÊÉdÜæ3×ÖæŠÆ°}$×âJ2mÁÑþ\i ö¾Ü¶ü\¿zK@=N›a²`Ú¶gíÁ1ÎjLðxTc²08å£ ì®º£]E`U[x`tF'°GÕÙ@ð¦%Ð!0ÅìeZŸ}d káÜQqÁ<”v‚´.ç ŽG בƄJÀÒ0CÎÕ<ÕÊkˆG&Ð`wdjMŒ#Ž4 7íÇ4CnÿJ¸†\Õ8CömfÈ ¥6ü7mßÎÀ é¹Ò±ù£d+ÐYn_3qÖPCDæpTŒ)BgmbŸ`sm®†²Â¤S-ÛÒ’I‰¯XüVÛ™ÓåvBmàFÙ•ÇzãaÇ´£R: Ö0z½urb"äð½•ÁÙÉࢷ¥fëè„m"šn—ÍÖTxÖPô +5LñÉÇ£è‰"ÛÖ™kø÷p3«i•‰Êì‘«‘÷VM£ÔL3›¶K¼$Ò`k£c -íÞá´¾}Ffh8hðv*Á&þ\D¿õÈÃ\4C»ý@íH%ð mw|[Ú‡õ<¸í§07â9 o%½Ñ„a ÆdŒì +€mC C GÙáP3vÖîö‹ÆŒkÐnk[{û£a;µ?¶þ©ß¨%²®Vb„ϼ'ã‚l´-ÝÑ—KF‚©Æ·½eK@Ÿ³,Ý6Š }6æè6µsup}.º0äNOt-hŸÈ¡èTñ·ÐŽ0¹a )9¦ ä‘|¬îÌÏ0A¨AfðG!ef ƸÊ4àjÂììÁF$ +ú°‡ö‡oü¢á=4Lè/ iÒù7Mju)ðšó_›œåËYC,LíÂÈÓb$L×¢<W¬ŒÜ˜&s˜†S6–‡ÀƘŒ÷™ßø‡ð³v´ÙáÑÂůåÆ”½’jx„ 0a˜vHÂRÛ)×Zii.¥ÄÜåTù1 ú0øKó(þ\cÊÕy>®vpð-frm53Ê&àùÂSv™†Æs}EÒ¦Üèè€Xr H²ð·äa€ß«–­klÃ+ñïP»•M`ÕÎ;y£Z}ˆGçÓÖPÒ@þMÓ6üň„Ô¾Ú.œï¼Z^Ø&?WÇw¯B䂧G"Dý}WBfŽj]fžì‹a¤‡¬Àd ¿ºøΓéàÛnr¥cÃB!`c¨´††…†âEÁÄÂ7"ªY&üWµÊ“_/¸Ôw‰å†&@D!ã-¿(ÊD˜(±È3#œ%zÞ0˯~“xpc2…üÞ6¬T^)ÃKd,z›ås•Ø<sÖ ’cò¬ÂýJj²a¨üÒKO`»:Ù¢Óˆ.ZÊ2¼, c’i|! +¯‘Œ±‰9ZÎF^Öék{,p±±P½K<¶D¶ê Ëj¡öèÓF£Âšå]‘£‘V.Ì$Û÷•ä©[Wˆ>‹´€ö,ºÏ@Ç©¨}¡mœSb1à €$—á@|â{^>p/-¬Id¹DÙÄäô Û%¨$£[}CàÐí¢qÃœ…ÂH¼†]Ølbr…P'Ì`ÛE²L&[ÏÀT KN¾ ï²má;ÒДÝ%ºL ‘ô¼°-“gmr¶íÔˆ@0C"¯r>>yÚ ÇÛ0^úð&¼°$Ý€ÙU¾hí¯²‰,‚8¼]>O•ßèè’,A¤ë¦ƒ4^,x ‘¯oµ¥Dˆø” ZÅËÂì@¼¬$æ5YIS ¥è­ngU<œ¼\M;ʬ•Pø52ÅZ¦1 )=@uQÙÑÒP¥¡÷¬Qi(¸á +âB­,’4±‰•@Tz,<ÐBzi‡J<ä”H”S¢—;¥@x,l86©’&n»Î¼G· +žlòµm¤IŠ!Èœ¹¿$Môe‰P¸#ò +üØðN#føí†áXԎƃ*ƒ'ОHuQ gkï©7ŒE䮬-¶GÛà ®ðkĘð Ë,¿=¬…Ÿiž¤½v¾B ®c15ÙI/eajL®Ê5™½§(VMºöEä°—Ê´bt„Ì&ÔÈ35ÈMrDíé· Æ&§õ‡ÎG’§¢H¹ „…Q¡n¬½Ò ™"Þ´€>A(jRI£?G¤êÛ È¤Ú~õôvKùÛÁ™À•O}­7c€±Qxwìgò5Â{gX ¹ó·ßýî˛۩Ñç·ß}~ØnŽm7lŒýê«/OOß\þõú¶Çgþö;0î|§k™(Šç&<«ôA44…X"ÁL1Ñó:ÑM!·¼Á»Û(¢½ €‘FHuŒgëX³¦„Žçœê@¬JzR&#4²FÝô+ÖÌ›B¤gÝø)çí92Mã·³:?Þ„†;ÝdöwE´9øAÑj+K_M F²þF°&¦Óu²±‰§—‚G·§È‘Uxt²# +òΈîéˆã·ÏànUQe)ng½¸]C›3ÍÐng ©¢½Ž3ÀÛˆ| 谸š¡(ì€æ@À2?KF)ô’è‹`SiÚ?NDC’k¡…Ô:C,gè¦Uù‰ÈHhkOX`ŠlHN´¬¦p•A`" À.]&‘“˜-Li2¡çCŠ¾vÎÑÆÒ ¨®¤›ZækƒÕ¢Iȵʕ°V5ÖÆÓ3â¼Á½M¬u3²èe@Åœn˜Ñ¸ØÏ€Í{ ÄùKbÝNÒì`šÜ‚íꮕ÷ì“Ø&ÇÊíg£8†­X…(œ 5‹žË~/C~_Iâ2ú>b_ÙÁ’+qÒ¶ãĽÝ^€l/ihÀ‹s*‚p%±í¼dÔ£9ôÈ>“9ß©„Ó®õî:øDî·ª9±çR¶‹²0Ok˜´pp[¬FÖr2á× ‰ºÁ ˜ ï¬äʃÉÛ¦ŠHSïô· ÝDBv|`M$O ÀŽbü}r ÒÁ®¯DS¬ÜZçxÄœ·|ç*kÏ9¨åÙ›òX×+º‹räˆbùÈ}ð Éð‘;ˆoé¦}"G¸Lp”„výЕ[‹¾9ÜZˆ…°Œ†¢–ªë£Ðòìï š†êhw6ÜŒüwÇ¿oþ2ϧ{lGDm ’zó=$WmŒúíwø?$yüö;Œ–jÅþ8´ j + ›â¦‘S¿ýîmµíÅ'YnÅ ¼Œ‰ª­Ã`°ÿ™”៧ü»`zÉÓqh2.Œó °’å— ˜²¸´hÂdÊ‘P ÿ”ë‡q}#}¾ÉŽñ—·SИ°÷íË“Wg_]œ]=ÿËÉíË?|Í´óùnó7 êk£¾|ölã¸?ž@Èë¯Î^\ßœMÉOtz¼nz¾"šV‘ÐÈz¹ø ÆF±ò†˜Â64´†’ÿ»º0§èýJa:¼€"/Î1™¸e:GÿI"'Ó81˜Õ›æ˜&ëP ž­,{>l½O¼ÄÇGüå/uÄ_¾¸=»™Ÿ0²å"ñ @´ ˆïûü¶ß#9¿ç÷¿å¸ã÷¿á¸ß+½Ý|ÖMo·jrª¡”1ø•¶”Éö–ëàÚá /[¾Í O$?¤DBÃ!ºs@C€z’MhÈ“Á`äÍևɴPÖ fb†×".qŽÊR¸‚®@`ýTSŠ“¡}[}Ê% +X±‹B˜!°£T(Ô—\ᩨ(½QYî€3•ˆÂ,-?Ô`à̃²ƒxuTÚ§€²µõ3°ï©Ï·Ü¿bÏAá{-5ÇžQ"À…~9ƒƒvSYRn+"Ý9œ—Ãwõ-öP&GüEBšè&Ó@Yƒbám·¡f‰­£i+-'KXG`ta2´ïk˜s‰E¡ e†ÂN’…Ý%žA8¿œÁ‡Ð ÔsQgNŽƒ¸0¢ @ ÍIíY}0zë´PÖÐ]E‡ÁÌ2-8É+ã@× ÀŽD\ð8¸om˜v‰Á¢W¦'H &.X­Õx”`;Jx.è}A‚Y&óò³GË=Ãzp“G½F‡¢ë*¡·N§`òùîæ"8DîJx¬L +(IÎÚɨø£ÅŽƒû®†i—(PüÅ Û½œÁ›:`b’G%$ôx9‹ „HüÊ@ƢƒöÁ軯bœäi ¬A±(–ÿÜp5Æ·DFL£p7Yƒ;qÁãྵaÚ%‹íËS,úêê ‹Iý!í±Wéå 6ZŽÍl‹Š–,[àªâwŵ‡7¦ÈX´ä_™ ®`¦§¹OÛ²žAá5KHŸcYdÕ5(P±H ÷­ Ó.ñ X”N±¸¯UF7A±‰;° 0ŽHªVè0kŽaŠÀ¦~N‡bl=Á2ãâkHÃ!÷®s&Lé`Ù^¾¯ÀŽ½fƒûž†i—Pì-žeÈþšc0HÃäŽ=²ÊóšjVKp‘ãw¶æÈ(Ï ñÓÁpuØWߧ€¼†ÅCpáìð‚®…q +ìHăûÖ†i—xP$Ú0{T†Çºc±-Œw€q›—s¸¥@VZT +ÌÌðô¨@\/9 ¨XtM1 †X¨àãtÚÈkP,2Âé@=§M%I²vÇ.xÜ·6L»Äƒ`ÑV3{Tš<>×ó šK€Àÿs9CŒV7ãÐÉ`ÁÑ9‹oC€´‚Žò< f§ÂtÚHKPE›ÁcÌ™^:+ 9ãØqˆë÷ Ó.° (Liþ¢ty§£0Ä(á±88ä3FyQ2[ù›°PÈOPÀãdÃT¢_l2¢^EÓôi ¯AÕE†WŒ&$Ò´€Œ× @E"-xÜ·6L»Äƒb1¸ù‹ù®s,b:EÀ#u9‡;±¯Áš(D$ƒô/I¿lƒ`£·<ŠšZΓ)~P)Z ¬ä%×I!š¹Ä& P`G`ª³Á}WôK(/r—…ñ—+È£—s¸#]”Ö$N0`$$Wµ×³ ¨(äô¾a0„Åk˜°L;y *¥3 —‰Ez™ð +YSu +T$Ò‚ÇÁ}kôK<ÊfvCWó\ßkŠ·‘ ´C]Îà¨Kqð‡—°ˆ0Uv)¦A€ŒÅ,‘"Ã`H™5ÖN§í@YƒbQà©DnNë9åY× ÀŽEö!ôÁÖû´K<(³©»c1RJ¾yCdA1SÐIl`Ü*¡E¢ÀïM€ŠCÃó>à }œNÛ´Õs +¢G Ò¤P- ‹·EV ÀŽA\î8xØnŸvŽÅ_È3ó¡k—~¿’*S†"_Îà`ÛL^Ñap Enƒ ˜¢WÈLm_D}0Ä:ÊdïÓv ¬A…t§~meZ@‹>NÖ @Å!-x—½ˆrXif8}¨8{–O½×ÔzÉð ¨ë¤u‘Ã)hˆIL7CLj€ €1ÐX¼ëo·œÆ…¤pÀjå:öt$0ƘÊfLL¢}[#!ê•Ó2÷t?Å…ªÄ/fN¾>Œ”$ E,eK!01ÐÕ¢‰‘VXtÑà€ÒãÖ“&ŒÁ&ÀŒ›^iภ„C* ÙóÆÑ6¢át z:æÚidÞ—"_¥à¤P*:B´†B¸i+F“(ð¸»ù.Kñ0Dž ôÖ€ÕÒsÕ®¢E@ ×±^|ÒÈS +GÅ”w‘¨9K€Xû€Y­_éCo ~ ¨Ø„)ì:ÁìM¢YΨüÞ©”Év" ~s±^€ãçU*H¡)ML !ª‡6ZE”M´±^T•˜N0=ˆÓ-¤g ”~]Ù‡})¿’yV¥ +¨. ¿_ eLȮڜUÓ)dúPÖP†ÚVóéò,{IT‡€pŽ…ä¢WMSŽÂUgtÂLâ úÚÍ^ÀtrÌy{ [–\ë$IŸ <œnÁ¢®öâDš‚Öâc8]æŒ@9/ÌÜ•‘qàªAÎk6Øky=™v±†Ó-këËŽ’ÚE!ØáRx ãj(FVZª¢y R! Mn$P=öZ^ªó0˜(ÆxÃwÍ\@.—UÐ –ûuÞq©©40FE-ÊDè>V;³—´¹«+±|’*~Zààt n:ÚœšíøŽ#s—ô­â¸Z¥U%²‰a¶ˆz^“ÔzBskß6‘?p9Ÿ­ÞªÈæa–ÔŠXFˆ÷Õ  ‚=DÏEgN­rÄ®X]‘wÜÅÁ0(FSÌ£×¹cSp»€R©4‹Ðß8΀"jìÿGœE"CÛˆÈÓ-qo\âLu §Ó§-®EíâYJäAV#¾J€ŠìEqN +gÞgX8 ++ŠXVgkgï€Ñ;ò&aΟ¤úC!v˜à*”bÝ@õïF÷uFNϵhé)Ÿ8ã]¨Š8¬=Ið¨¶G¾x<؈ÄÓ-Èí{U-ÒP5‚àFÌo®d©¤âOf‰-Ƀ&6BV7#§jE( aFoo‘¸¤¸°a™g)šiŽ®—#6”æh¯@g´Ú©VËÀ'‡”fȲÑbƒõÁQQËÊ)›Ý+Fi_X°«øÂõnDâéäv¼'¶Ì¥Úu††.–•1”¥,GþXþ44 ÑÍóZðI›ÎÄôÞ© 5ò·BÚR1tCk@‡ ú¬GŠ§áG¹£ÙˆõÕðU„Ò‘aXãmãŽO·`b”:%?¶PÕÀK!O5A^¯BH®®¡¹´{TJ…Ú5Z¨Ò YS÷YmñrQŒÌåqÁ¸Ì¨tM•>ås‰BZ y†Ò%_§¥;+˜ò%“«æÕCQ‘™bjbUi†Ó-èé2_W¹ÜXxü ¤}–Akw”“o‘/+FQÝ cy‡P3 ¸ð&R„÷Ûª I¬²Ó€ûé3$b‹òB&¢×(ÍÆd j†u÷$ÖQé(A&u"Š”‰†ýr‰¡%N· §ã ‚´¶dí©¬ìYðd™¢{Âek ÅOTrârŠú€óÌ@WŠ`ŽukÈÖçb…ONyÀ–\Šzæâ€fá–ÒtÉÇ,%+¸Ð¸‡: 9~Ȥª–X©Ì–¥hܧ[ðÓQ)«Q„¥.å®ñ›ÔÅrÖ™«œß)¥¨áZgAÑ \ÃïGËÌéÈù0ˆ%£ÎyRñ`†á5qšÁëÕz)ú"E³ 6*YO(26ê4Cê'ðÖæÀÕϵe,UP%+Q‚Lµ zlêFáH+ë–(tN«‘õd± ÆÞÚcªÃpnX\ìÍ.\µÌ”^³õ!ö¨JE$±­T/n,8Læ”h’ „I£©žŒßhQ`1 +ò,¸5BÖ Ìr/1 +@×ÀHCmÞ‰6/Žúø€q»½Ù=rÅÒ=ÇÏI¬’ê$fM¢6²¬|$-HO ihQØB„5_éR@ÖH’L–jæXžH­±M5Œ$Ç{ ä®RTëq«Óƒ-QGKæ±h]íYg+–KãÁ¬WÁ3Z轔Ǫp£ægdmÉ‹îÈÅÁ°”,ƒr$5µ'°D­axÓ—ºm 0,§Ä ŸF&ß'@c=‚5¿¨‰×v+s/ iÇš% —÷êV’—>Võ#‹“!‘ÍŒLjV TC*ȇݭâ%±ÉAJóÂn‹x)ü$ªT*Û•nߪrÑ*ÛJhÚÕñ…u’X Ñ@N,(“§ª}™ Ñ£¡*)d’á(&Òé†Éj³ji"”’Ëz4d¼‹Ef3y†¤–)iQܦ£–¡?EA“¦D¤Gîjƒ%ÀÅÅ®Y3´Ij‡‘±ÛLA5œ€™X T »iV+{_ÙÙãcM…Ë"häò` ÈEã5YL4ÉÅH1õ†=½ çHÞHhù\¬D{Ik±HÂa u¢«ZNüÄ¢ý,ÃIÇŒL§µP[¦èÓ +žÃiÊ©·à1†¶\Èí@Àê);SY]Š»T^¸Ë>d|†]¸ 6¥³„ÐÛÁÉÂåxkчb§’gzÌ܉-U’ù Çòº“Àp˘Qy¨œö€¶xÞY¦OÅÀO T’ÖÚ³¬Š#ñw+ {>»­ËkU¦éBÀ(&ìÝÖg0ž©Wò|AóWÊAÿä…ATQ·ÒE‰ÊF7¿S’ŠTSkl”–ö¢ f&ck^Á±Iå*Q…Öi B“¥„³êb$ËN…ÄÀJÇ>°¤k‚”t«¥ùÁ6ÈÔl¤÷¨vÜ\²]ÉŒƒ^¬Ê³¢žƒëB}…&€J^œ âÔÆPy‘À¥fbÊbѬìö M@Û ›yiØ …€˜2LZ°’£)MüêèdH‘Ê«!+¬R™òC9)†7ÙTmd‘’休£|NÐTšƒfMŒÝ0Ær”uûà¤!OÎRv‹ÞržnaZ +=ApHjôÊA²<”®¤¥Î¢ÝÐ%ωÏßú*ƒ¹V1Z+âŸ5¼À;uBì"¶Åàœ1æŽ/`µa#Sa›nêR –²­œÙeµ‘†äæsXÎ×K~ן5<¸#NÒÒ7…Üd‘ÏB"‘,6ØûËh+ØW†NÂÇréhŒdǧªç'œd»Ø^ˆ&à2ØÀf÷îQë?°¾Þa+©µpÀ90˜jƒ,s0?×ÄX`Û3@ùf1KèêªtÖK9jç­˜ìµ"]x*À¥,u†YTÄ¿y)ðÊV!,a­28TO™«ª¨IB™papLA Í@ŽQ™ÔB¥›‚¹GVÞ, @#"òP/eJ}e¢Ç;g4‡ÃQWÊÔ[7–¾ª:…Æ[†«'=RB|Ž•]ŒM3\žÇ7dÇTªöŸ2Å ¢Q‡‘ G]“†ÝÂj{YÞªA )«»3PcÒ[c–#F—³YƒæPz¯ŒFR‡6 0˜z¸aˆ7/ “‚‹3`ïB²Õ‹¡ >¦‡÷e¤ý$é渪ÒÛO‚:æxµè  ÷Rï$ €Ý¹)˜!…QgàNX`™¾feV(jI.zñÉ¥è}ß„D9 +¿ä¯E5+ `Û^få@@Æ ¼iŽ{OhŸV¬àê¤×KÅXàUžk‚®ÉÄíYp¡+^ªäî—(‚˜gb/ËœÇÝ+ØçúCÕi)þ=¶cHî¹nbI»‚iHhM+Â\'æÑIªlHuÎz:yhm±ôàGŸâ ´™`÷‡°Pb ÜéMThêÂ'µ( …–|É<ØZ)¢Î=R©.Xewõ´^ûÅ°Äv–®—™,ªèà…AØ’i³ +ìב[ZµËL-ÃÈŠt‘W¨JÛªúaÜSŒô:@5ës‰D÷»bR— a¡xòΰ£ÞìLⶸ“;oà–T™D$OÃKnYÒ°­i”–:¡gÁ6kHM±\nüédׅε%+AU‘§ ?øX.–ôë¬Â ‰~­XÒ´ž”X)5–QD:’Zj¤®“£¨h•“ðÒ¯ÕjU,Þ•¥c( x'4KŽ€Fî Š,š]»ÑÛI¦@Å ›žÉÃädÔÀ¨/ÁH#4ˆîéŠX¥Ã®lÜæ²ÊËÆ€Þl*ÅÔ ƒ ÎrÛø½4{D*˜ +LC) ÄÛH¤4þnà²nhøè_çž¿ÜáSdHÀBc±t“åûs÷.*¨>8±‚q®"Ø®­ä2çÊÀ¦m4ù¾~¬^+…g$³¿ dlWöI³¡¨ð6F=õ΂U +o£‡MÞµgCHŽÓ&^´Ù~0ˇ…Z ðœÌ@2³FJÑÏIÔú]Ô:/r ºãºPc†-/Qª;‚AÏ %瞈 +ßeoD ~ytìòÝ9¢­zz†ò`k¹ÉM–\ÖªÅülïs™Õæ¨?”n¢‰’Ö&tƒ-¹¹²“^ƒQB@ô±~8lýA¾ §x”~”9˜v™$ ãÕIH™˜Ô(Z^¤šH Œ^œXFÕX½R•k³Ó´®J;'•å‚¨<€Éž/'Ã)­a¤Ÿm!W'vœ¹[Oß•µõõc%“JÕF3Pk +LÀMŒMƒß8/"´ÉÔím(&s4Sb«*&ià‚•a¬xAUµ‡’aݤédK-ØÈ—&âxÐg¦%Ã(IQ|iû.‚¥ÚIx)KMÔ.Ërɨڛlc¦%¿àžë»”$ñ]›ÈCqqºa‡=A^)«ÕQ`Z~άƘTì‘ã èÿö޵Ƕë¸ýn@ÿá| hz¾ÉVœë¤H±¯Œ ¨-ûšG4*¾þ÷wWÕUsíÝG"y¨À€"¦T\=÷z֬Ǩ1†OBˇթé¾tÁ&S^;»qQäi­1O7ÐqƒMqy‰BRÅÆsàw)=¢§À¢Lc‰]Z†ya§ï‹Ðÿ ™õ~atW\I ê¶í¦*×ùC™BNE”_Ý™# §ˆZ¯¬0&ù.–/;ào†½5îEØ¿eîM¨Áš4$ÑÊ+Ôqµz$VðKªP¨¯©Jpo>ÕÉç…—A3B_!ç1ù2¸Üyy ¨$-&£ póhÓæËòîµ {6¡jA00}®Ü‡EÆì\w[N¤–ÀàŠŸÆn#Q…¨†ì3ÝåÀrwɯêQÏ8<ÃäÌhk„;´(´Šá‘ cphÜZ§žjÎÓ•lçð+@ÝAÇY|û±j Þr¡‹I¥%8!Å'N>žB&?¨â韩ðÆ ’ ü®s*c‘,>jƒÒû2ʤ~0¢'Íïb§’’6³Í_.õêé½Í‘F“JìdE¤+X†â3Ÿ'Q…ŽÇ̱øŠ°kf¤~Õ©Ô ëúÌÖb®Ø’«ÇÑ-(a ö8L³ú×d¶ñ,„°øôlqÎ(NG¤&}+n@© +-·Q÷‰JH‘PÙ¶$¬p;»ev¾_ãvaCŽBlÿ¨ªµEСގì)Ì6ƒ¸% Z‚ï(‘P‘,_jË6Þnî +­)iL ÿvs;†.¸ù$ûpðfݺÛjð{³ÐÑ(Ój¯ŒpmŠ4Ê:ÌÁ×±¢ï&htŒ`Lïý-Gr))ŠùÀ=ŽóÈÿâ90™ÉŠ)0ÛÚ‹1ËtÚf9ukOÉÞü‚;£óMHRuÙlFÌ̓¡ÒÇÍÀlºŒã4y„Ú=æìÖtˆeåfDATp«µ N´îtÁ½TBÊL¼Þdå½#‘o~³¯s°”ªD5íhf "¨„EÒ` \þ´T%ð.5WæSê†æ$!RÑ nªE{ÁÈâ-¶ó`…A" Ÿ»Eãš½éæ+X}B[;Ý4G5 Îv&væµ€Å`…jóÇ6(?o«c­‚] .ˆg˜wuD›Â?–Uù8_¡³¬ŽD8™WÁ0J>Æ´:€/Újóû€áˆ>ÕHõÔ ƒ\*¶QP=‹Á¹Nx<þ§Piu lvþPà%&¶2ë²%†©ÇpѤD¤ìŽãœª‹Ñ0—Šó^¶Dº3sY€›ugàôãåÍÉÑŽÞEe!_¯Áê2zsìKœJÓ•ºUÖTFˆ íSQ•R!)ø +´S ±À§ýì“Fòq²LK!”1œÌ&kk¢¥8 `R†<_a!ÌJ€É H«Ïˆ1óµ½Åï~ƒøHÝt‡ÿÜâ´z’Dô‚3´\æQ0&£æ`‰CĪÁ° G¯1/ðÈ~Dµü€¥i™ª¦n~…p°ÚWˆz×qh? +ÞMo9Òr&©ZÔ|té{r¼ÑÀ,–¯>]pEÒ µá|,™{m%\ò’ú“Y${^m,è™+¬Ê‚"*¢K ¬»´”;µ¨9p Zá5@LÐRU¾„LÌ ^§ñ8ÜI&œOÌ >)­O‚N ᬛð eÙ©F¬0)»*e˜Òï`& Î⃽]íßLW‘Ÿ¤…ÒÂ:˜ÏEo‚°øŸ=©|ÈP [¨Š¦1Ê­¿Rܾτéò÷£“ f8a'[fšÛÒŽ³}´èuÙDEftÌô\½MÊHlÒpXýÞWàPÌð=Q.Ë&cxy6@aJͯ–€£!z=X¸däõ "'‘U Z—f âÈ@†ƒ.“ȲÒ9#˜XWV>–ÚãuLv{Uï÷X¡9ÊtyøI¦W¥lðs@‰^!0‰2ÂÔ—ïÌŠ²T€þQÑøFF¦jôT»[ <È׬ÎZ–OÅ‘ ­úmßÍ„ ¼bлU–üø"¶›ðÊKntý€†j Âä\%Ÿ‰€=ÊÜ0òYÁ¾k¹ˆm¾I—]ä¦â>ËÕŒoÖŒWTOÓt…¡…“1Ã7|Å£~@§ýP–³bòøìîÓr=Õ¢mb 7‹µ‡–L WW9%ÜÜQ°y¨ á‡¦¡Ì—4/(úN-÷£ù~bâ˜^Û–£% ®¹¢ø|SÎÀ%îý¹+.1b@ ¬9o+šX +f¹X§“Õ|rø =š jôÎŒ|èÖNØ}²Z‹5Õ×f2!ê³]ë¨]òI ¨ž¬Tê1 |Ÿ¬2l¡—Š\­”ÀªNó; b Ü΋&<›V±“CÉÚ`³ž¤‰tÈDKÉÓõáICúdåò bƒ"‰‚„“üü`ýWA+VIWký]À:0òyïF J=‚îòˆÃhSMÆîÎÿx$í £õÁ¦¶õb” Å-›§ýZ†^ѲEÏ«vB¾Ö< \ÓÞùT»ã¾߈HDj\ï—È5÷󲕤˜Jõ‹0mçÎLQËŠnŠ,Ža’Žwô£bz×ݸ­}©¿e_ª¤9ðC™¥CU˜lŒvèŒoÞxf·\RõrÞ"±„kÕ±;ÌA×bƒ01ÁVh¾“ç@ÿ4ã²ÝòÀÞlƽÅ]ÀË»Z`Á*·eÎ&¡,ÛºûýØäTã Î!z5ú('¹8Õ·=s…Þ¼WRjÄó’Ï·ñùBð^ÆsÓŽ ÆVÈš(În,ðz€WßLÒ”³Éïç<Š¥ý€laÈ£8’ZZÙ¡gcgyc§3xÔ&98/\üÎ$â6eAL]1} é0½¼u8‘q54zÞhÿ?“‚]ÐHZß‘ì[Ô0ù.LâRéA£aÒ{ïÀ£Ö(sœ“mj¦2ì‘hä Ͷ|/:ðNê$sÒl`lÉI2­Et¹H¯|Í1x#Eüøøq<ӣÒuo˜’óbD[o:·Åt©Äe‰ +W´žé œímøJË•K1{º7Ö3µ¼õÖŠ9œö#——:°2ÆŠDàRó¯£ÍÌ-\jÖ^BÿÒ¥ä\J +¡á¤þŠD`ë‰Ð§êºl`H3êLŠéV>Æì‰OÞ5I¥ÂävºÂƒ”·yšœdÓ:=ì£T"ݬÜNôvð8íÆð ¢€²$Ò8¬,¯ÝAÙŽ‘3νJWÆxYÖ¦è+ö»8«Žê0›Ó6¢v.]Xº°»?óÏQd–bQ5ÃØ\,«ê…+ðºº5H㬣&.Œ²ÊMÈ<[çe¶7Òƒ[j,tÝËÁ[ˆû¶F3Æ|Ý1[­ {QzÌÒ~Öó‚©.$3DH6(ä‚v=‡aç(+•«càE`ŠD®8H¢»? +å D'oD!=|c4"wwrå¥*ºæq Ü0…ýÈ6°­*T”e:aÇžÁ°# üBÚòUIg1sùµ©¿cÞÍåF=ñMÅ¿©fàElc`ZzËÅ,óéâçP·o¤™zE«m˜³Ê7j=¤9ØØBÅÑ3l¹bä› +îª|”}ƒ!Í]A'DY=èfïv:PìyïCÛ1•ly>‡‚`õçxÇ’ -ƒµuÿ²…G¢¢ Á:µî0 DBSåh¬q"ë÷‹1¤6Ŧ,ã3#øè¤X_•Bå JáG|<£ºý£˜gE\Ö/”;‰A#%ËøX½‡¶ðÉZÑ¢Y>1ŒnÔŒÈ`Ž^ø”EBFØ©i|Rv0ß(aÚò›ÃÛ»­‚÷ÚUÈvО´š ¯‚]O!©¦ÃååÑâ0]ÅNÖF]d9à]s?¿ˆ=8üM¤¤‚*~mn£ÆIìI0ãˆÍ¢•AœA"øÑ™¢ š^Á @6yŒ£À$Fì.Ÿmã +ÚƒáH¯ÌÁ$£9ݨXrð[;0 %âz¼ ƒ{î¾JmZ½nnt¤Gõž¡‰}à°îåYb@9Ža5Û>ZÑìõòŒ˜½’˜ÐÈŒµä$€FR!mÒéœÅRT—Ê Ù§íÖñs¸Esô]8º㫇4ýöÊþÑ·<ìR“Y„.°±\¬mötÖf®´âÊ'‡(jK<˜ÈÎàX|C‹/ˆk/ÄBáúá„— H©$–ÄO +vŸvLX—5x cË‹Ç¢S3ˆš»®s§Ì+‰ +ˆAÁFhÜ2ñ 3²Ç»m÷q,sœ¹¼wLÆ{V7æSùBì¶ãÄ–K.cøV©±\ƒ·ê“iï™+â$'b7f#Ž­Ø›ø¾ÜÆâåúC,sLVnN)Cœg:§£WvHH +Z©â¹D´ž”•}-v=߯PX•Ð†;P]èÛ$Ñ`xŸÏ¼RŠ$6ì·,ÌW/fD=tkg?&9¥2™Éžê¼ÌI¬ðÚ™NŒ˜u_a7ŽÄïMâQ\•Ö¼óÎu—XÓcóñWÄ‘ñJ©ÇèÄ)ïè¶m¤Qã5#ŸÃãJÕ؈sÔQ¥boš6¦áÏ…4$Û±»Ò‡>¹¢2d‰z;ˆ<*çA5‚u2цI¢ÄÆz´h¶aìì +oVw£iw(Àl$;t +ÂÅ:ŠØ€ÁNÈô‹¯¥#¥61#IçºÏ0HaV7Ä÷G`èï`v^2‹íÉãB±CÞæõ–ésDÔÆÀo¡ªÑŒ9ºùö½HÜ šƒƒ¥pËšò·ø”¶æQž0t¸qÉÛ™<ñ±èˆ+Õ[%L4‚À]ª,=¼œb¹Òæ2r8á€îãlçÛ.(>ˆ÷aÑ+ºÏÒø(¬o6/ÈšèFÂäÁñǬÓ|'ù3±·®Äx}xšÏ«‹ɃŒ?ôX;¥ Ïüµ¶˜©Ï鄸ÑÔä»­Ì<({I"²‚.*Ø +•V…®èb“:·u/%%¥I§8€8¹Dªè^Q€~ä§i2öÑžæÐOÚÎGÇLƒÅà†)L©Å¶éØ·­ùÔ$S ‡Z’ULž¹É8½ìÐ+Õ ‹J ì¨ûÉT“À*øµAWû“MZy•£ªT}¿Î­‰GæÆ‘»&©á©’D?ÞvÄÉ\Bù±xN +|Þt¡ZµÃ;áœÂ+ŽÌÛñÊÙ<:N9ò¶úPtÂ<_I##eÛëa¸+Z`´7&¾Èìßút8À ñ¥ž){aT(“æ±È{<°ìïm‹—#;ÜK¾óM!ò§ó÷9oQŸ.Ï;A>9ä„Ç7§¹‰Aa±%;åÕÌN/ Ë÷~-ûXòo4y®9 º’‰Ð9˜€|*Ûõ3”‘¥a u:3"R`àÁQ”‰”ý +Or“EÆ#ZRóòEgúµ9â¡%nŠÊ˜„›“8ÿ.ÆLK‰ãÜJb¯’ÐÕX‹DŒx¥Ûè=4pø!±“ãÇ BåÕˆQ,CP^3$ʦ&{9(–ð*„+ýœX³Ýõ•+÷6«Ç^²¬U5²TxÙ(f1â_5S¹}TÊu¦Î)áÒ"R ÔJžÄÉÉ›î‚1ÝIJ .2–OIÇ«®íæÐq*„¨ ÓÉžÔ^>1ÛAò,v¢Î¦åÂ_C¦go…H=9TƒAB¦”Ò D9zp.–5ŽÿÄDËè†òèäN£{Q¢`+ ³+QÙ/LGr ˜B÷ÙÀE®À®|P™sE¼š"-b’£p¼p}pYf$Ž¬Á-éú¢ÝÙz'cnA¿¸Ö%Éb¦‰a8š3HŠ^óAb9@ŒÑB.ÆŸ«(³~ð‰ð–O62´—hŒ$ú. Ò”`VN0$Îÿe-àŽ•w&ešuò–hŽ[ê#/îJÞƒ.~q›Ô¤j4Àø ©¤ç…oõ$¥V;¾Ù¢fÌå·ö|µŒ´ýïÿ•unÇ?Øö)Qʘ¼3?L(¤F'<2• ‘1ìTx‹EB†Â­F ¹=d@èÅ9YÁÔ . —Œ5gÒûË`ÐãØÓº‰Á˜;Ö#O›™²ö–Aêç´Øús“zr%€$ï[¦·Ïجm!¸ +[м:ì•ÚÒçÛ½CYH£¦o/9™P탦çDš5.ò²$‡L7›µ{æ +3/.‰›ŽDÀI˜ª'±µ'À ±p”Ñ›&3‘ì|$l!9nBAˆá”ì›RÎàôî“b,Þ=ôSè>ü«ý3_€e›+‰‡³G®£MM”+ºaôlè(–B? +=ºf•ÓûRЯó‚Ήßa°:Úped=©ìÞh§3îc=>$ª´CÍÝü"Ø~lgÅD%%™ÿ» +‚Lr³©¹Ð4ˆ)w•Û-g#¸ÒŽäF«÷.Mœb˜þz!ºh~Šy`Áç[löç Š7¥Yï³?K$7%®Íë–%˜bš©Šõî0)òKÊv½ 5"Å£à–§…{s•r`7NŠ? +Œ˜É‡‰Þ‰ô¸Ggù+BòíœwÚ—ð†ø°ùAY¡‚ã¡8¾¢e­7£‡ŸC‡ÖÝöù…CÇj³PÏ~•6Ô}1U¹RiV-÷Œž>Lª¨qºß² ‘Ç@ D q€+(º %Kx}¹d%Àå½$à8>w¦¤:Š·°ª2R˜¸ø¦ßFBbøK îµ\ªøÛ,çïÔ§Ò÷{-,·hwÕß®ê4DãKSž}§»sWœ}š½xÒ©¸%VAχY²Ñóí% ¸ºìîöANkPà¥TÁÜ$™0,-¶OXm&™í6Â;ey¤à†}ãà[H.‚Æ¥# +0dNÐF¼“@™K6 åe Ô¸tÔ{’À-­;ê_9Ç] )¨:T‚„UŸ´¿×ÖÉ@¢Õ*ngÇV»¶ƒlšO—…,Éïš©ùÒNº’‘ ž7þIÄÒ”.Öl|æÍèPŸÐÈ‹Î1C%á]ØN®À‰Ôªó>Ï`;üaúà&™þ:ªZÊDïg:ÆÙd¼3급Z¡á<¸Á— ÿZî‚_A1#Ñ‚+t:–W—Ý?6rƒå!ÀôúpÀ”RÁ_'MɽJȯ€÷åI"猖ðà]8¸Ý×$.\¥^6Q˜ÐÑÒa&†ªàpE};‡ EÉmÄmŽ$`ݬ}f öÊÝ¡ä¶N'Å·„.™Ñ5… ;§48%lÆnÄžÚö M¶NOq¤ÀíÎ:‘¬âiF’$+¼r~g;”dº7ˆ¶½¸Wã2¦‚Xa±êKrl¥´§YBø çðn¯AŽ«€ì„°ʧ’ì}:ûêÞó™ÔÜø!(:”` €.OŸcÜa†ä‰€ãN8¤@ÉÞ +ѨJàM‡¹Žöo?TÚ*ïds¤x‹« >Œ£Ò½nñ¹}Í ,I÷Àý4oÒvËÅ +AËãð ãªÒ\á)`П%Y^ì†rÌP{[ï£W§±-=yÀ‚‰}·kc¿»úôow¶hI^}“i±ÍifÏMƱV‚ÄÌŽâ­åÒg®€=N >("³·N@žëéŒ,ô0Šil.›U<8ûc[|ÑË1ö~Ë)k3P^ ˆæ²Þ€ž›à’ê è2Nè5sWŒómAË“]V±YõÎñ­sÿ"Æ[YØjv¸Vv`o7î©gb*úA-’—%š53`ôDDQõ&#quh¿Ôr‰­®=#í’æCß#šžÛ–Å:pfä—uv;á ‡àÜ>ôYMpF|­•uЃ~\Þî곊^€\H³I$3[îm#$ÕuŠø¦J¾å%ñíÍcJLϘ}J8 Øš«ÏÂÍÅb®æBN¯@§UM©ÉËòg&“I¯iµÀÅX(¾ÑŽ¦gDAªZ?Ga¼PËY¨¿`xØ?\Œ¥yxϼT‘LO*]Y¡qŽlÊÌ,b§¼ÓõæËe¤}ú¨Pqݱ9TÓ‡r§û‘ziaOv_ä +i$ò€nkN'ä®Fý+$–{œi"æMšU$Ìæuá?œ®}¦•Iä†ç4/ìÝ„ÿoû@DÐÅu'œ‘¦ZwÑ'8ˆ1.…·Á ˆÞLgCp¯+Ýô¬{LF)S±QH~¤rŽ÷\ø-¼EíPAÄD[ õøÔêq a€B6B³[ÝM—â“’>6ÇÄ4Õ]Žœl?ÀÛç‘(³THZ¹|îSG};GŸú¡£=úÙ®#¢=ïظÜhöj#Àé€&oåSÝ Ó­¿HäDÍûìC;zFãžÑtÊéà:2TÚ7ú’øcã¤^—3ÃÈÒnlLã0¸LtdÊ/“ù=lŽA‡ß>ixTWž\5ƒ-NzÂâA¨˜H p€›Åî"ÏT2–_³¡mÙ/bY—ýÍ3W@ñR j@ŸTé…¨îfw ¬CÊçP˜J«<öd«ORq|ƒF¡É„¦Ÿýe²J 6žNˆ(ËXcºxtu’™™r†Žœ!ìbG8/ªAVç0×8ìÏY5Ç]†ä+™XlmñçcªKULr>$F*íäÌ ”n‘Ð4ht7\q2$çØP.”þ) Çö§ÖŒe𙘰ãÌê’hoBõ>¸´ø­â3‹Qb;ª¬Åɤ?Š÷!;¼Rðè&Þ"±æÈ +±O +k¤îS$§¸GÀ|:D<¤Ææ·Š.Ó‰,§“•I··ûëÆpiM.VÂæ˜@£bVå¤tÂÉGäôÙ£_FÃʬÍù a°”BzüÕáP“ý˜±ØB{šN-eÝœÑ}°DuÐI/p¨Ói ËŸKãE%y/“ ;p)¾isÉfþ›U«ž¬ˆ‚v¼°ýˆIFps»¸D¯ef;‚èÉç%­g3B£Lº+NÇ.|Å$ö°Ÿ´ˆ]I¸ @òµ…M‰æ!Àû(¡{—A +ë:µjLÇD7ªÎ6‚•hS1>ª6l²±¨„†A¹†óÙêÁ•²Cx8jÄ ²L€Æ{éà÷ŽÏW£Å2Ê”Ýhn%ï8%P¢ª9êàôyXƒE»£¥µXnëж)T@›Þ×=Q ùÖ¶¹õeC‹¼¼*ySx{馋 TF724•âÊ:ÄŽ}³qHtã`¶;¾ ž +%‚ ô¥-1ÛJK§4$ }¨ºˆKßNr€·tÖ +Ôhý7Ù†€Ìe‘á\;ä唧±‘Éx¿ßõ·OÉ—äÞ„ą̈ËíM!¥NØ̆Ów‡‚Äö|8“òòHË\Ãv ò- å, +eÉf×ݹ,$lA'Îe²¢3²ZMI P 艵`ÆÈs$W¹ÃSðNJï2qù +!,£ò>koø=íxÜ*æ× ÅðÊ° *š¬µ?¨|ëÄHe… Ê«xŠ&±%ÒûrµGšÙK"j&xW*4,’ì”"¤ÕI0{K¾›EË—s­‘K~ÊK Ý©ðÈÁ£,’<_¶­Äè´{c9½ò2zC¡ªj®›˜Ö’(±]²9ˆÌÖ™àBÝO¾R …¨ps‚®~SMŸ)ShÁb:Õ}d©8çå´2¿'dÝÑɯ^Žn†iBÿî¡ÆÑ!†IŽ±az:–H;ûê;ÏíÁV |,ÍUh|ÝdnQ£­0!ô+,âw=‘pÔÏT¢fç¡lôךh>{rŸms.B9ì3ï-+2’;öÈ‹Q3Ó`‚†ÓQÁÙ™>¦' ÑAdésàHmÞœ×0¶E{õ1ü2š=:v‡ÌD}ïÝYÜøǪ†²#Ùëö8£ùÞ–);j’MT‰+¡ƒDЮpz¶ MH°Ú Ñ˜ë*€žL\g|ÒªŽZ€‚£µ„‡(Û$'@×þ|dž¥´ƒÞ¶¿DñcikzñÉG±ÎzÒvð‚v³ØÍsûl.ªxYyCßô­¯¿Oåæ?üÐ*7ïDžFmäÿiúŸí8i†äQcÍ«˜‡Ç]„’éÄJå~'ÅK)>óᜥÉÅåGæê,')ÖƒÈûÃc #A[•0‘O€Œ}ۆɞ´ANt!ðÝÏl“k!Çñ"s°Š:;_;‡J‹½ïÏìrØ «1ŒØvŠ÷ ¦·*³5?|oÏÝ0:шªÐl¾Gj!±¬ªNÍyÝÇq_œ±Þ¨–Øû±óÙ¡7×™á!^°¢+ÚL|ÉN´æƒ §°¡ƒê=Ød·ØŒP)nÈW ûjr ŸlƒÕÜœËMž|QÅ\^ŸQoïÉÄÞÝ;¢DE:Û´¾·Yȃ›½®ñ™½R¾I^E–¾ öå+ +’Ÿ#ÉñöÝSÂf$*±œ]\•jçM£è÷tÒÏïpJµ+‚2ÑjHÖ·®A*ÙükLù8ã…lÈí“Å6V™‹×K$9A-6õj”ÐÊF¾`Fü&«*`#*üPu2ß·^n¨ËÚ/8Ø),]5|’^@…ܽ­»g§OUZàf<(鎒4¥ò”¿n5V £Rv gY?¢œáŠ + ³úHT£,‹Ô}‰BµA\dJ¡DݨÍ2÷IÇü–n…Žp;‹ºå%lô| Š®£¹*®F û<Ž¸ ÅN&©|0J*±mH¥ì +o>|+s¬d5I‚4„ðŠÆ{ ^ëÌŒ¡LĬ€ž°§*Cb^hƒ…Eß`~ÚïYÄ1#†˜Á:ð°Ï½zFt Óĉ½ƒâî?3:!]ì2±»÷¾_Z4'ß3†AU#`à` +t œÔõ±KÝÇÆX và†£6}u¹®êé¿\?ãïíóÊßùä “£]¹L#µÀ§ÑØbIŽ~m’Bb³Åg»Ü++HÖ…7\^uÙ¡ñ¬îÜAÏáóà:6»¢\yG!ŸxI¬$F Ì–Ïi€ïì!i#x˜M6ExË+\s^Xo9Fee Ž¬Óñx7ÁQ"ƒ²p%¹‚àƒ,üOÀ£,ßÊÉ1Ò!ß¼"yÙr1R'Yd%CÊ›úön¨Ó¶E?6gRüB„Õ¾ø•¸9ê\%ëM;Š¢Ö0ŽRl¢õ’Nß%m%£w(Hv¤­¾UôYhïeŠnkbŽ‚A¾Ô’ŵŦ÷ V ‚úåŒþ‚ýHÇÎJ…àbØ-ìÙ‘¶äܬ KîY£÷Câù¤øe‡PB7ß›ÏöO=èQvРly{»sú‡1Šì–hê¬Ó¡‹½-¼é ›ÔŸ«œõÆ’“ª7lÇ +¸dÙ¾}ô œy)0%k'ÕÝt¼ýðQ‘=œL—™p_mÔÐI2o°€…Z÷EÃ&KGH€ ¾/2!J|mÖpr¢XQ4²~Ž6]ƒqp1"“ç¨Ô±/N›ïysŸ‹Q„Po_ªü·Ü&7ÆÉqyݤ ìsFÜ`‡òê„΄—öi“ÆËžÐSzfΉðvƒ`{ ‹þË™4”ß•v!}g$Õï2R@n—\Ö³1#0Ùò-ìv ÷ ã¢Ãvi5I D+±ËÒåÒv º6‘Û¼µ*ô”)³#¾Õ^‚¯À¸7d”£·½sst9ñU1g$v“ÓTÇ6¸£g¥»¶¦ídÙKt“èc.ÂtQr—(yɼ(úˆ]\TûÀ¸ÚÅ> EŠ–O¦ È‚À'ƒí_ç̼^lT g“`jŸ[3AË‹ŒRp.lþ'€˜Kä)¶„òÏô#üÞ¶#ñes6ìÌ™zWcŠÕv³s~ûÄÙ: ¦ýN*ê+ßÒå-ò¦*qÉá²yogêùÜ\½!BM—íõ4 |« K5Öóµþå5kú¶Ñ6›1yíÙûQ>]Çt¯)ö,|(`,×ÝÙ¿Þ†9X®‘7töcoÅH“ìˆQT­¦ã³n`Õ´u°úå—ÐC*sø3ô¬•éÜÂ/0ÖDÑp‡Â’ÉÄÿ$Ç£`ðÞçs@¶B¤j+Àsù ÞOX™Ž¾–¬09/·ŸêŽ-$ÑÆ!ÈæwbŒøD>l¡ÂŸÖÉž‡ró8Èè¢_´ÒùJ¯†øQ]ãEÕ˜yš57+ò)ÀŠ= Ÿ‰Š ,Ss¿³‹¤ûbã ~.Ƕ¸l`N`&Ö†ºöèщgc¥¦ —õÐú44&'ë5·v§ÇèÒÃ.çÛ¢p|Ýoò?3•@Ä-^t1WÔIF&Ô‚¯à¥3‚ú–Ï91«ôÈv %êœÎ7é%d‹íê+^.H7& …~#]@Ä;0áŠ`’õD!ŠŸ24ç(þ¡EB2ŠÅ¸vPáå¤+ÛN¥&o5"*Ã~å  ^6W5@ÏíWkco¡áX[Ã_4@#ñ•ï =àÁÈÝ–DšWãØ(H„\–¾OÖ¢ÓrÏtau£»Ð—l²PŽæ¼€Éºˆ¸"jgtâÄc"e–²AgȤÚÔÞ쀑tJ2¡¸‰ŠÒ²ŠùêkAEÑDÕÜ¢£RàÂ^œm;hÒ9Û†yõ¦…£ihËŠÃíœwk^^ÓZ\Ä5ˆš#¡u¥£ æô¨÷ßÓïí•|úÃ÷JÞI_ñ÷*tŠ¥19ã÷l +S€D*fù +øa 5ƒm±² Wç1„€AâQ#¯ä¹ÕÉÁuà$# ÒYöÀø?*á6:âˆo®Ç8ee=Æɲ)S÷©(l)“±S'tJ¥© {S6Bá#†Ç–-Î-.V–DˆØ}²å®9d>¿s3Ò0íƒ CÓfgâŒ4”õkìG;´ž’["÷TNÔŒ¨7¦°]ÑŠnÕ;N¬Yw+QØh\Îüï®k-gÛ|«WŽPž[Æ,¾¤ +…@ÇåÈŒ šÑŠzeŒÕfk-£œGaØE?>é”2úEe껸ÎédX01؄нÚ.¼¯˜"›#ˆ:+wõ‰ D¨6.••d€¢ÎÉvƘ [RüfÀòI1úWÖ°S Þlx§ÓµòúV„¸~—çF]ڰ±—wJû½÷v +óðNAGW_•®;úóCDi÷®ê$…j>¯ %&VèU×q˜¹÷Ñ.”ǃ§Á§ '*Pî þv /ý½ÕœBKëÝ»ì¥Þ+ƒ>±Çœ§ääèò º•l ü;tä5H(ð(Ú+÷~©QAÓ±rûø²5žžÎž»´\â(Ÿ{Å'E¾bÛOV¤†ûU’ƒúCQôЂ{/P’6ãUÎFÖ#3%”­GUÚÒ0‰ñSaãŸ%–êåÉåÈiRt¨µæ겚KS ïý:õÒô#t<æ¬gÁåáakõvãhOP‰rÿx{féç@Ua‹˜O´÷Í@!MšN¬¥BÏ„`k„/ÏWf›:Ó@l~³œûÉt™ÁzBàMR>´ÇTéX«Ëy~¼7ïx*hâmïÕ{?_—I0ùù¢Fš\ƒS²a—êRULq1µ1†µ­Ü±³ó|µuKæ´r¨ËLÉæÒtÃe DÆöä£!xê³ÍC§.íÆþ Òzíœ9·?$Yõ®E~jþq‘"@­@Ü(…¼ÇdžÛ×GT–7ËN¸·ú¹;÷ª„*ˆ§[ŠÊ®è* U$ÜÏ!3w/dmïíߊ +¶ªXgUÈŒ‚(d…S£qNjÄR€þÓƒ®QAb̾ }ÛÁ(“Ãò© "•'†êúLÍÎ êsДÃ=eÝgeqnÛÐÜÖ¹#wçb@â™Ýah¯Ïe_+¡pjgm–E½MÐÂ>MSñëHÍt)ìl]=Â6º›~3òý)'û…¨kΉ\ù®^X–»}ûL +€ÝØÒJÎ0Vb»¥ÑÚðþ0AÙ+'\ŠÏ^ MygðÍžË:êw)Zo2@`óä8Ç©q\™e*1"a†ºÃ +˜9HþD„€o6ç6;õd‘tÖ9 ÄýÌ–&…¤!š7ûI,C,ÎÉoèÎÙ›¤}:îY +ëú²ð íìjÉØXqa›ÿ’TzC¤VZ—ìµïâlFÞk‘@Æ i³'«1ÃÈ.n\ÓDÿ[gŸÙ+ÓË Q2ñB»rŒ"€Z®ŠÛˆ«‚‘­9¸Ê¥Öì•RšHæ{ûm2ýmNΆ$ávXÿ•¤`8>†óhBÜS»aìÖo%Þž64NÇÅu¬´ˆ³‡ _v«RÀX)ºº†jÉ þdyo'ØÒêx +ãP›æZÏì5‘ÔÓ´B^hÆ>%izÉ.t^ŒÂÒ»S*u'¤ùéwÌ#›³ùzÛG%oÚöû®S¬F6ÚñJ ããªEvpÅÎ ¶u•L«lç}˜XGµóÅßV»íÒåBÚ(…‰ÏêÐU—Þ #§íßñ*kO~0¹™t× »©NðN• ä™~ +.tcXñ…vtÙÅâËq<éõÔMÌnyø.BÍÙdGDÔ¡sTMÐeœ"˺ïªQw9wSÝö iK‡ ²ÎîªQGX¡ÚÉ®‹ Ýæ8×PÖm÷?Öåûú¹W:+{†`ζ¿{3ƒÐEW_³ç!´õ[Ë…dϬÎVVÐ/ã™Í§¤U#9ÕD~aIÚfyõœ!Ä){Úô i³y]A‹¾+6S†¾¤Q—ñD½ºÄ‘*ø¨I› ¾/‚Ø{r +Ø[dƒƒ‰™ØlâéµuÇ9obG’`î¼Ð¾Gif')ŽŠÀÚ-–)O†ÝÎ`Zèéµò=ÌêPŒ„n°ü5œôK——DŒ*(â•î®E›¡ã}Ùã,e®Ïfg7sæÞž&v4¥‰Ü8R¸Ç€ˆƒÒªâ;ð>p6Sªr©óÈ䟡“žÇ…»×†ØƒLkÄ!Æc^\™^SÔ¯GÙ6_!÷R]ª—¦ç¶BQ&w6¹R~VÑ;dЕdž«’;C‡ÀÖ¶Sh¾«êÁ‰·G±}/´[ã@/ÚS“\í]/N fCXÕŒ3†“M ì=¬¥{»~´TÔb@6Hf€Ìm¡¶"vV¬”!À.Ny‰HHfl£ú[Ë{—JF“õàf,“/´6EÞv‚ µçPñ ’RÛ–ñ½YàŒ¼{4= ÷_£È»T‚"÷ñ˜œdW _ÇKt*6ì>”Õìc +´°ŠÒë®çÊ[ÁöyV6l…ý€*`•4T[µŸ¼H®l „¿ê + ˜xŽª/¥îm+I%·Ÿ®8ýÚ ãªnýR0˜ôª îkøð¢öñçŠòf€ƒJ´Ô‚¬Î `@YD±ÇzF²Õv@ߦÖM1ñܦw2em™òG¨í«îÄ2Ò£ æDYÙ° Ò§D^90aBªhàKÎ(zöÅÑHIʤҼ<Ô„Lk— –ê ¨]‹5Ò°B÷ŸƒˆÆ íÀß—º®Ñ\k-’¼š¶˜*|õ:'nOòCºÈøµdëÛOêc®á%?Î1.“ õzfp O´æ$ÑueN']Z®_¶¬‡•R(OƒÕÓXÀ›Ðš®Ô¬=œyHIqÚb8o†väHó9Ññ—«ðx\+9~ã©ùÞl·xa˜Ü…ÍõGuŠzÌÖ<½UIþ}¥ø¿< ïþò¿|ýÍϾ|ýú·¿úòWV`º’¡íÊ%Î ÿ„õö¢ËDÈÍ~»[R=lïþòg_~ñÕß|ñÍoÿéÿ“?÷“çOÿú?þö‹_ýÓ—¿ùæ¿ýÃ׿}oÿŽåûw?ùôW_ÿòËÏ?ýëýùíì~þÍ¿}õåçqJ÷ð¥¬EýÏ~ù£?+ï~ò·¿ùÍïoçùk,ÿ®ü¹œ`ðBz÷t 2¥ÞqóUV‹·ÿÿÙ¯eÏ>;Zùݧÿ"ÿÿ§Ü¤oÙ*fµôž¤mCZ猠ß[gnçͤW§2ÁMI©ÒYA"­ÂeÍ¿ø`ÛîÿÜt=Äǯþ=kÌ.âmgò!Ïó†—zÓýß÷è(ßt©oúž7½Ô›ÎìMÏ÷'´ûI—‚/‘rmýÁÍï§ì‘Ü¢?TÏoïÊ{fN˜Y¤ìí‡Cf°²&2Ç1Á}e +ù' U÷$Þ*1|dl©<ÄÀ£‚Y72?"$CÈÚ•¿œ]ÁÌJ½%óýÞäoÈÛI©9Zm7xãúÿà†÷p£¿Ï~WR„*?{«‹ŠYÜN³È©5ö[·cdÉné«ìweMä_·>QY¿Ç?^Y ;å Þbxç{þîgßöwç-éòU6$Óü“Üñ¾ëçø§´á}ç{óÆ~W«Õý¡Üñžö46ø«uþûݪØÚfà±°giõ¼ö;ª Ì`JÜÎ.©ƒ‘ äÕǬµ +L§¿©,¾•ZÁ–gñZVÈVfñ W§ø6îy}“ÒU«Çûð§¶çeÅ~Uy“tüûÛnz]J lÍ“Œ"ZrŽ9 2[ठKvîÅa•«@CDo9ÌŽÍB£P‡´jFu´¤° ƒÓ+§‰3|åÙÑceP^”fm±i:Pþõœ©’\Ì( Ò29óá>|‹íïþžŸýï)Ëû|ëÝï»?ÜcçXβq»¾r M®EE€ýgšfZ@JààéV‰Â­+ÝSðTpŸŸvç­€Á®Ë:Š|µ(ÐGÙ¬3G×Óšh*PB•Á¤´ÒŒM_"¯b€(½äABЇûðwŽ&|»|ó—hÜ¿ùOUµÝÛ;þ³üðÖðŸvG–†<½ÚP¯n +y ú³¼ã?ßøÃ.[¨vW*:jº%|ŸŸüìÛýäΓPC•°ù;ïßýEý–¾ô»/ü·Vþ‡Õÿ__|óú÷ÿù7_ÿó—oÀë“êáÆAŒüî?p/tüo`îõ½%®cy¨¿=nqùí|~þǼ֗úõ?~óËß}õË7.3†ìòÍß<½§ Y¢ºÅû{ûp@‹Œ¼öÆÁKpIio¶z_YzãàXVKŠûθŒ áõþŽÌXv»Nòq0>Ó»I4wجª5—EÄgaV<~îb<Îí°Ç…ËÆ%çp¹?oËÆm?ŒxFç ÃÓ®Ìe…¼¬—»s€ñᎃcYã·û›£ͧmߺ®šl0âz +0:Ržö—û×'9Þˆø¹ÃxžÚaŽË8V >NárwÞ>8–›þø™ÏÎpBw3`» +`•Þ?Úskrä8cñ>µ*š§z6^šA¶awØøØk^Ѻ‡ ZÆÏ]ŒznþÝÙ_îíÍT¸¯?×>¦á8µvŒ†WbæšyÕ±BÜžã§îåóîñ‹>{Øs±»¶üî_ÌŠ/y²Ù Ä”¯!¤0H¥Ñ£ß}‰ »ßýÕv¾.¢ò¼œkçÏ]Œë¼ùWó˽ùæÊ"lÚhüõÌ`t°¯váeÇ"Ç ŠŸ{¸™ÏoßãD0ò5ÔtûçooÿK˜„+_§$[9‹%É2“¸ïnUei†<³ÑKS‚)¨~1u…vÖ¨âT”¸@EBãhîU ©â|wbÌÓ‚ëv»!…š/Ùî™*ÍÈÒ´YTâ“a,[GdŒ +NØtZª K| ÚRX+ ’˦jv•…¹‘†Ñ2 +3‡1ìNû°»ƒÅø±È êØø9¥ûㄯž›Ýn¯Õ8®ååÎ×-±R2ÐtÜ ™i½ÜßLãž×¯âgÞÊ%¡ŠœTÏÓ¡5;iQ…Ïÿåj·W…gvËÇùJFM\˜¿~žìö"J·DÉXê«·’Ö‡j\Ù7Ô„Ãâfì&žæµ¾]¤('öů¶B=eä AÚ¬­ Y_ÝϤT¯ËDL3c3]‘7?"Û•¿Àˆá¶Gdž«šïY¼¥Ìòþt‘ è.Un“+]`±-_Îد¬b6ú7n0¹öÓÈÅÅÏŒ:ŽÃÅ•ƒja\ÈzÂãò‹ªŒÒëq~¾øä}á5½<ü›ãˆ¾¥Â²Ï›%FKîîíTÎv}:§§¤>ÇCW™>Ýp²¶†tI{9Þ‹—ÇS­Úf?9§µ«ý} ­9¾¯\b€ëB¾ä1gë·ë7_\÷EЕÏÂÑaÂQº†b®2edw’rÓ6>mÛ×’ÏB|F_Cª«]ßBs­×—!‰ôæ—¦k|xÊÿ³xÊÿÇŸ[ðýãÏ×Å;l#-T˜,sYá”ÿ²p^CÆ¡Þ?þ›dL«fĤ»À…IÎ4¸5Óø/*hÒÏÃ7¾UYØg …a¡úÀ†y~»ÿ=2ì Zr6ä~qbêÃXMýx øo ·ŒÝ +¸pÀûÃBààÆãRF4ŽÃ…‰´qÜ€tªsÐ:yáUÞÒÛÌÎýü¸ÏÍ…Ëx•~h&¼®õ/Ú°´t–b<ªÕbÌw¡ïT›ys·úè½êC;ÕÇïS¿K}húèêCûÓGïNÚ›>~gúø}éC»ÒGïI¿#}ü~ôñ»Ñÿ¾½h)ï2¿ ÛÐ’Ž×…–èë¥ÃÛȼåöŽY=éZ.<ÉWûÆt¶ò†úUG¡ð:û*Ö \Ò8ò-FÊo)窯ÊSæ|w7i}gwv‰;U͵(ÃJo±püƒo{0Zßc’IÉ+½+÷«ÇJÛV{ã (Þ â©3&ßm¾2½¯¯ U.{ãóêÙÑ8´ìeØ$æ6zo•Í!í:y³*‰¦§>ë[LaX™ÓNyÓètŽ¢Õ l5bŸ)sp®{a«“rk ¦bÙ[š¾£$i„›úvSæJ¾E “<ÂSsøpçÉ(É©Öl.Â5‚;CZU¶›è"'b.ÛÍáL4S .JƼCÜ—è7çÞ¨Õ\hçÀîÍëÂÆ{ÅOpK]±Àôð2cΤ3špF¹g¦J¦!¤‘BìçvêÊà¤L]áÀh·&'– +†ry#J®p÷NQ—(à#O§»KÒÑB3¶ses²™MP è$u„S m<̾BWÃ]RaA w0L‡h‡fhQu¹®v¸¤Fð–œ(˜Ø·—#ù]#k‘ÎMØÔù‹·ˆ-R%%› ^RÊà'ëu¯$0ªÈ<Äxö"…]_3I!à¡‚€Bñbº ©H´-¼ïÊ÷ì+(ÀÓìúÙd÷S¤(äV6IöŨ¿¯@ jÐZHuD£iý@ Á&ÚÖY ÒÜ%£$g×Ò?÷AAð+[ ß6—”i¹bµ ófD<}lvÆõA# +mÛ -RuŠµGx¤eÏœ",{›àôé¸CRBåe¼7ƒ¡I.Þš^*ccÑ{+(Ûp'+Ãåßždö˜±í^˜P¸V_AYPô¹×ÊS«¡ 免<dq5vånÂ7è°UÆqb +Å“FѾПRæætx“dT˜b'GÊt¢„솖ÜF¦1)Ø®TöÓ•~:òˆdˆxh…šŸº‹Dâ½(xé‚ŒBAJZÇšqÕ»Mgä™+håÉìäG‰, +ù¬GÏÞ¨*l]Ò>Í£W¸£al½vá²¼§›iÐeñQ“CD}$*-€^Po~ÏáªA#”9zsÈè(®ØGKnñ C´Ðä+ ,k!e´9bø—9(üú²x´»Ý¢×`Ð.ƒ!šÁÉ)5E²¥÷V0åíCe¢›RSŸnl ÆÒŽsPWh‰X³Wd/ ÷qƒT¿åÜò1¡‚’]dìv?àzêØœt¾%$4Úeìëb÷Fˆ­M¥(Á­¯¨evž¤h=õ‰Ã?Å”;<UÙH=.¡ÊˆîŒËPÚ9JÍbÞÀòuHƒ¶à ÓÛð&¨­eW¡œØè¬H¢(´II”ƒGN23SÞ ýWÑáp)ó¡ +''‹À¸ûžJØš É}Õ]•æk2ƒ3Bs Zg>øöÚ®`?¼5¨ÔK¡’ ßc9Ò½˜Ö»-ÌzaÊ¡¤Ïfœ3áÈ<Žèø¶•\´Y¿ä Û$®Ò¢$l§/ªNÆÝžìö:#9Ý!;pçXo4ÌI§ë”ÌÓ5ä¾Å†ÉÜEUì®|.` c¦Ï_Î=0x6²‹o©äB3+…öÄÔØ)ª3 +Ô8úáº3ÊÅžÓöÖ¥¤5y!àqôV6jb º‹Ç•‚ÈZÆ58ØŒ{²¸À›²5DAª+㮄äpÚԢ«˜²ì×6Ëæ4R¶XîXa¢D¢ÜTv ¤jƒäZ,Cη,wsw„À@#Õ¨dC¬)9ñ‚úg3ŽyŒ½Ž¶öÜ"–ŠFÓ«σwH÷»‚’ÐXíÀwÏ1qpÞƒRϪ¬'Vw ë˜2IÉU,á;t4Z‚?Z¨úI¶d+hèZ}àöªó wÒtn]Ü=aÆÌLZçŽI'l@vR·÷9°mpXªÌŽ1WMWS(¬’n¨Ï¸C"õA·€ÁnC3•(•¼-ÌfuÆH,¶3<ŠGª‹öB3ʽwÎ¥ŠQsr3:ã´–l&Üײ@kJt¶½./s¥³~ÃùR lÔ(.Un½?Õ/Ç ‘ª9&ŠšKMÜR¨’{Es˜rŽ-‹ºÔ£ §dmw †”U—”*Ù¯¤–üžf@Õ•VbÑû$“O]¡ü#‘š©%)‡näkÓ¤j;· #aü×u®M-i£"¸oáH¬0j¡™âÌûäÍ5UE3Ê„N¤y–‚9[¸ç&¾2ˆB¾²ëÃi;í*´@lóÊñÉÝv…C©Ùª³’À h*΀šM½æXaÀ%¹ª$‹. ¬€ +GÎ\*oº·sÁd(©Ñ u>ä Ü¢›ÓÙ1 ý%óŠ 4Ä8y»„3å"3‘µÁ †¸yƒ‰ÂR¨ÙIÕ2þ^G€c¯ƒÚ§«ÏR!A"i3Õjß·¤$V˜­âÉ—=(qÛÙC³í"®l¿Ãž°»$U‹¶ ‘%øq{Ó-FÚV·KªozwñvµƒÛGd£<ÄI&y.„}² 3Éê”ü€U$|±~“Ò1¬áÓÝ'Òc*^kK+ V +K³Ð-²å Óî“wi¬&E;‰`oÑdÊ0Öæ€Ø«9<’¹¤a2ò·ÏÛ}RwµjÐr»Sâ†ÊÄf´W¶«2±“Ç2<ì'ýTíSÿÓ8¥¾M•MÛD.V»Û QvqwJETáÇÂ` ýâ nW Ó–îŸtû ÄéÝ8ĵX³Èª\¦f4ezÍ òéÕÌË«^M6÷DÞáŽFaÆãSãêç +£#3ak]™:c´ e“m>i²¤Èq굞2V§÷˜ÔɃ6-ѺOº½6,C‘D/±®# $WoÃ~*«¿Å7Üà!Ý©>©²»O7#5³Ÿ¤¨û$­»èsWEL¦&˜kE|áÜÁ2šMnNÐ-Çe$“wUc)Ç +i›“îk ÛšYR[ž¶Í©ÒDšËå£ñ¿œ@œè$ÿR¤Š²¦ªu¥ü8•+ÔUÙ4Nö„FO÷ìw¢+i¶³íŸ‘LÝ>ÁTè|êLði«ÒÖM²D^•>Ί J¾˜0“––ê•îìÜþ²A¹CSÒ:Ä4»*‚tt£Ëᑦy$©&ÝÞ‰rz$ˆŠ?KÃ9N:çÛÄÞ|ßH/$ˉ 4ñâ@¢œ+öUQ”¾E³›aœ½’•ü*ÆÚÀΕý—Ód(x%Ä`v¾*d/ã ˆQs[‡ `Ú^âô1‰ ì  O1ã“0‚­9ˆ.ù +cv¸¶a6$e~«¤ì4&K(&àÌ!Ï”óm…Œåd^1„”ù)GMzÈÿ*æ t„Þü‘uaÔs4€MœÆ¼É<ÈùÒà±®¡+e—‰ÈK#SóGu,üÔÜÇ;l]õˆàlWwd×p°ÝÞ;ëÿÉ`ͱB¢E.˜é¯†'F¤Þœæ^ŒÀC@Ë]3Ýl Ó0–ÞXr*±Âd½iø£;‡¶ëåI­ØÛgÖ›‹mÍŽLîD}J•N[\:5¯îWí«O"0ú¶Ë€X©–ÝAU#_§¶iꆟéOXî—&JÅ®h8$ƒ hšá]êºuCfFÀ‹šÏ‘{.T5vA¼T—Œ¸YŒQÌÔ) 2ÈtßDò+ðN>:—ðHÝpHBjù‰lqî„=©ƒeYqRîJô8+t–ÄN‡ˆ²r'Ù_¤+bŸEé²ý ׶bŸá”àÄØ=ÊÖ(m“Éz;È@YÌùÄð+ú²Í•áլ͊Ÿ +SøÁšY™a»mrûÕ>ƒ/` +_jG5UŒ;&Òpc½j’Æ +­ BT[Nû?BK²×†Ió€WÞH´ÄÖzÒ–úà9Y¹À\Ï-Þ0 +€+$€5;Y…x5ê™^×.Hδ íï°‰Ã'® +$±vOÕ->+÷H ¢š¼Åä -¡-™Æ¼Y&×>¼A¶0å_ñý”9^F*@Ú&2b¢9›ÖÀ9QŒǪŠ˜ÑÇ33»}Lß)Xvpþ‹²mV/±Y=Nµu‚N|B&Âì(DhÍi‚#õŸà&~iãìÔåT`¯è~+r`áV=œ'Ô–¬S7.¸òyD«8’éZ£öÒRiƒ¶U—{ŒáÚ}DÍÂ_뜯¼s-‡;2’þ£‹’Ìአi ,{Û¿YØn`*žD>yÕL…çç¬Ì2ÇȺ0ØÛÁ’0®›V–N&žcÆƦdÙ§ÿý-•'oLuùôÛv„ygw“â„fVôÈ$ê$â]Ú^üHñy Ô«s_ÎûÔ­›iq_ξ¦~‡³Ûm0 ©±…ìðÆ*ž´25™˜Å°ò±y_ja,cwpw¤ZÉ° WMÇ1KrŠ>íl¨;9¨¥ø´ió”À[2šW.d$™™Y˜·vrˆ/∕:¤ ‡”¨KÙ8Á½:k 8$’0k?k#zt“b;‡yûuzí'š +A‡X šF=›È¨GYºeB´ãaƹ‰íRöÐñ-Ó@æÙ=€ö§2õšCš#c¼AhRS¸ï*<Ô’¦«j'ÿ B™:¾*O}¥¼X2ù„2õÁï5*ã%Oè…9¬qB%IóŽVOš’°rJ…¢äUæ;X×”ÊÀÓbJ÷Hô4HœÅrðç†Pßy—Ã%I¥ž¤õF—$Ê= 4J³ì.‰ãGû†–…Tš1= Òåàλ…†’¶.9y©äl_•ÓdÙQÜ#ÝÖ@Í»%5&²q’”r²# ËéX¹_.éB©nÅ©"9ËËý\™­=lËóQ·§‚£FÕ…¢1CPhaZ6†¢ÏëOcá3Dt¨ÄƒSyû˜sºý]·Âø¶ˆÂš5gÄu$Äþäy5’;®qWIûÈöL“SKJÁˆ7çl71Š•z(ÂÇqM¥Bªå,uuÊ/n—š@ëþÊ·Ã •ÖUÅ"XÚ®#8¼,ÇŠ³φ¿JǘƒËÎ$Ä2ïvDdoð,Û0)±§ô +ª˜*:€Ëzä»ÐñUc9ò4 +Vn{Qí5ÔÝÅ~m,âáîJx¡aÓÓM†ØÚ8§×d„`â’ä‚2بfÄ #l"‘ÑHd/ÍÔ +Ï¡b¦“f¦ÇªmrYÍuPLkXÒ±˜õ°0°4jP­„ÊëÝܸ¡V5ûQ /œ;(ôa­²MªÅf©fX}Ç_£ª+/ZÞ \ÚnÉ‘`*€h¶µÎñÇ›5.Ô¡«Ö1=¢ ˜‰a¶ƒâq5Ççlß°jÅÛ!(‚û(n\.È õ!-Éj@“‡a69˜zi 1uOT '‚K'Æ7{hü®'ªNZS•¬P¶Wߊ3 9T7Õ5¸.ÛÑ©PŠ3;¡ŒÅ»P©sO¤ÿÙÜ8Wh NK˜‘Õõd²Äv¶ú^c¥®*Ò©]‰Iš*ÌëFIH¬íP sgD¸4 +³Ã)MÁY%£5gƒ jŒ^ÿ°ÿ¥öÙ|z-çÁ(`z¯ß0XJ7=æ:÷“8BU8#;‰1yš·[a +C©:YB—Z @)æºx•ÞèαÞÈHÚ`k2ó}p¬m+"K|ÄôšÜþ ­3>ÀE=#Q­ŒÏÆ&ü1E.þ¬6T}6yie€¹`/aâ,ÆÁIïcbi”b G&I2Ú;ð¾è0 Û‡·ƒilx[+LgP©r¥Ó'0f†¬ºèQLÇØk±ïœOŒ„ëè\ðTõÀtJåvaÿW„¡9 =Iöàº-Íšlú=ëÐÌ«ßÈeqܲ=7KMÕM„Ö£6·Õ¨í÷J·D©^Ã9Hmx%6H¤;çf1h±ý-L‡_#•’y%”­µú¯42¼’@÷Ü)Íe$xVf¼åwðØ£L&oVÂ[¾ç8ê>V]T;ÑÑú_+÷wJÛPäùˆr†Sº½È€Lƒ&JÞkVÉ]O·wJu’¢£xß‘êøÒp€¶0õåãDv›Ó3‹_ƒ…•Š=‚¦‰#•Z>\Rf™Š=Ùéìó²óh¨€Ï×IÓ·ƒ{-ÎÕX´Ð¥=æît]F‘'ÆKÕÒ¹Âr¨É˜HxþÅ;ï.É:kêRΧC¢‹¯Æêì¤+‘GíG¡Ëª¶É+I–ï3jÈ•e0™Ú`xS¸ƒÌKû0ÆMõôhe£¯º’—®´†´Lû³xé©rÚcJBjé)Ö91ÖÞm<Ñ6VP¹(Þ™U‰u£ÎI ‰}ºCÖ̈¨ ­G +àÆÀ‰œ‚Á Ea"ŸRR+ü:©I*î· ø£6ÊáJëì¸ÍDnîíBÔã$e·¯Y–êVâÜ&OkvÜué¢Õ”öf¥€DÛ²°ðG8X†¼›ÒB”ÄXU[­¢•Ôp‡´küÈ«Lճೊ7ÅíÕ¾e$K¡Gš­Æz¨£¤ê¼‹Å  Qö™ =·Å®~tM÷¬Ýx7ï†ÌN]“¯`ãÂÚªer+O3cb€¤ä‚Iš”R×Q$w(Íà¿bGWV\’T +ÕHEqIÝ"”zô›%x‚lÕT)“ä[¬’ dD—.ií.búü›ºgxOB™F]/Æ$òN.ÀVT6}g >¸ë>¸–ÃÙ¼ˆz%™ë<9hÛÂŒàuewGµLx§-6‘¯Æµu@éV +ÚfÔ¼‚ƒfàÌU/g#‰=ät +±Ub ¾Ô}“æBþú³˜ºÂqú´2•ù!›xÔBýÜ9ü‘õ©Ê…_üêä„Z•Ó˜ÏFç/Àj™ —!O¤9—yÙ,á­u¬`¤Öñ9« wªíú‚i„è+ú'õ$ÂFo§ž-6š¢À{“ñÚcÈ4\‘ +Dé ºiØŽ=]ÖƧÍxã©„¢Â—OîZ=AÅü)Åø]:R5¥©2{uXxE]fí„ë7ã<üµîs°Ï^I&Ê×®Þ«_9œ‘aŽ”éö‘TlÉôK§fßÓ æ Kök X-åGmeë«qŸÑû2oåXh("ñ%‰Ùôí L8ÑJH¤ð4¢sV@ƒ~'’F6î¦Zb€qe¼Û}{T¢ôiúqœƒ*…ìfâgºçÄ£qX¿¶J±Q¹ÍkiØWè`3î¦Zç¾±aw‰^)\p*’Zaoª“¦ÕžEMr$o/«É(@=Ïsj´wGÜÎÞp9ŒH6>ûîe…³ÅŸ›ÿ\MÑ`#_ +ñ¬â’Æd;í#42Ê+¹ ^nmdt—´¼Í¾ ùW ~íþ`!±ª7…±XL\C\ "`ÔšLx”ÆÍ…jÛêfX ÷‰ÿj㋶¿qž©ØW’Y=LB®OÜÇä:°‘Ï@œHÌ4>СMÃ$ø4¼$¦Y±mk³*Àkpò—NÃgW¦“hL½âôB+&;PSŠÚ®éWî< âаž†ÕrdÈUµcÌD»%î—9§zïZwd<µEþ¥žSk ‘…¶ª$ºsD`â>e0XT—1þ¨Žô«mÅl‹öyð‡Ur†u¡»š@ç+ÆÍú+ªÄ¼Ð +ÑÄRÆ,¦^Ï\i¬6 à+ss=ì•gu%YrsO+]Ì:œêê~ŠäYšÆV¢‘q%£k¨eÙêXñª Dêö^˜Q‹ÕæŠû£€2ÒÒ!ýìÑÝY÷®¨â‰ßÓ´ŽÝ+ß p©I¾—D"XÑÇÁ—qf nfåKÓtŒþ¨±RD6"òó"”.Ãý‘†wzxù$C("e¾Nv€pR* úì(L„ïÞ`'ébu1)›¯EÏ©ÿD­Ã*öÕ+¦ ½ÑÙpÛðìÅB¨X¡’5€HxqµÀm#qÃÍ™“»±tÉÂ%eKu–· 4‡ËfŒa‡3›¬K-yØ÷höæ«Š«ó¦S~IŒ©ð #õTJ æ´oό˵üXYYö„Oj =FÝÈíõƒ«Æ÷y>µÔ>(á€gû àpµù»¬o ×ûÞÒ Wû÷nÀSüÙ»g#Úð¿O²áo)µ]j!©Ð²K$›Œé;ùówºÐÿüs=øörýâ¿Þ‘äõæ{sÊÉÿ³RKÏúŸùÓ[´uùO«ñßožÞúOÁ¡ùÍ›þ +ÿ‘?/w«—ÇÃWúì¯nÁøåOå¬ðëo\3…+\/š·—èP¥xR²ô²fÓGêÊ&û¤¥üï +H¯ÉÇlÅ'{ƒÄ^: )¬ŒbltÔ¹;{N¥:--ú +äÄÁœîØ'Ä´½¦ p¡ +Ôlñ UZöæÛ×ë,„Þná{-l,ir·Š•Í‰íîpftzo<¦ÁoãïýDïøæsG>^ôÏ>ûôõõwïöõ7_ÈÁ×·Ü~ÂfŸ(^þ=í3¹ôGr„–‰Óß•¼C ¥<ëƒëÒLħžl¤â 4G$ñœB˜ªÎx~“•»ÑdÛñ5HÿsB |ê›NAïb¨Ã1á9ÔIîÂï½Ñ4ÞâÄômïó´ïIæ÷o¾øê˧¿úòýío ýŠª{6Ac™Y3É'íû¨ËÈöMZZÙðâô;³rüÂÛ¼7V3žß'ú~~p4J§‘Câ¹_Xa?¬Ò­á«+{Ý«u…Óžy°J7>\ÙýMø}ïMz÷—ý›oÞýDžÝ»×¯ßÿË׿ûͯÞýOÙŠÞ½ÿúW_>|œ:–Ð1&"e}çÐg£)Ñ‘àù  –EâßK3ÎëMÛ„¹³[p±ñϳÿù8ÿ<û¡Ë-‡KÖ;Û+_Y5â¡tãä4mÃýòç’0´y=tÝjê›WÛ3Ÿìã¡åá‚vªo]»˜ïoÓÛ¶vwçñ^µ‡‡´¥Z‚‡¼Ý6î<þ|ø¡ÃnkoüùÛ¿® Ø»“?–¬w¿Žk/oœèÛ6^{ºþ¹bÝô­EmfëœßL_bì >φ¢ñgWkÇ +ï7“îãüÐ)Œ4Yâ%·g+‘÷íп¿{O`þêÎ<¤ïXÞ]mv»þ×ý¡ãx)·uÁGwvgISŒ;’ÆxÓmrá<Ø·¯zÚæù¥„yöbÔ)Ãt·À´à~¹? yœÙ +(k“j»Ãè ”·.o¬ZÞ>â'«-¦‹qÈÜÀƒñ€õc;òAî+Ú|Â`m—@#V¶·–ã΃ëqÃ#8zó!¸yæGLjwëßèÖ¯fWði¹ÎظÆ:Y%’aªþŽ?—@ }ËÌÓż¢|O±'ñåy|;LþL"Ý´Û´lWÿF‚¸ø·æßÞ +ý¶-¼Ìñç DuH-¹²õÅ¡ÙízÃÉ)õçáþ(šSÐ…§C$±½q×›+l³ÃÍN»3É1Tɺ"nó¿/oZßZ³~àhŠ+gU¿.W4pãÃM¨~Çæ´>£DšÍÂ¥ žËdãÂè~)?<0t¡Ëbòè0.k\ø»²Pd?,·ÉRŽ«1Ÿ´)‡=ÂA™}Ÿ1:2ˆÏq#Ρc\ôrpÇΨð{ÜÉØ­9|¬cã{_–\¨ÑÃ8M¥÷~™Y’ÚòÉ…¹{øvo|å>µŽmuŽ7÷Ê‘ÓYdKVñúÙcÅë?K9ì?½+ïþõGOD±AA/Ög>Í~Û0Œ9ä– +þüYÈû¯_üÓíRúÅ/ß(ãÅ÷Oúw.Ú)§áNŠö÷WmD<©ý ½GÆ.‡‘¤c0z÷ÝØe´8œMÐUBkÌ°Ng¿ØÍ>@+-‡^ŽL‡Ô‰™Ã>\ˆŒ8µ|ªG§±€XìË{djlœÒÀ@¸T3TóÅŠèc³B¢w+ìÑóç¶^lâ7tpY¶-Ä©íËý£R;j5NHˆØãQ*RÛcÓ^qp*8óÝÁô\®Æ~PÅœ+tòË7RM꺼:ª÷ít5W»܈ä.Bæ3u +¶W§@(rþ½Ðä\Œ(. ƬwŸF€Nãõ‚ÿÀÁ0êoù +ݤæfl:J}¡Óéqbñ]^oÙÕþæÁo‹“{ˆ«F[À9´uuÞ_ó&\®ájëØG[=¹e¶+ Ì¡p÷ôA£rx70ì…r ÀX Kçus‘b§l,}ée0ÚÏW¿ëë~ c= ¤e³M„ïøµl,¹×’ÑÄû)xÑÆʇ2‰×IY¥…3Rn\ÿ\Ù¯Åp·ë`åqNÇ—Çx9˜OLÐÛ-µcª¥FÇF“µ8žÆîty='öŠ0FHoBiŠ·±¼hTc¬»¬Ð]G²êÀOöexÙÓáÝîºê|qdN²ò· Ÿö7–á®… Ðd´!Š ¨™áŸ-Ì-n8gÉ †ù|J a…mó¦§Ûc+ÊBÊš À]7>ßÝÇãà±]ñb¼[ÖÏ!ì„ÕFhåý³Éqûl2?$©²žçp•>Ä^2 +•cžà´ƒ¾ô°UëRÿ/&ð’›°eöM e×….¥|8«¬c¿½Þ évìاq–èÞ¨H6ù@`ÿêÞ¦Õ¯XÀÌ»¶ÃnY•Œ£4>óuÓQðëÁ “‹Q(Î{y\á´£*uØ–¹ dX•ìcšê8…ã`ÿ:ŽU“1ŒßEOÇ÷)´¹@­oçã‰ïPé<ãu-à¿uûå–F¿5w+œvÅU~uwq;Ô/?G\F\1|½s(]ž+'”KÚ_W.Ž:–½‹`—÷º2ÉŽK;Vð'Ï##½®ìæ&P¤Ä›*H•åxEíקÒã]—æÈ®âCBVpq'ZŒtT`¡Ï½AØðf.öfŠgøÇ?f¾ú/_þöÝË¿ùæËßþæmè ':û-\KïT@Z³ñòÝ“XL,Ý2rèJ½÷0 ë•ßvÂCÛCCŠ›qÛÔ•…ˆÙ ä\Áû…9k¾:š©t¾Ä/ZßöÑ~9“¯P ù-xÁyݼ=p¦?þ\¾úî!mÅ £ìû‡3C DvÅ¢!ø(>®pRþïWûs Ö§Î*Ä‘;WÒãÄš‡‘§À5âßl׉…MÇš ÇÿÆŸÇÉžGÆeÅšoÜ]ãÃÀ«Ï~(˜çO>}yyó¸¿ùâ›×üo_öå?|ýÛ»¢Ò?7qÎ8F®g}×w,üp7þÀÃýÁ0¼èá~ú7GuÿlÕ%î{¤ëéÉ®>ìü¤¿“÷zóÓÿ¨ÿã?ûúè?þ“ÿwúÁdz²Sízê²f¥ÆÍ=h8Ñ7í–°¡4$»?à„a”ée×Có M_µÁr4Ø G£¿ )Õ}9XjoLÑ}ÙÃxžÂaŽÓUÏ {¼ ¯ÃédùÄyë +uÜ•9ê}ôÄÈ.N%œ¾Q%?ÒÅF©i¥a‘ˆÛøªr78"ì§àF/ C~#ÖJ$„·}ÙÃx9‡Ãçë«žöxîoÅääO®îj#)Ž%¿Ýëa·x˜EçìJaT2(Ÿ û,.ËŽæD q4:\-™o¬åV¯Ì`ÙÃx9‡Ã~œp,{\Úã}¸¿yF1PKº|ðñŃ.üû,üñ…Ó§ÔAázýW®×?•³úð ð_üŽ%”sÞPpWU÷xÍ o9ÓCíÄ‚i Ø„¨…{dÖÂne!òöÝYð—ÆÚ¨Ÿs­˜ƒà¨  À/l‘B{†êf—¶¤z_DIl²BÄWÕã\ƒµ†õ2¾"Ì í`l;Ø0»ô•;ZŸ» pž’?ETú7ÃKûç…i-µ7§£M圳åŸË÷à8gÕbsñä.ó9 îm/œ\y$Ýì`»‘†T£ŠïóTî–Fà:z­F¦¦4z"šò¨^)B4Pî f”ÚôJ%-Á†ô©*±UµŽ}µÁÊ¥ü<¾ðX µo7Té#l ôrfv¸ø0:Þgï”æΩV‹—ò§³oã…}¡çíÈ?gˆÀ×±ê¦6ýw Lçêà˜öDPÞkV5׊g·pݸٯ‡nÞ T ™Ýyç³bA…]/Ûµ7TŠu„@øtþž“¤zç¢]³¼=5qq‘`{aØF=ÉnTÑ–c¦Mè`®Ö<ÿÞê>„åU@)áùQ섬´úHnêï¹ß„®œrè¾0l"¢–f¡]0àä"‚°‰â¯ þ³ÈZd ,¾DËÑDš6Š«hó…öÍQæB*©cÁj#µ36^´²49¶LuÈ—©]òB#שߤŸ‘«õF¥ókÛ´™ŽT‰g® !Œ~ªê±ræÇ#m±*VfËKNbTÈ>k;ìŠw9ÎXäÖW”Ñ>¡Ñ(R¸–æTƒya4}¡ªß*¿aç&UHc+Ò8±a÷ýᄇAÂðè +dÙº6Ñ:!^$[ŒÊeYõIõÔ%­”ùæí“ +ú›¢Ê¶¬i²Ékµ³ÁÙ—¡1þŽIò^qü΀c¦#>U©¿ct G+>T¼¯zÿ?{oivˆ©»G"Åa‘²H‹–eÅ$ +…­$þ}ÁVÈ{a +@¡€DfÈB"3+—n6sÇ#ʦlA‰‹)‰#Rô˜’EÙ1vŒ-Ù¾é0ã¹ÚÇñEáƒ}GXöÈï[Þûÿ\ªºª› ’Eàýo_¾}‘t«I²Wä+1((ñ9¶ #É°Ciöv çÙìù(caÒ)lA.dëЖ»EiQBBNEìpTc†Ž4^Yœ\‘Ƥ”<€¾™›uIØô½+ûÀœsT®¹2ƒëI©¢J0l)Â×Â<" =52”§bOÇQî"vÖ[†`‚ +AO°§ÂxM(†ã삇ò/‚<Hÿ}Yc°ØsÆÐ9ë8º5¸Ê‘n+™a Šµ"e˜¯‹ø9-ù3y2½#³T ©81˜jD_f=m'£v‰s‚ŒNÁ¡ÜßÒÛS™hrx¢rS—Áëe¨&“bK…Q)"ŸÉ‡PU§LÏ`½t˜Ñ9Hš?¸çÆ"à +Wº%Êl½. iã\ª(Ò±B9q(ÖRÇRÁ0õ¤ntãÊ I¼IP¨)¯_t5¹\F@“‰6©²Îq}ŠÀ@…A ”‡™<7s ÃÍ÷¥è§u<²× “bz@H¶+ ÕtÇàAe&I}  êÜWVÔ&°GrËf5“3²¡£Í½2³ReÉd«l¿€öY]©F`bòÒ9œ˜.´ô8 ƒrˆEìµT†í¤üf¶!Ø!M`ac˸ïPni’ 昜h°LX`ß—pØ ÇçQú;eÜÌ$X*:’n0M™ +N动@—&“šm)¤¯SxM¬ì8ÊqCy¢•¨éºj‡u%B¡ä Œf§'!ªP›O9l"DÇa^pʧ»#7wšæ â31Ù†<<Ÿ‰NéHÉ2Ðâp*¶2[s)3ªÂŒšn3ïQªdèÁòe—â+á[2oU)›nJ1¿Š£¾Lm M[Á`ÎðP©Å|rã¤GÆY!_­­ž£'Ëš¶@í ô@\ <˜Ë•|ÓìmæÊ 0™ózS)“µƒ~È{”2b©Ìha“ ¼Ð³Ñç-Gj7!Q…E¾±˜T@UÖ i_+p™,”º#Žö¦€«KþnnHÜæÊ›†!¤=•›„ÍûMŒ&•øARZfúÀ•'…Œ¹}ÊßBÑv(Ç7;–³dÉßLêAN™ß¥í‚R¾Ë€¾ÁŒOMW%£®ÐT¡o«Øº´#GRCqO”V¬éRÜ-ÃØ•Ž +ý øª§ £ •ÔwÄ…`VWb/v•Äȹ®¬@Qò¾äMÇ&•\‰³©PRÝ5ˆ•hsr¶Š•ˆ¹,‘r}™cK×\ ÃPŽÒ€NøÙJ÷åRxÚsË’É”[‚Îüøš¥œlþ˜3V‚cù㎓¼nKµ¸4t÷(ð0b@¥v=I5” ëº:Ë× WiæM N\N,­Þ‚±æ2™ ˆ=tƒ©™À¢„³hrÈ(e;©ŒóçÀˆˆHŸD@rО ÝÌ ê=d<“‰/®§‹„ååå0s<dèÇ•K`9 <_"wq4¶'¯gbƒ¥d„ü •F‘Æ›ŒO@„`J L¦NT„Î&Ibe`Ó®Ø]C—)…uß’Q¬õ¶e¾DCIÏ ÒŠd%²éßàpnw—óΦ$Àr ôBÙ MgŸeT“EÇê´IŠÆ½rŽf¶ºÉJ1»nÉô?–D©¶<h¨DC‹ 4‹ +N,3¢A˜MwÁÌX,à¶%Ôq¥Þ¡Ì4ÇÅÕ93¢ NœCõ¾´ 1…l÷üPxcÏqd"S-À™6…„ø$“e!þV'A20 $ÎÔµ+­Ý(=Ž +ÚÂÙb,Ò~d•zƒÓƒ¸K*ÈI€…™ôdŠ,ÔýX’B¥æp…½ŠIÍâp +o«R,d_G¹¹ê,}±xžœr&¥îRË]_féÂ̱j t0¯LQ娼d> £¸óöùáÔ E!F/ž0†9Ω ú¥y™”]B7)ûG° 6æk™µoËÛD€ÁAé¶ÚºMˆ<ß p˜aÐpå>§B‰øáuùR?Ä)œuTì‡04é6Ý l¿â×1»´Ê>Så³i,/sN"&Öý ¡dÛEäjh¤& šÆö1+’¡‘Ǻ¢THäåºÎâe]†(C%Û8Š‘ù:èIù’”u1Æ”«ØÊ:èj\*Ô9§¼cv8ez¨;Ò|ÂNz®&EÙLÆ‹ÂPĪ[:SQjÂðt©bìèr6Ê  Pc1*žÎw•‰ ®öøžJÁ-H*,¯?b +îÁŠ)Íà(éßÁ’é4eè)YåkLH¢‡§²µt™™WÒ³ N1eš:iß mtføÚQÞö&¥9E¡"2®Ò˜©pM]ÚÃÈìß°î 4/‚lf¶džzp‰s-™¯‡už`-ĹÕõpªp¸:¾Œ»Á©|““¼‘úÏ–®æ‚«f ¥«\Ñ°g¾„è>ã ŸãÆc!kÒ0ZµÊ'b…\öAv'c™™LøB!«}¢?ø(mÙ*¨Ô6ÐíE´$Í…L….]Ê\FGÁ*>ü¯aƒ9­ °ŒÈh) jnƒRXMsBÞB– +¾àA²7é¿È÷v¸:[p…‚øBôP‡‰ ÓV‘Æö‚Ê$ÔD“Qçê„TFíst]Õš’ðTê(”úOÈÄá(>SE±8Êc)Ý2¦*>)g¯êKWšúÒvÈã´Gˆþ]v§¶dLZHU%%L!¢ ­‰~Si81@‡ë±)‡ïhRPlk>›$!G²xÛ’.ï”a¤]|ê*?HCâZ¬£4=+¼D¼s–H î,Å.‡Øf2ν+)@LXHó=)CD +]yðÒ@ÆeúÒ‹Ã2¥™ «œ4@Íô{Qƒ%NŒ.}JÙÇ£H(§.÷¡$62¥ VÊ7¹L<.6âqTª žâ£TP”ë2r -ÒÆy2[!:X8R¦S²€ËŽÔyJ{\È‹¥³á#¬Ë2 ¶ÜE÷ ÊD¿p!kAlf©±eÙ Åœ¤aêìVâû†Æ ;•U,]¦ý†ÂPˆfMž$ú[êF[†DZAµïqž Ô–ÚÊŸB¢hRCKº/ ‡¬‘ºÚ’3 Ýì컊R%çÖ×kO†¬q2‚ÖÛ +“¸{ô¬ ›æš”‹ÝpYN…S°Y,Š$€·)(Ä|y +×ù>Jëí dm§X/6¬$ÕDlܬ-æ~€b™ .¢DzÕ8'd‹ÑðAM7Ìјˆh¡dõ¬°Udq_ÙÛf›³¬ãz¾Ë3“™Ä€=Ö\9À“àQÚiÌ)iñí ¦å© UPH¬¼(4Ô&Àí&¾£˜‡«Mš˜AP8Jê° ‹2´Š¼"ŒlÑcÃcK™z`ó+´ËMx)€¨ñ°èÁ’ݲPõàêÒÉsÈ1 "]ÈÓA¡g³Þ]âÛˆrÝôX_æ0ˆY¨b¸²u èY‚–Ñg­‚Ô=ÛŸ}Yšö-õðÞB¦’Ö9ù?²¬E—æp”/`)Ghh“÷$Ød¬û›äH’”Ç\†A +#%=µÌmè¤'ñ9ž#3à²øXgC[àpÿC²O$mu[½£RxDc`oz¤’B91ø}ðöe ”;Mq|6iȘŒæpe>‘òŠæv5åý–¦”ìq‚€ÎQŠ@²gk,0íRz ¾@îXÄfu¥?€$ÿ|É°ÅSi Êôeìmva9 e Ý’kH*Ø°tV0:N(Ù¬k©4ßPˆL'Æ!£P ;=ƒ-®0ÇfVêS}Wã¨@2F&•ÙÔµT–Uéè0 ßWV’ýLƒ—.û梦ŨՑºO¡ °º¶ü`lFä.zÖ{óc1eŠ7TzóE·Iû§ˆ$Α +öžÛrÀR<ƒBwlïÕ†0Å*Ò¡x-6¸Ñ¹M¡ZPݧ³ŠÉ'KÑ4ØžÅ?ŽLñëKÁ!dÅf€ïù*X#~¢ÎÁÆÇ’¾²š„î,É# ÝMeˆjº”]€È7›@¹D©2l²á£B–Ä,¡>Xhì·ã6k.€ýfH‡ý¢ý­-ðqÓÄð† #¤5@$¢œm=ÈÉå *”t¯4_.ÒbžŒ@˜ÒƒdeFBx +‘ ̓ï£m¢´…ËÈLжˆn BR„Âñ!)Ú A4é’†Ö¤ik•Ð6¬"… ¦yMÊ>D W´ÔËrˆÃp×O1ôž¡ϲÓ/Ë㘤2Ãì6#iØ ©sLžä7¡+Û’úmcnzÈÊH_!ÍçMƨáÐ<#|Á¤¦ÅTiQ€s™¤šCŠB!' d r-6˜É#­$bat%Ò[ûÒäCÙ›H—é:EU Ë•7è»"‡$e˜©Fž´‹><×à‰Wš÷¡ºš©¹[Þt¢úîNóÝ™â»3½wgjïî´ÞÝ)½;Óyw§òîNãÝ»;}÷ÑQwM´fz¦abœi¦…›«D˜âÜM¢ù¤0JJ 6~6kŠúBÃ&"èxE5:ŽÂ4Wy6«üÑñ78Ïgí¾ÅÁoQ{(MV4ÏReÛ‰j +”[&›èx2œ ðò¾LQÁæi؃Ԁ‡”x?l6–Âà|™Hz])“ÁªÅ*V£)k<ê²p<2Cc¸ßÚdÛ °Š" ØÝ…Òüø¤Çeëw_fˆgÿ(°øAö/Ï“öîZ@q`jkÏ’ +"å­# „@©Öòbȶw·åp’>5I»ÏXÆö ]{‘_3V[b…•Hïƒr°LVα›&š®!™©‚âÀ€±ÿ(õ]I”“[6  Î^ƒ…h ‚ÕPø.ÍvØ_Ã1$³% )ƒœ–PÑ+0öt·<¤ÚS/ìø8Õ.ÈÜ5“Í£Ñß]¡%Ç IeqÈ&ô4åДүÑCb’#Ay– +“êiÊ­ ²ÇÒ¼RZUxtQ¨PÏŒ~L/H¡ç“1¡+©á‡¼Ð[JhìfåC.#©´º uñLÆJR²‚ªK*í}M’•ùÐÚ@Šè¡„”úÛJ5ë¤t6$ʲ$^s=Éyc BÕZ†b¹Ô!n%©…¡l‡@¢n: ¤QI—=Pz,"Å9(ì™%ÑÇõƒóaêZ£õªë`‘@ +Ð’ãË+îÁ–¸Ê·¥äM¹‹ … ñ ®?`(–fy”³‚ð’¸þûàaÇì‹4¨Ã°„D88"[N …CÔd4ØIƒ*C!š+"1Àù/Pµ#ù*t#T=X¤/ŒÃ‰-}M:àÑ[z[!Ú{‡z0ØDJ`ä×mûdôÕ4G Yoƒ+1Ý>'H3˜˜¬ÑP…ÅŒ³0m‚B-AÌQ™ê¼g³«¢4ØÀ)hlã†æòjÌó¡èD¹ni¾t“g|8K»ÁoAVaˆ1Aï +🣩Lr¶##æè2ŽL[ᑯh eåPÖèt¡ê"c9¦y‘'2¥/ šB3@àBY‰íÀ×QgO;å +ñA(tOx'èí’5qÜ'°Ê0]™â7vÏ?j['<¡ 6£f¥¡²øÐu“b·+Yn*?(&` ®e+±¹&ådž/½¨Œ ­Á¡¬ÇR‚(Š§«Puù¼d4}T=hl í.ÚÒb +Ý ØjEH›2£1´êÁrl圩862Ô!̤„Pix„™l&È‘Ä»XÙ”üMá5€ó}Y¨éÒEEl +­8d‹‰¡#|Éžajjd¢d½»(Ž¯Í}z–àÈá3ñ¡©øž² ß—vXâMÚAÒ- ÖhÊÊì¨ÊRÝšìýkÐn¥WhÉ—…é ES²À#Ø!Y±-•ŒÁ(ÍŸm)7[t\ ±-Ü;[vÀ˜w Täƒ Öc`Œ(¥8^¼#‰-¥ÀF§<·FWw!HÈ^6˶݀ÂB.Â&²Baº#…³lýjkÁVv3PÓ ®‚¥Ð»¦°(zFÛŒëäsI¼€‹À„¶ +˜šË¼’-µSžLyÌ¡¯Hd¶!Û®x¾â¶¿q¤Ý-ÆI–Ç‚|Ø]Ÿù^Ç´ÃxÑ0¸Þ•¬Lœísz)‹btß–&+Ž#{æ0¸&‘i·±gK(äO~,hDâ;Š]±HW¥ºU=0I˜ÄóežpW…¥À‰qÆFŽ€§ÎÂñ4ŽÒ`êàØÞ [˜©‘<ч+´ÙÒwNÆéQA©"!ȱà6©ˆ,·8:™Ç¶£X(ýÛ¤ZÊ‘-Äq¹Šl7 ¡K&%Juw™dQáS䬠Ïf¤„j"Z…M¬ƒüŽn)˜IžÄ6Þé„• ©—rã&‹NÀ*È+_Ér":ÛH†Ð‚ŒÌ!‘Hx åĶ)g2Ïdr=]rF¦éKÏË•hÉð,éÏb ê ÈÕ”b(p¦‘®~ž¡¼µm¢r–<¼ÀzÝt0&…r‘щØ×ggA:j´‡¨^¡.CQ¢¤_I9–R´8Sz=€@î o#²:ì`‰ú8=0×bÀ´>;8lµøÊ·Ù PZúèärx¥ÙaœÃÆru©“V»ããB²ûBte†`²a¹l®ç9ª² ¶™æ•¾;ž2òRÚÐC¸ ïm+Ðji +B…’”¨Ð7 ® œê‰°þQ–4ÛòYR‹Za[ÚÔIsAž¶BM–6¨¢£ «94µ ¶Ôq&þ(Ô%÷S(·”ªÍ2\CrF¶òõ£@ý6gZS˜I eFB2º&hÌ=Ép™À€[…ÎLBY H€;y\…ð ²ÆgiK\Þ,½Ñ(/½MŽÀ\¦ÛL$ø’~ƒŠŒjBdŽGa×°\jj°½Ëæ*:Ú¹s®‚§è*†hɶÔT)ú àfé×HQ à*¢ 'hÎg;p£ô”—ø<¹ªÄ̹ÛÓC\ +™’QÈßW“õ8§Œô+£zó9€i +Ýúa™åÀa€Žq̓4› h8æ²$Ÿ{RG¢é:ò.:D˜'›ôÂv¹`,ÕGPhsr •É[ùêƒ^ËaE–êD f¢³=WËSKaW}²Ù :0<¶Ö‘ÆÔp—mO:ÚhCsH&gÑ ' ÆOâ$i‘M’¶À8H)´E˜´y´$Ǽ™„c&sˆ|Bd™trC‘«K’Ã#Ò•–›2p'9s†Q +[Ò[¤)ƒÁñšÌéJíO—Fu #»d›¤^¦B曑’"-´[ +ÌxÑrÓf«%¥°“R¨å_¡É´Åg{—€Ùá`T.ÆÒa,C’0`6Ù-çéMÐ`l àLMéJeH_%®ô8O$9¢èZh ìG‰¸ÊWÆ5šµ +zÒïÅ÷B|/ûúaÜc% ÕT”4T—Ò|›gâùli•dùý°¸À£çX’œGB"`:u¢¦ å8Å•\26ÕùR!Øñzš2„Ñœ-‘¨¯4C®aKCv‹^ÂbeűË="%Sb°@ƒJjQª°¦ô^¤ÄîõJ\Aì’'1K]³Ú!`Ä.°b%d‘m¨¼¶ K&€jãK4‚‚ƒøa™Ï’ =ˆÃæ(6Ù¥›Q9>I\éxÂÕ4“íB1;hÐÜu-)‹4‹%ÒÊVî=ÅPC´Dnk­XÉP|R'KVêäv¬L¾¼+wpV.¿ƒ+múìWeñË[¯2Àºƒí*—¿¼å*—ßÁnUnúË[­Jºå¥mVÍÜÁb•*ßÅ^•i™—·VeÊéåmU¹ø–ªT~;U^ñ¬Té:ÝÅF•Ëï`¡Êåw°Oýè¬S?‚|0öëÌ£ò»\“¦õfÕ= Ûãz ‘„¸:zkH¼Ívçå¹aÐ ÐàGr0HC¨[×QckPO8@*ÁMæDÄ‘“PøÏ"¦5àÈ\ö&&•ó" í4ÈØ&5D°L“Í- Ê¡¨IÈ‘Y‹›lö6Æà8VØBLgÇÖÔ¡ŸãH“ÏaØ\ +ä¦VÁðüü‡%´\=7AfRRjÈ$Œ,jqM‚Kã.…îÓdà–€¿|—!¼ÊGãJƒ4X±UpNN|!Œì îqD¦yyk‰Ùž¾ÜZF´À„ įði:í'ML'åFYáB BØÄ\`&ÞW™´¹”zœ"1rŽ øMƯЈ`Á(žŠ0é†ÆœÕB$Гôië*K£O®´PH a–Y”j”¯‘®.ó×r¬ÜrSŠQC\&Ç`#QßádGXY +[C+9c(Š†O¬ –ëÒèRò(©ÔQÊ‘±A$éZ!¶”‚Á€ÃQÔ=Fß`?n•QA¦yÖC¡–ˆùRIAا×ó¤5(„Î`½¶¯¤˜oCÅ©u¸+å e§B¿~’‰€<žæ€¹Qˆ……”À¡€ø¶T¨c*ßeÌZ¼G0NÛeã`ËðB<]º}[¶Œ"€Ra/”Ô È° zi»*€!­]‚pÀ(ÈÇÁ×Áw$ÃŒA(ƒ4»@fQÌ 7Ê;NÉyf|J–@s¥ðŠ6…¬l[ mpQæ 6)ÙÈÆZr(†”—Y|VB¾l†Àj‹Ñ€(2¦Wµ‚U>;…ì9”Ë[¶‚勇sHåÆ+3d!ʳò28˜ÓP‚gAþ‡Ï”*]"Ç0Ü'ˆ.R!ˆ\˜ìÕ +ò:ìñ¢\HL&ÑÄF¬h UÙŒ"ÀÄÔ’×<¥$Å‹Lžçf°“2ŽbQÖƒû&;{¶”1½ˆÏH؃€ÚÖ“2T—Í*0àªÎF(RŽ[ây’¾ð•z ”lŸå¸õ³Y9LQÞ'Ìf”Dˆ2†X}4š®)ÝWÒo1öLAEI¿ 2¤Hȉ¹±`fÕ•/q0>ž-m‰e¤d›„WêJsš-Ý L1}ü[g;}*$©(ňôCº +à ÑfðH¨PÓpo &é R€bp¿Kœ,©ü5 73²À-…ád|xv‰ä=rt +”j&¹‘¸”Fƒ<©10U êRôpª‚5[­1v@ÀàÈxÄøºU¶z¶¬‰›@@ÂtOæ4‘0Ìêa°µ6ÆeÜd“ý9%*J‚Œe€ñ*dŽ ]†ghäê!:„r]SŽX$^õX¤ÅÄÐ8qWØ„Ú’¹éÈMJrä¾Ç¡hP²Á„!EUÙŒ£…è ›Då·†·ð•!;2ÏkË>fe:Ç—ê- ›¦Á3XÄ0R®ÜbÒ.¡‰#kÂ!ã-Å9!-¤%]âH¶ÆµT”6+°Ûµ¥ +WæŽâqúŒá$ÎAõ´É=`¨Z*¤—s0ÝEÍúm ³Æöµ c3¸®TÁP¾t,н К`0åK¢ ŒÁ†ÊÒ¨&?â$½âtfzÈ‚™I9°OÖÙ¬Y +Xä˜Npl·òMùRÀÁJÇ“Ö:° ì>áÐ,¹ H ûM IU ‹­™«#k(‚`dR.I¶ÇR(ÑoJׄ˜‡}*=I[»Òè ûšXr}é}i!+'ŽŠƒåì Á!=f;Ë•HÕbÊ€Y¬„¤nØw 0p­*¨ì:ʬe²½b|dNÞ 6W¦5”Ë ül¥U`Ü!t¥V2–—í„’F>Ro+³€’QÆ8\QA”ÅáŒÍ 3J€hLDIP¬Üü ?l¿îÚ²gMŸ{Ò>Eú 07#ÚÉšMŸE¬Æ ,)N³CДj’ $† 0ê>$þC¿H¹C9Šui?‰l§\¯0¿ÀÞE>_Ì^d +ÕH,›Í×j2 pšÎ7b´ÿtQlr[¯gÃH\#2 *KPnû’nv OÅV¸ã‘”™ ™{p•Ë9£/ƒô`úJzJj´P™œu¬Ê‰.r¼P +LMDsM5^Ã2"ᙩB†xr%ªœ³õÉe‡: ö'-\Æ3KtÙã¼FÀ•ýœ€ËP’''î‡}œ.ó“ü;^•ÍL­ž¯Š«2Ä'ó÷>wÈÇM¡Gɬ/r±¬ˆxeâœ/ºV"ªFÿz …ÄJ¡¶%þò½5™©g&#¾i÷>÷dñs?wûŸ¿lùßÛúÓ:Ï·yÞ×ÍõmšóMçú6Ìù¶s}Ós~Ùù¾©9:ß×ûóé|_ïÏÏÊ|ßäÏÇi®òçã4Wøù8Í~>ïëý¹ \{Skú¸ÍùUÍ÷£šó§ó}ûçùºçûé\ß¾¹~Ô?¯c__×ßÄ\_vÎor®/3ç7=×ÛÌù6}}\æ{Ûµ½®9¿Loû|ï²ÎW=çWu§îÒÿ'a¾ÝÆ~s}UsþË[öõ¦çžÇ§ó}½ó}s~•sý¤Ï÷¶s~wòe×y—yÞfwçeöç6ó¸éÏ«¾o¯ên½é9¿êŸÓ\_çœ_÷ÏÇi®¯rÎõÏÇež~Þš9¾#~nXó½ÏÜ»÷™÷nRûÝÏ|iÀó¾ô™w¯¯ú÷û¿ù›‰ûŸïº |¦oç?ýoÿÕ¿úïþ³ƒáÏ_Óó;_˜þ­¿þ›óoþæùÞ~ßgzÏùÝ/-ÿð_ÿ?ÿößþ¿ÿç¿ø§¿pMÝ/¯ÿäoþ¿¿û»¿ûÛýÇË¿Ü{·ª+æð¿ÿíßÝhbmÿÉ¿ü¿þöÿ¾ÉÚÄžþàý?þ·~ƒ=û¹w?­þóñ£ròú³ø¹Ÿû{¿º÷ÇÿUÍüò ÎXìÄÚ÷~pô~áFwçKK¿÷ãË¡ÏÞ¤î;_˜ùúŸ}æšý⺟zþ'¿¿üå¿÷.ýô¼öïýêÎüááà¯|~ù ¿Ðc‘bqðÿèéÚcøY]°ÿ½ËÇüý—?úÞw¾-~~ÿ·/õÏ÷¨ûÅÙßþëù_ÿáõuÅËü¥±Ò_ü³ÿðpíº9¼û™_þCã‡øGGC¿rÍÚÞû|_ìà0qø½ïïýÚÏ÷Þ³÷>?üäûm|ûOž_srï}~äé7ÿì?H.ýÖŸ}ö—Þ{7øiíýÝÏ?ýæO¾UxÔ7~ù_üçOø4àçK_h¾vï|æþ“oþäwÆ¿ô¹ç?ý Ú:‘Å©ÈßW~ïKñ¯ÿÙ·ŽFî½÷Þ—–¾óW?¥ þö·¿óï|÷÷ÿ£íð‹z糃ç?üÑåø½÷ÄUs?øö÷¿Ku¿óÝ?øÁÿéŸÿfüKï…ºM~ã/¾‘ü²(yç~Õ]”ã¯?¹¨–ÿÑùÃóAµ1ï|fàüŸþ„ïø»¿ð…_æUý»¿f>-í=ýŸ~#©:WüõƒÍ¯Ðßï¼#7ë½/zþNa¹ô£PÇâv}ÿ¯¾1ûÅÖ}çž÷ÿô·Ö6;Ôñ»_~üýŸÛ‹žÉ¦_ø!vü×ýîŽ~ýçÛóçýè¾³>”þÆO¿‘ú•_üìÏ¿ÓýùŠ~/ü{Ë¿6|þ£?¹r¾,n(?߶ù¾ûE,ÿw¾œüÆ?ûïžLÜ{çõãïm|¥0½÷•ïþøùàgqðü'ÿóóG¿ñè—ÞÅçûƒöÅaß]ˆð›?ýÉïUí{ïð¦ýNêËÍ¿'ÆÆ©½ó™Èã«Üšû«ð¢äýúÿ§xBUï_þè‡gâÞ¼û¹ÿ`äþWèE È»ÿ­¿ø=¸gêÜß»7rô­?û:]›wáá½wé\õß…ûûåÏBÑ;ï¾÷Ù/ýîO¾ùä>_ÇÐRð ýä÷.SC_ùâgþ³_üÊPòò[?ùæÓŽ*ÿî_üéï7g‡~}hfãù7~ôgß|:ÒÀCåoýÿÓOÿÉ7ŠG—_ÿÞÿü‡_2Ü ˆG¿ÿõòÓÿñ¯~òƒÿù¿÷õ³x_w´!€ÉôfñßÿþwÿëÏ7fz“ï~F¬iféñã¥éÁ¯|ñ’öê _ðY¹›=«¿Óø ¤J9°Ï¾÷¹' l).J¤xZº `È=ÚB-`ê +òÁ=ò‚ÑÀ3Œ"}@̱+é2ÃY_ÀmRÕ¥÷;FbÃrô,J//7å²ÏSQºÛS¦'ŒWIDùV³cO ÜÀÓ¤™A$*vª´)ý·r(’妘‹'c Ûä î¢äæ”)GeŸ“Um +€ÔÔg¨' Ke¹5LKº¦r¯ºFn8M3…ʉgªZWÐmû&°?#n&ÓÕA®ÇacÜ vÔÇDÄÖä +dƒ­l¦.¯¦Žãb#K™ÈJ>sªF}´UÞÌä +qy·ê™j¯NW™ïi:Ÿï9tñCˆeÕŠùÖŠÔG¸úbé2²UÁ¯½z…jÉj¹"êPXSÜïµÅ: Õ]Ï—²uhëðZ™j¶œé]7’näòjðÙõ’â‰yöîl>SÌ|õCY‡úè\s+[-‹°šÈn¦ZÈœóâXKçõ Ùz÷‰ú(uèA¼âñzy\ü_$™©]P±U¯–/óÍí¡%õÑÚ~-sÙ^µm·ËÅKùZïXéú‰b¦V+dƒm¿n[™«r¦$-Ê F¬¨м Õ~ ,…žüiD&$¦z¼”ˆ.Þ+Äf`¼cˆ³ á& ÛòÅstLÔÿ£Õº6zå›àËDŽ!“ ø„¡ñ]ƒ>ô^ÿí×ÒôR{¯Æ¢å +|ú!…åj8ƒ›º)È6X­B !ŠÆÕˆá îÀ¸- !$þá‰UÂÿ Ö«^‚+T¼ªÇùÔZ ׂÿ™ˆð B{¯E|ü¯ö:f&ÁXÏÝvy‚gÇ7ÐÛ>øu‚¶ÕgF»ìXHiJ»ìÀ¹xz`ëÇ|={‚Û-‹±iWi¢ÜsŸ2ZÿÇ‹!f‰>àòêÐ¥x7=ŒçZf«ô Þ©Ü^ÞWƒ~ér©\ôë'úªAR7AT ’(ü ¦í 0‚ÿ55üŸ¹Á”_vø€¢ñÅ‹w#– ÿ…Q_çØaRŠ¯E,±fþ÷ºÇ(Q¹õ¶.´ñz‡ÈB^¬ÜÑd¼ñ_í5ŽÐÔ7±\*P¿@:¬©Ú¨× +¹¼|v‚EëIíD,[/¼ŸWÕ',ÒMŠÀXL%Q(E¨ +å:c› î#{yûf7-ž4ÔFüZ¼¦IX0,–_®oæ³åjN2|…x"mZÒ}Ãðb9,™ŠÜû\dXuŒrq1¥¸¸¢‘áZ>õ~¾´–ËaùTç™M&Ö3ÕzÇUb‚ƒ;-{Ö©Ýp]¡õ=tŸxïUÐì½ ÿ(®U¹òõ+£ŠÁÒ”B^oWÈëb©ºXªÑa©˜i¢ùC]ßâ°åR®Q¨w[á-6ì•{sFWyì¢Mê«ùlæB±‹v +•Ka(´U¸ª(z‚úK>ÁuEê=ç/6ò‘ùrùí†oæ©Ü|kßÀÓøH@ým/Wü4@ñ 8¥œ<¯fr…¼¸y6PBUâ°B~zHÆ y÷,?éc)8­Úþø9t¿*äÑ{‰‚Éldë"“+@YsB›8úÍ*⑽Q3åsnêab»P/Þà +2p3Ÿë¸(W¿6ÒTò~¾Zģ́ÅR®¹Êi±Q¥*š&Ër™ê¥lF%åJ&+KhÓ*™Bõw”vˆ¤l‰rµ”¯ÖÚ÷¨és÷]jéå¦û¤Ûr5p§µ7I)Üô…¼F”‘ÈPíLå-"`ßu™‰=>š6Gž‘#ÏH7"¯ƒÊyóÄœºÚÛBͽn”u|ýÚÊ'à,”Kù³å«ÓÈV¡xùö€œWÄ3†[;;«åë˜@¯¿…>vÇnM=Ü· +SŸÕå[²äªX¸aÀDÀóR½òVÂ$ÈE+þ±zÃàN×öeÓ[³r‹ #4bäCºTHàéø7@Eo>¿µB§äŒ0º ¬44Ã~á(Rùä +¦¼×ý?œe'nR7FPö%ÈFê– æ%>Ø>¿9>ò“ˆö×/2¥z¦v•9/WÁ²ömAûŸ +‡>Fü•È”v 墠¬RæQ”dC*þ?ˆ\§=jAV·× ^÷›×õÚ²¯J;ÂÀ+£9Ä ûÀnBÚ6Rå}¼/ÍëÄOa$"E$Ÿ’¯’Ø0"bîÄêþò§ÔÀ§ÔÀG +âuÉÚCRp׆ot÷†ÿѼÿ×ýäíO‰×@ ã­ÂúÉÌU¹qVÌœçßjIÏÏ ºoÔê…,˜¿=¸þA«·GW÷ZEÁ¬×hÔ‹…R`ðørª–NnªÝ¸¥‰ÁùܶÆð¡§Yµ¼é´L©a©¾–oÖ¹äꙢls7®ir¡XVdNkåb£žW_T§bu_{YHò©zõgÒne«…ÓÓ⇑tæƒ×lßøs³Ûž›®uzotIjW ?½%ðà=ÌG2âzlÏ\¶/ ‹»/‹[ÝØ(IηPiZ¤2æ²;sQ›L){Ñ\R¨É%4——õ›ñ±â™ÉVGsÁÂ@§Ôœj ¹éŠ!­×‰!?bB ‡jÜ}ktÂkÕ ƒ$#–íY&»f½I/ƒŸAÄS( +ÐÙ`§ü‘"ž…)é+‚­×„øi]~—ŠÝ7¢½Ax7^%ôŠ=à´[ûR0,ìÈá!6Ép²P«3ÒŸXob~íôù¦èf2ÒÔ亞»—‰9 ó&Ý’Âø$œ`¯ã³>=¾·÷ø¢>X”A¸;©'Û_YÝ4Í^Gê~Rôm"h>å’?"b¥|VWªVÜlÞ©ä§È7ÃEøŽìƒohs먄Â7é«ükÔHý69r¢HWóùˆ@/õ2ÈIZrøkw,ÜÜÇÙÍS²©jV#dV¸ð}ÈxZ¨PïTSçšM}TÓ°pa§>ÍN5MªéQÛÕU¿¹Zçj˜~T·L#ü…;÷Mñ&t=ôEïÚ¦}ꢰ}êPØ6uQØ>u(|ÿa}+–ËTÄ_Ç<ÛãùÓâfû]˜Ï4j#/6ªQïô¬óehªö"½ó2­Çb¿‹\¦žÙ’²Ë‰xA¶m%=;)úÈaÿ}âçþÔâó‹Ë¯Å¼ìÎ}{#‘Í@aß?œ…½À®Þ ¹F³{ýšãŸAò¤ž©Fâ*øð|D‚”- 5:;TtA +~¦¯ÝÁ&Vºù N ’¾RË÷d dˆ±ŠŒ`¥*Þ+äXÈ/]›x"­ò…ó‹zs·›ùbÐ>\ªìËb’m~Dž;ÑH¹Ç¬`‘«ôÈö£‘Á¿Ò'°]Í”jgåêUmüIe²=TÛ¡.nú +ÍS—m9$R­ùJ^•ßÏ/¯×;ñ”W‰r¥¯I}4–V +¥õr¡¤^+†Í•¯$B +iªë™zþ8™?ÄP­ã7 +ˆÊߢb.›)Š©¦3YA?‘òJkù¶ž¯fó­Áo+…R¾s»=zÜíÜc5VëIÇÒý[“–ëÒ%êãÅr¦q¹ÞÈ^Š_Uî÷„kÊžnìàé™QMÓÊèø,Ø“›-åµðQCs6PoâåSøð)|ø>Ü +>|z×Õ]wz]v=ªY®ÑãÆëÝï{(ÈÎ-.¼Þýº·vøºïûëA"Žƒ—íSìñ‰}QfOôµ€_û‡||qÈë‘7raÛ +£.ÿ«C ;(œ-sÞ¾&Q9Èœw_W¸ ëda­ŠÛ,¬ÕýŸ~Ô:ÏcA±¥Bî<›qŸHÍËϸt6œ1ïSµñϬÚøm´Õx£H¢ŒÖUI÷¤ªŠŠ@ê*¬¤¢õÑõ1H_°¬e‹Õ¦ùgkÕì›Ó¼ÑZŒ7 Ey¦Q¬Ý,â4Ón7 +:ÙQEÐ55ÆëO•r¡hýׇý_½`„h6ë§4ügøpS|¯ ~ vàÎéI –Û\|ïsëÙ¦¥ ï]êù¦r¼wbßøn®õð‹§ D:}uð«Óå«…_­._ üj´}Õ%ù<¼)nÔê H öáß }íè]à»ö@„ꊢ}ñO¨¦ÍªßK´ï“õlèÒ÷ˆV4†¬i¨ èÑP=FeÄU¨5/ÂR$1ãµL0êD´ÖùɺŽÖ4Ã(&¬jš¡¬i5Ï1jD´Ö)ʪ†žd í¦CmäEØÅÕpw¸…¦T„YreQº:cºÑ`šjw‚yr][Ã] +¦ UõP]#è×€~u#Ú|@r;×~ž—§Ó|àfû‰«ê0ÑÖc7ZÏ]ÕÆm:z½ýìUm˜l󀺾Z‡hâmðƒí¥ ;-û«ªãœWgœðã4¬æMV pëÄGU›gݲѪ>N\¬6´ÙxweåØ×Õ<ÞÝæ[š¬¬âhÍ·54=YÅj¹¦¡É*FhËÕ‘ïç‹E¸C€¡ùŒ;|j‡ÏMÇØôãæ ðc˜‘à¯sT„æÊA7˜r¼ü<ÁÊ”c¸xmÏÚ4Í3Q®T$òíÑWkj +!kNº¦­¬×uaád×ôªÚ4—mAŸA®«ÈV£$øþkûi­O ¯æsb£ªâYæ3¥È¶ k7 +œÐ±!÷¹þa5sUÈÝ°#U›[ß.Tg¨:·_j”΋èZ^¨Üt1-m¸'za›‚p¸a7áÜÇ-ãQ„ëSëñ€–QËír­¢¥‰Z»×}é^Køf¤{­®]÷ZŒïµu¾Jr¯ƒµõ½Îù“îµ…‰¼×:¢Ô£Å½d’']£”³ºú99ñm¨p t¢…ª–kµÈB¦ž½ ¬Ç‚â­a¶ÁwƒM+ ¼þ¶î–\DÇW›©ŠÇúWrí"O1$*èoëBËyýwã6ïéí‡7ß±–m¨}ðf±i­XÈ~²P)®h«Ü¨fóqPq½Å‹k™ù‡×ÏÔðô(æâ%-Å[õ>ì‚9¯_• ‹rì·rU_í¼ªț㺕ٖgöV®ìåÒ¶º öJ¹nyºïD 30ây«Öw¸Ö´-ð´\$ÚJþ¬¾V-œC8¬k—*õ€oÕÛ×ñv¡nß>)˜ûÕ"­W·Œ¼¤ù¢Æu=²^Í×òÕ÷ó‘íüWë‘T®PÏœŠ…ºÖŽm›6ï~lÑdªõÓr¦š‹dËÅr5¢GªŠø½¶*ØXÊU؆exÝ+¡~¯­î÷ÚIœ·êÚŽOÛo ÔC#ÖJFIöC1x!QAŽµ¦í•{)ÔÊÅL=/ºÌ#™ß»öeIpåF]ô^nT‚º<QUÉÌMÞ¨€Ô9ÏGÖËÕÀ ̱aÚkzEt¹™©ÕóÕÂ×PƒÙÌ£AZ¡,7P·›­æk×6麇í«ô#3Ö +Wb&ÔIÛµ–§(:’ý¸òr•B´µïL±Pk-»ÊÔ.åf(ë¹J&— žGl1kÔËj +<‚v¡qZîQRu:ˆ¦}UzUõHˆYBÓxy|ZäLU­â¸ãïçÁ¸5rš)fJÙNw)Ü&[,Tă@è«â¡œ‹¹µ-½¥I™£ÝÖÅ3¿aՊïë¸"Hùý|µßZxÉ´å—±ÅõLUì†Xjm«q*6']57á,ç%°±B‰76{£‘ˆYÉ¿Ÿ/¶B±¦®SW§ù\¬XÄ΃~;WÍ#žUbó3kïx½Z>+óMý¶WÝ»(d/:Uí0…õd,íÄ.4ÝΕ¡¦ØìZ7Ó‚·òÅ…L]ÜÒ•r6S„ÛXà +Ѧª½€ÅdSÝð÷m¯ÂÐêÛDØH¨e^mFD×—G0೉ìUT^µrý"_¹QÕìÕ‡o7\·š«Ö¢$ 6Ÿ.m[5yékµƒcMAçˆ-ʵ\“pµ¯V¢Ùr ÒjºkAÕÐãE0Ý©ª@†-àÙÔºT­¶Uí¸ö +­üšu‹Zâ5µT a Öº™\¾š¿¦Ç¢žÅçÑ»V¶íat©XÊŸgÀ<¶G­³R=š+Vªgå9×p~u½ÊT/kå³³h½\¹yå¢ Çƒ)t©-îO>×Üõ *7wݾP1ói¦Úsï¡b5djv]ÝŠ > +¥³²¬¶Ýf›ËŸ‚ ó—Ãµî” +s¤Ó+ÅfÏ˧ +endstream endobj 220 0 obj<>stream +u0½Ez؉¯›ó‹í¸ÓÙj. +H®˜©Dß¿iE¹@½óK€ªèÖ£z¼¶¢ìÑîùD­J¹V¸æÄ š8‚Š²í^­\…$#×]l±Uøÿ”¬ozV–°¯R®÷º†âÈ£Øg¦vZ¨_eZIáŽuËל·ªxÊâ¿ž5sùZá¼Ô²ΧR©F/Êèÿrm½nX¯IBáúzÔ?Ÿ•¨\¸O0J ¿„EºÑ»v5Ôg袪`HjAL¥Î•C`¨òƒŠ92¹Ùc®Õ‹ü–+•^XêqÇAÅô-*_‰ÏÅ°xâ­àå«ÍB žs¢ŠÃ™.÷JÀkpâ0ÄOÜÍ4]£3ó)(ÑÀ : ߘáèV4²—?$Ê‚·ÍEžoí­­?‰¼o\³$ ÐÂï´ÓƉJp(7;$»2¥R¹ú#*®QÊöæñz #¨¦Ðqw¡«Ê•\㺵kûÈ–{ì Єł|‘n'\ ª´q“©'2K×T:Åø`=îk!ö¬iU¬m=™>–º£ÐP @'2R#ÔÞ¾µ#™j>R¿ÈG˜"Žˆ« j‘.ò¥H-ó>L+SŠ„§tb$SƒbŽN‘L+]I4²SÃ.Å¿Í}XnD*âuEÊ¥Hžæ…CSwçà]hh,"SMK¹Gêeè"›ð²g"Å̇ —ÉT*ÅB–ô!µFö¦·XJ"¯tC£•Ä=kˆÙ•Ï‚á µH£t žŒÑÞ‡wöZt­*¡ëx "“ IëË…*›–7T2Üph1g±é™–uÃÆìçøaGJ§ÛˆóQ«ÐÇÉ!¶«â{Ji;¬d½,þÃVàÞc{)xºÈGo®­ë,Û•[²Z.•³ÕòU>)nYwÕÞu¿< (ÕY¥çÑ´/ws¢ËÂY!à†oÓMÛŒn°–ˆ=(wªµžc¯v<Êb)+ÅmnÕ.ÉT{q­4&ØŽÓL}%óa¾§xözåÞ xg=_7¶^ɛ肻4¦eÆaŠ‡ÙµÛsಯ@?HËãÞÜBK³í +ÿ˜NáèÔÅE +"äòÆ;²Ý[ÂßTwó:‰]ó49\GÙŽZÊ#ÓRÂéƒrõr»·>£k»ðônײéZÜ®ih•Ýô²ívµpµš©^îå¯/EvÐf;08êÙB0:‹!MHª’â½51²®À‡0‰›T•‹l¥8;Otg±ªÜ ëF%¤Œ^.”$Œ³:j%íD´’âØiùý›š0Yh\Ãvz4´ý‡àºXwô ª‡–ÇÖÁͼiÃðüŒ.„‚j×k_znjsã›b÷Mo¼xko±ŸŒx_bC[[ÞxG±áKoiKë[íiKÛÐŒ{b%T÷ò§»…ü797@ß™z~û¢quZʊפF—„°l;,<ì$É C†VÔ©È°øD7¡ØEI »n`M'8×¥òid 9–Z$tUzæš°­ ŽNïÍ MežŒ-™ƒkãñ¹êüÕÅäy©o)Ý76<”(d¢µÎÎBÊ韜ۙŸ]µM®<Z«6²n:e¬zºeõkZ-ù¦=˜›z}87=V©ÍÕ– 1ÌÜÔJ_UÖZªÇÏ6V榭üV¢03›MF£Cçmc­äöÅ€n2=0éÌדÏâÖÁøX쪼R‹-nÕ/FgþF:i=Ø‹?/í‰a’gÚÒiÇÞ¸þ™»»qø4¶ˆîv5\oòhnú2}47Y‹^&ÇéáùÜ™÷+}"hãäÙÑž/ΟLžÅ/ê‰ ÷@oÚ‘“ÁdV_y17ýhh:“®%ž?+‹ß_$s‹}ñqïùƒØÖx‰&ñ$“kˆaüçãÙTÖÞN\XÇSÓ±sp4þxìdt.1´“Nägw—ú/¦²ÙÌ%üVM­\Ðк6‘q«…'“…gK¹xqàÑÐxuôi#¶²5ø027µtaŠaœ©Ý£¹X);t5:³:5á^=)¸îDíÌŒU³‹úè央ºÌ&—j»bçÜ¡¼»gj¹ÉBb"#Y_ËÇ‹îú­ae`.±8Ý¿—ómÍ“\<³ µò–Ù}ñä1n·`iYÎó<ÛN>MžM,¿He2CâÎéΆ¿>ðd'¶–ˆ¯'϶ +/æ^Mž‹aâÖþö1mè“;H룻qëIl-|¾wœ(.j Ü'XMx«–VæcÇK:Þ›éÑÊÔIzøl9Ó¦·Œ‘Ϧi&Í;â46ý|zx°2’¸p6/S£+Ñtp_Å;Ø/#°ÙJåà¢Î‰×u5(×?’8¿HÕÜ©ìÎfÌ=0öZÏa}¡¸ÛÔùýùÔøØ©ßéXüËürB Û^Í +`3ë'ã+û—æ‹UCõæ÷Ý3ñxR†fÌ[«íק¾žY/N¥“ö1:;Ñ‚¥‰ueÏÒ©¤íÆñµ]„=Q}aw GMNœTÆŸ×sWñbi·Û¾xò@ô±üPuPI•éÁq÷0¶yv1$š=ŒåÊÃx7ÔGæñXz¤>‘Cˆ˜¢ù‹ùù#Ÿök{åüÂÛZ:œŒ6Õ;½˜ºá¼ÐžŠ)ž<P @U!™+ÎUûŸÔŽÇ"k):›í¥†ŸÊ^¾äýÚ|Hð³ëW€ÐÅtù¬c…êÆÂãض±›¼÷Ð_Œï'N.“sôµ¾\ŸI£õØVãA!uºËÜ;J./öm©¯êB×WÒ+¢Ö2¢3 c{þ製ÑgîæÙPÖYì›3RfŒ–“†ÙÛ˜§ùÂT©ìø 2Ë[Õ´#T/ø{ÃãÐ&Ñe579\ý&»äfMâÝ€³év=ºßèӕк‚ûÒa¿ð Ói¿p¹´rGB«†Ê÷/Hh;äªMkY84âUÍÓ ½V0`0*öG£Š¯¸šàÈš–¶ºÄú°ž ¿Lhï tU±S¾TáÖÛ}Ý¥Á]Ç *Ü4uúA#Z¾Äe¤c‘ ÆMÃ7 £§ý‘5þ Bk‰ùÑ·­ÑBL ±² ákÃÎìŽõ8æO½þ¨¯,®ÍUëÃOSÙF¿$ê  µ¥yÝe è?‰o<Õ$‚ÙL§Gú.·=Lž<"ŽIJ¢T®šq`S½óì +2YHu¶òYã*ªé†X;=~œm–F‰\uöi:åm.Kr§VÒ1K 3ùXOˆ[ÛiFëŸÛžO>.çú7g¦Z¾Æ‹ÅLÌÙ;<šL.Íô=haÞ˜îz:¶ÐDÜ6ïͨàC¶_Ëm£@â§çÉüÃÔf/ÙÜAvs=níS’ls‹;}:¼pêLÏU«;ÖäêÞ#$s=»PuîLæ"ã!z›i §“E=®-Ù“âŸy­ ÉíÓò)w1LÓEu*Fx +ØÞK"xOÜá~ ±Jó•û§Ödîêò„Î! ›ÚPWlnÏâÝž¿Cªó¶,Þ­ù; ¡;gŸvà R§ÚôÃd¾ZÓòë3S꼚·jáir9î ‰[2rŠ·_3ÍÉÛÏ;7íZžc<˜ ðvøó‰¬óu߸äwð$¿–œ¿È šxcÎhMílr¯Ü:U`c¦kå³D¡Vp'/$.#b–«'íý¶±\åüþ›'ùåäÄ“…GâÛƘ9øxoƒ+”5÷²qº®­,ŸV7º¾¯)íÑÔ p$ íÌO,òhìÄÖ*ñ¦×ô±šâK¢âlŸÅü©åqõAÝyषæÅQ]Z!ΰQÞv禖÷ï‹'pÑ'Ù(8‚I¹¬Å¶æsÏÓÓ©™ª1·ÓçÇ;ö`âpf¸aäã ÛƒgôAñ¯^1Z]@´‚n×J›ñ–¡ùy¶Žþª‡ÃÈ›éÄEGO£j7ýìèa:Ujœþ•tî¹Ù•¥øÆúܺ1²õ8t·vñâñ‡½ç¦âÖŒñÉrb1Y|"îÈÆxl;±³“ÊœœŽ¦vŸ,È0_Ÿ?˜8)§‡—ç púKÚRêE¦c°Z ΙƒøÂV‘yuÉ &O­üÝ´¶{ãÏžÍ5¿äpo7|ÆJ†ýº—¼0ž\ª><¹þ‡fÒÂâöŒ¯_Hi@¨ËùxÑ9? ^\ ¢›\2Ƈ[ú ?ãñÃMµ¥–€› ¶K>tÎœ5^9Ž?›Û}P¯ÓœYÞ~‘œH›$!U¶ÅÞD'š${«y?Û˜¸[…È?9qz¼M¤j§“cÎè¤îõ=Nì¯×Ã(l/7ÂZm/¶¹=ú4uâTêðÛó0eCð›XüsBჇC‹¡OΪa|ÎUGSÙ‘ÎeD£ÓIÍœÚL¦'cY¼è „Æ×—OséTrÄWun;¹TÛ›:[8=Ÿ›Ü›ØC™MòüaÌ=: 0„`œéá¹ê¸ßHqª¬ Æ~èa‡éOJeEôòtu+9¿?w–\^ÞÖãã“ù +’< + + y±=ø`+}Ü?øLœeÌnu©‘ŒnæÆç&?¿ á;8/ç~º”`±0—­Œ¿ØšJ‰ë¶rèdb¡ ô­<½®«ã^ P.ž¡8Ú‘óÄùùùi̬÷­‹Ýx¡}qáz–?]‚+л–=pÒ¡Š/NôM² ‚#„)¶Xpê~ܲ'_Èë›ö»Z‹ÑlÏtêtâ Òá‚2SÜßéQÍY­M¦–ç¦â•ÙÈ×"1627u´³NHz¼Ü~þ°¸-ëÏ榾éyÏÜV2¤WˆgR'ãçEt–£©g™­õØæYÇ‹øïžgW§öPaãçÖµ½ùØŪ•î¹<ìXÅ»˜z’Ðw®j£3KÙvè_ÆRÛ1×ß<Èiþyly/¾Ú>Œ¸2ý‡pú«ñ«µYûfkeú¯¹—Á–žfÛÓà›ôúä¥<­…ñ¹Ýå'NΟŠÛj^\5îcöÅ•xqç}s/eóM£5bÞiå<ôæàݺÕÕaO 3?zh†Ž`fÙÐ秹ªÅ£Úå±V<žÜš©NíOùOû+É女˅™§–ÑziŒáBlýpö)À´­Ævƒi8gCKe³³z¼øïUov58[~ †NG; }onº˜étíU•CÖ] »ýFÎ×áªùF¦7 Coj°QÉ#ߦ- –;$å@Ø~5ì•ãÅhªþ|}.¹˜Û¨'—†‡Óð´çv·3p^xÓ6 +¦…IߪTûsâ–XcââgӉ앆¸KzWˆ+†™Ìå²Û“Ÿ6Ò'ÏŒ+±ÉLuíb[§•3æ3ù6ð»°½™xœ¬ˆO-?H>M¦cƒKsÓ ‰hÓóõŸö Œ÷ OâÂC”“–€sg•Øfibpæir²ì)¡7¦w²2±¨wyÞÒ Áféé„3yº6óæku2^ØÜΔ¦ç‹%q^—¹\h æÃ᧱±ùDìùÎÈ:S1mvé*ti$ˆ­1˶>8*†™™¿ðÄXcóóÞöS3b{És{°+_¬= 8°$¬R0ŠF +ÑoââpúE:¶>«™\­5ÊÍbQÇy¡û›Él"s™|X˜ÈºÓf*Ÿ:8[L)! VÙ´ËV:=§Õ31oñ‚+€(!•mÜæ­-˜Û¾9¶4•>Ûl{A¹Àvª,€ãø~ËuS$Î/ÒCß•½N}ÄÖ*Î|¬ü¢6ÑH €2vN¼gíáäRfw5Ø‚&1ú† ¾WÓCóéAp&3‚Šó±½÷ š\í·ÃCëRÙÓ±ˆ»qÙÕ}PEÀé'Ï´ƒé&!Í‘;7õl¼üű@+¹A³Ç3±r,ŸÆ âì¨#s86ÜRÜ--nëÉ£ó#ƒHîêØzN<Ä´XsBJ¥çW*mS´oÐßuœ¥Â¹³=¾`¤úÆã¥\æ¾W“]€Dhq=Qè;J>̾HjÆK(zÃØ}‰‡º`J®׸¿s:ÂËÝü‡³÷”çd³}]3I}à#E†z8¥LõZÿÂ]4Evn÷( e8kIàƒ”ÙÔäGÈE)>„‹:›üÎÚÉK±ð“Y-7¸5‡Îú ž̸gzd]LŸ¤Ëhêj¶pœOT«³™Ò|q÷ùiüdu#+éGŽ9\N-6æáU-L¬¹‰ú䨰WÍ—®´…“õ„€÷uSløÈš5½1iºútBÓFx@EVgóOûbk϶wU`=§¯£+µjÕ¨eúÆ'†6¤ºLö äuЊöÇÌ•¾±Õ'+}£÷‡úFêñ‚61³?¢†Ù°jFmõ.‹ƒ=‡õM=Òw½d®‘J&žé‚f*ñæƒ 9ÕUÖ5e>ڮlj6 ÀŒx*û³ê«1:³é\ô U¶_ô]<_èÍF`5cǹþ1#½Õ7¼Pƒ?÷£æŠ/–­>›Ýë<êãøHÿN¥¸ÒiÔZÿ3'ÞmÔ…Ù ·´Kï¦Ãr­‘'£SÙµÕN£V'“æûN:ª¥µGÓ-£Êç)vúxSg~—Q÷ŸjéíøFǵÞOW&Ö +«›Gï/±Øe¹NÿÐÒŠµÚíhwªO³ÆŒ:Ò~®÷÷Ì»>$šYeì‹CG;ù`–G]j9ZkÛ/â¨âU¦šG=ªížnvuòà÷Nî|"84ê³ñíÇ]Gu'ζ†::Ý7R­MŽÖ‚QÉZZ ¼î>“׸mÔÚÐì¾ÞeTûbd(žŸï<ª5r„,ãÔUçåÞOgìþNcµÓ¨Zzy#ÞeT§ÀöìGÁ¨0LÓ…:ÖÒåííΣÎ?˜\É_ìtu~ÃÛí¶Ã@^ Ž÷E/hàÔÓËtÓ&ß© Íá&?luáøÊ~ñ°¢‰QÝJë¨+‹xÔýña8›æåºv4zÐyTë ®­\m¸G]¼ßðVö³^§Qجó ÁÀ-£.G_TƺŒz8ªm­¼htuÙ|¶’N?êÃQ‰ iZîöba±ë¨[«ÎöI·Q“Ú®öÌï4ª¦ve¼;wüpªã&ï>›©uuw8\é6ꪶ7þ(Ž£âMkYîJjtoΫ&:Žz0¶ý¬ë¨Ïïo¯Ä;*†?u´£ÌÉhçM~¼S¸œx<ÖqÔgÏžwµœ4|Ž£‚MQÛrŸ®j'©•dçQÓ;úý'õÅN£V«ÇûyÔSs$ 4¼ŸQÿaʤåfî×ç›!Ôdµa&4u¬mÔµ™áÏROæĨ3Õ–QG'Ëûc8*â›KÿaË«8;¼Oè݈ïë‹Í€bSK=>œ‡QÇÛ±ìãñ%sj]Œš¨£Â0¸É©Ôó(-÷Ñpj¬.åFB™&ËÍp1?:ã=>„Q'ÚGuîÏì/°/õµ,·Ïˆ¯]ð¨“Ñ–µ>/Ϥ.iÔG;++Í;,®Ôó‹2bY¦]›H£~ÕgL–N[*ȯÁ>3wUéüÕgÖÎwûZų9]-t©°¿®¥¦×Rôµ>8˜ÑVæL¿¶c™ƒGÚÊæ# ¿Â…n¯ÓVrËN·æ mµoÏëö5¥­e7[¾›fk[‰Ëz—æ‡cÚÖn_·¯Qm{nu¸ÛW†Ùé{í\aºO«6ú\þÚµ´Ý¾úÚúܬÃ%m÷|ü‘Ú´ö ++ÚÞ?Ö­ùcmÏI%º}]×úÝ£–¯¡M;|®=9xØ¥ùSW;z~6Öí«¯=Û™2»}ƒaŽc¿K…ãª}¸lwùšÖýcµË×Ó~=>ø,Ý}ÓN·ôåûK]šgËúãK£ÐåknDß¾<ØuÓìg¥í©éúAçæFm½ohdé1~5†f‡š¿Æú¢ s³ôµ +u@}ÓÖÒóP…ÄøÃÍ06œÛ—k gmxŠ‹Û—wâ½™}¤æytzû‘»Ÿè0€‚«^i7mæIÞÈ¡€•gÓŠíŸ(ÌœŽˆ·t?Uó6&×›Àoõ¾14³>Nhø ì j_Tn¢Óp`ÕÚÒ}ÔûéÜD×Q‘j•Ø(X¾‚Êtõ¤Ç¨óã~hÔÜà`0*òa:ÍlÙdà ¦ä¨óÅð¨Ó÷‡Ã£Z[‚Q˨G[Fûâ`“7è2ªÓ¼ÁÓΣZû‡]G;|e4ÓiÍËEÞ ë¨À亚oU ¾P7žt®; „Æq·Q7ˆÅm>ÚþÉ1^8þÆ×}ò†õ¦:Õc)GSUëéʺ´FÖ©Á cÅi!…^0^ètC°äC‡‰@žƒÄÁ¹¿ÁÛŸÓÇvô1õÏa˜§gÑX%üªNFK¼šM5 ÁÓ –a‚‰@XÉÀàH÷Âdy³ŒOL' ìaZÒÐ-ªY~g]ü98Àÿdž„¨é)ž¨|˜ÔSGµùЂC3žM¡ÁïnÚØj™6ˆ¨z œƒ5ˆ{˜hÚÃðîÇ'.òÉøG\ËGÚb§9I™ NKÕê2­±ÔýÃ[ªw â®7Zv=Ø´¦Çx…È~w\ázßõ+Ä6¥ «û1 +š^\3ûSÕ­,µÔݳ{æb!:TWWêúu}ŽÂ_sálþHc#SŸžÉªvÂ|&ê‹Ùe„£o¢‚HøˆH¡‹Úg wþ©R(Lš·p"m_¾SŸªLÞŒ'ßEí;4œèö$¢žüc“ÄZcÈ׺:-±„8s{R×&ÂnðLÚÚqru’ˆ•NnÌô¢Gq#Ylï¿%Ï møíoZboðr6ʔрƒ‚:C‰6í­©!XªfƒIÜžü´é²·Øû>iØìã24u_ ݾ ÙZt÷I$`qè #îä¯!7`p°ëoÚð 1›à‚v¦ÁÜ©¹ªÌàÛuùúgu+mIm[À>`GÙf‰Öj«e{LþUÙ¶ÀíºøgM¸dðíÞAœu–´,ê—'©‹‹Ãz1ûÉ’—W +Á¼Î!5åžßðY“Þ"·Ã"6¬¦+8)Œ.òC[º˜îŒlTÙ±ÎR½îÊ©Œ÷ЖWY›Lí鎈ÇÖ{±Æ™cò–/´†Ä“Ž]Æ—‰Dkcœ{ ÑÐsô Ç2&ÓKìÝvO˜MÑ3é,­=÷N³ÌÑÖ‚ +f”iþ™þ:Ïh‰Ö^f¢ý™-ÿÊh!é–mšv̉Ù|/~EðnåÕó›°HÊr~ìxšß•ilO#^gwð„¦Ãiz²¶çêùËX.Û³ƒ'([Éœq§c“b”‰¹­m¦ˆõ<‚Deð cÍ+ t­žwn_‚5›dñ@öMê`p$ü^MÌH : £G"3G°:SÈ’"ÂÎãì—§°Bæà÷
ãGr¼Ì_r~Q'c"qpŸõ¼òà¶Åß#ü!_ÞÃá¿e'IXåy#Í Œ‰F9˜ÃúÏElµi‡(xÞ`Šf\€9Ãïêtù]aë`†D"ʼ 3©Üü–fVŸ×m«sTÖúËÞ™dG:ÛÓç1\wpŠÚW_ÙéE¦8X¡ôʸÙèåítí¥Wp³ Þfw†ó¶ÜéJHØdˆE)³ccéÁ”)7˜¿ Þ6\Á,Ü%ikŠ3˜’È…ÉIÓÌÉÙÁ”ôˆÊÛ¦²tz¼3d Ä ©*Ö—8jhº9ýˆ›-fɘS +kYÎ QÓ–¤ÍŸ €"H„„ÑR£ƒa¡ðüƒbÇvÂBÁ–i¨DÐÒ^šÝÿ™ÙÊÒÉ$GüÑgf³÷ÍçÖÈ16Ž4éjOƈxUãVX/Æ°…g¦¨ÆÏ­ jrß,,Tx%s$amè?&âŽô‚½^"º0û” ¤Ž* +¶"êϯo2Ñ&¤ùbEAÀÌõLŽ(‚f.ƒ@\fî4å´ñi_f|Zƒgå”@9ŸŸ{~DânA§pˆ€öþ‡ús])á-l3Jg‹„lhm”]H96Ä'¡YW®RÎÄø~ÑjhfÔDZ¡Ç ¾¨ú° Õ8†ƒÝ¤6TÏvn£m[€´&ŒÐZ0 ^©„,Q*:ÏC'ÃO@S1êÇçâfòÌp` §FÌ“B†e4”Ù‡Yñ†iTºù·ç4¸6Z%$ÝàÙl¬…—IºÙks’ÃÙ-œLéæ tqï±µÌÒ¥o°µ¤ÛàEÉC +à¹>PJ•Ñ„Ša†C•çxÄIúpØb„ÿ—4Ô`ºÿ¿Õ‡‘"öAa¯[5!¨æ?lx¶‹‘ì<2{Ë G4ªãcžÅH?Ê]ƒ/6úi¦zºÅv~Ê+qI&dšÐ=å°ù“%®OBck#'T´/>ãj â±kÓ‡“GrihØÈžßa' ICêǘE«'Ç¡³ëÇÙr-¢g˾•¯fÕ0’ËoŽ¼,’ïew0I0[{­ jz;3á|‰œ‚ܤ)w“'+ž}ÞísÁ¡ìî¢é£8 ††=åwgbú<.æHµJÏØ 2Ù)'[ì ãÁ¡©äÕó‹zHSE’18âÐLMu‘äÆñ¤Æã0¶¿Ìv€š›lMPrKíÅÖxT£o’°µ‘x9D´‹±©,vG8×Ñ¿šÑLߤvƶ¿$eúúÙÂû(‚ +PΞÛ‰õn,WÛë@ïŽÙûÛ~=½í’›bÖ­‰å›õãÅýó%cù¨¾ÉΗŒå£žtp¾d,Ÿ_ߌç«&bù +.tqD8ŸÛk,–ÏwÂ>œ/ËWðCGó1{%X¾º˜ΗŒå+ø¡‹£Àù’u ŒçKÆòÙûèp¾d,_|0e8_2–ωr$&@'ðñ†g̼gƒ‘¾˜^‹ ÃJËßùàÂ*RnòŒçIsŸ\6ƒ0›TâýéLùëìå#©ä(^R…”4JÏ)K¥q¥üùHåo*äß@ki<Ê=C?‚Ï–áIòΗAÝó›„au“«$lfR¹|~ˆ²tbL'Äl2E€¶x³t‰©ž€‡«äcÛ é›!!~©×ž‘Ù¤CüF…dB7ñãÊdI£M€¥à0§§ Î9MØÆ®ÕJ9>“…g)ÁÔ|aÿÆçP"óâθÓöø‘y¾ð3m/dɹ‚ó²'Ã&#ó2$ÃŽ‚Ì äz༼‘yQ‡³¡lȼ‚{}{|k9!ól™çåÌ‹‰Ümú% ó +ák üÊ,?d±ÁC!óÂÈÌ´„öéÇÈZõ]#·Æz ­¥ˆ‘€HI½„ÖØGBYRâgVŽ¸ï'a±Û ÂezCñ×od;,& o9v^¶…Ÿ•¥!®ûU ÿpÜÁ±–|Á1ŽÁ;“v÷cëjq&¼[WkÉ4LIèðXŒÑ÷ãŸéø ^ +þKµùS¡§¿¸vPP¦1S¡§Ó|J  ¦JŒnª#~1—«ŽIC÷c¬‹K}?ž2=îˆZKoˆ¹¶úã…ÍiäNœ´‹²’ò%`†…˜²ð˜éygq»u8ÈA¦å +’=Ë${–Höê+¬4VÏ$ å’ÅVrÉbC¹€dIÇwÑu<Œ.Â’plz6Tx?vû¡ÍH»éöóÙLPž-Ӳⅲ‚ò¨qË)æÊsˆ–ø6”—!Ö$Z6 S’ï™#(/ÁÈ”GlT£qdPžmrðe&ƒFM°“n¿WÒᜋá ~Î4_ÆÒì4Þ<2lí‹}~äBrž±µo’ϲ¹_¡<äÉÅÒÝvOØYª\7£`o$è@‡-Âé£Ià +]iž%s–„Tmuáô«ä’˜ý•ÖˇVZ?ÂâñÛ¥ºYï–f¶æ¤R­yxVªÞa­õãÒÌñŠ^ uØ÷ñÕµR}««‰óÃÖO /þA;ñ© 쮜PBo¿!†’a¸Áôć¿LlvW¾›8|aÀî*W‰`¿±õ›`ÁÖ ìnUßcôª°Ya°ßM2د]Òؽ¶Ûý ·×0",ýVð!½® hˇ¾^C°»¹×“]F¯Úߙ峙Ï ì§^\$ÁîEf¯âúu'ì7ùÒ8½cõz›Ðk[œeöÚ9¸ÀŸ‡Ð«ñïí÷8Þ+`Ùê½…Ç«ýŸîˤO™â¬#&ª³ƒá·µ²§`msp-ÉíJÏï ·ûSo¡#&º=gÃik™â>.¸è“¤ +žxøžìš^ËÉwQÇŽ).Eq*µD dÍ3ŒÙ]C—׋³¢%džÓËëq‡íö§Çò!UÜeéɹƒY+ë%‡í†¯¬Ç;ý )©°ÿ Ù'¼|hN§ÖX‰S4üÀQTLÛêUè¤+µvت|¡qÀ>¾˜ÍÈÀ>÷eªÏ%Z~À¾¸ÉQ‡=W`_ÜHìö<}qSò’“rö%$øç ì‹# ªç +ìã¸Ì$`¯’Ø“êK wì‹ Éþ‰ŽmD`_ª‘54 +°/Îb g åì‹Cõ9¶À‹ì‹“³¶G'°/nLŽ¾É؇êóPyûâÌV ÎØÇHµÊØ×ÔP‡ÅÉÀ¾¸¦ +ÉxÏa€} DËØ—+ÑØÀ>>¢ ì‹;…‰Ï¼ ؇꣉c¹ûâP}!%°/ÕçK¸Ì Ø×€sXÌhc`_ª/‡ÎØgè${ ì ‘ƒœÀsÕ³S„ÓG,Ä_ÖœR6`;e‡å¸‰Ãûˆ³ƒ°ˆè¤pRß𘾀Kú`F}Å fD~\“#¿*~)&G^UüâÄ4 B2H•RÕ3•Tþ62È´äÉ%ì-ü3™eX)ü˜c +›êXr/©\o–1©¦:'©RËô†…M°ô!ägÐ_B(É„õÒºL›a—)šÛÄÊóŠÿ”jÅ_üÏÇÐ õÿ2e=& ›äú¼“cÿób6\˜¾<®g×ÿË4¥¸â±¢3 ø_4Wÿ/9»…£ø_Cså:¦ÿ+Ģ𸎟@MŒórD}G×VnO¬ŽSúL.4ÑW~ƒKÝc"ÄôeÏp s),|luÀ÷äÃô “iÖžçcg†ð×3ä€Çñ%©! Þmìø7‰õÆÊQwA4_ÒÙFõsÁ—©_¹¥Z ·T+r=Áèœ4az`ÇÐ1JÀ¿ÉØP*Ð×k¥À„`ACÃÕÀ´û#qŒ£¡T˜½kª§%ŽakC¡}]%Fû>÷Ê´ïs/å4*9W1¡5.‰}öÜ›‰d?¤+3&5Þ9¨Y×ÅMÌÚ>žã³ô”í ÃÎDQ„óQ†Æœä!=ɵ¸ãI&[ +œ~ŒRÜÛ;VnFnˆB‘zôMÊáÚШ¾i¥>V12› ( +ºQ2C(’‹Á&‹îGx–B¼÷•þ _ælFf’ÿ~ä©û‘iÌý8RÝ?/£‹Qú/33¶î_ø"½Ð^fHSÔÅ •þe^‘kÚ†mˆ·î_jT=Ÿº~Ç#¶ôßÉ V‹!%"V2¢}¥1.|nÐø)ÄߥŠ­\_ ޛȈ™Šöå¹£‹44rðëœúž9 }Ï’“¨üÞZzCì»íR€Tþ\uwÝvØ1ú!€T˜²c'Žù±TÓ 'ÉHÕí'ã=“°TñÈ´T UÁwK|–jzáŽY+-Å»® ´ö—#'‘ H5½ð·=9J’s¸øܾg·Ï•1ÉR¹ñ4,-É¥â³aLÁpY8üw{9»%Ï‚c\<w»zþðÃb•0è–CI¯ æòÅÚiwƒÜŠaBSÁ¨33Ƀ»½pݪÅgÙ¬ Ü!M¦q€1˜}Àçc£^0u`!ŽŽ»…1q²…?¤£.¼¼¨í¼Çvh¯Ãö ß—¿'l´à¦u8Ãcüg¹T½6bˆÙj((ÛÖ„»‚S¡ÚŸ¹þ@šìû7j°4ݘè&¡ê zñ+¡ ßú³ÄìU\ÿ³{ä´Pi:i³{Ïêõ!©4Ýê‘¯× FºŒÿí…Áy‘¿&Þܹ†y6(¼ Ah‰, ¢>>qø&ß°ð€lì#Îfl} 2§+¶k¿O™½N÷ÊÆ_PÕ C·§™½ö'‹%f¯¥åëúq`]{–pþ°£²öëá-îÕð{7?oï©ïéã_7­ó½ÀãUí¯Í}¶âDˆÎÕJÈu¢7å·ˆ2]ÿé‡`ÂÉU~Lÿ*hÈÆ']6‰ºæÌOKR×MôÇÖ)V0%¡X`zâsLþ|fÆÄ-6æk=|P!R(€ó”«™T&¶×†wºlfJÜb.192%n%Àãf˜Þ—àÅ%&—xˆÄËgüŽG´µ¤4°,3¬gLQLۥ怂•Ë†•šŸ4&ßQ‹/ ,}LáÚÀ¡—=¢h èŠ¹Ü󦕛vˆ–Îû­QîÙõ<‚‹Úw²qˈj]†=áu“[ù]Ô¹su’ŠéÙa°‘âiXhïü»tw7$ŽE`E`€^qwÿóÇøÀŠ ÆÓ_ÌÏp]pLr¨ìrÛ“n'™tmqö\è4Š—˜rC6¤×]–«Ø »±¤\uS=0¦¿L„À²?rˇK ŠNli¬õÐŽ+ü3Üö#ù£µÆQ=Ž¿µÔBBñMbpÒØZš/“a`h"þÖ’O@³-µ¤P&¢% ã"(f¦ÙèÃ#úôÍ0Ę­‹G ¥ód…$ò"ÝüæàD†˜ŽàG‰Cg(4È´:ù ‰¼xÄó .H"ï*Å'ÀpCyñˆ§e§H¦Bƒ±Š€’È‹Gä÷¤YGxÉxĽ“`î òG±BCç\¬0KØn„b…zYcº™7Z±Â °ù׊¦HæR¬2tRaÀ\ŠÆårü Å + ŒÒa9+Œ±F,VSï;~˜ÖtDiˆeÁC–Ó­Vñ³ʆ*xC´Í› +2Ĉ]í°0:<.eÖN2,䢗Zð0ù¬,¤†/x x¤ÚaüÑ÷“[‰ t SðÑŠí£%9áµðÓ $Ñ‚‡ ÑÔÞ‘ &W;t»µà¡²‹3ù +CÀãRÛq¶~ZÊZð0¹Úa¦úÇÒ<¦:WÁC.8öèVâñO…ìu +‡ñ¸Ã¦zLÁÃL% +Õã]C<Œ=üÕ , fÖ‚‡£É4ÉÆÍi£<ô ,Æ÷_ð0;w¨‚‡q­xÁ/& p6Äé€ÒêŽÕÇV +¾Š˜1çÏ™€W Õ œ·Ä§<¬&V;dï3<Œ^1Âv~‚f-x˜ c‹¬ÍÀ«„j‡ѸWñ3Ä:ƒDËVð0¹Úaß“!ÄÎ5oÖÅ”‚‡É£#ÓF.x˜l1†N +‡/xCM_µÃ؃•a +&O³ü²}R +&gG¢êÃ<Œ%š‹PH>ÊPð0YÁþaãt]„$OÁÃd]QðÃJG)xÕ=ßq¥¯›Þw©‰­&RnfÿœÌ6Â7bÁ3)Iؤäû‡@”³ŒÏPÈ‹F ö×<‘Ußýt ø‡u;ö¾f“ÔiÍþY··Öï/>-7¾ç7W$óìˆè›Å±&y Q]êñþm¿4yU*MÃKÓ7¿%aáy¥:·øeUçç/ªÇO/b«õÜ[ÏõYq}ë )®oÎÏ8¸ÄÒOíSÝýM–…ðVÖ'ʼnÞ<ÛÿÉ’pª¼ˆqÝ.ÕW/T_L»Ââì*ƒ^³*ãçKü=Sé÷wëUo®ÐMdºjå¼:¯L/‹ë«[«âúŸûM±½·û6¿ÑïL[²;\üjUÌÝK±±Þ-!Bò@l]RÿPlìõj!o ˜'huªþ.¿yQZßä.§_Äý/cù¹2a½}‹bYh?QB–íOϽøE§B šþ™þªâŸUûÏÙrÿ¬Ûn« Û[›@‘ðݺù²Ä†¶!®¼}lV¶ÎήA|[öPŠ÷…‹ Uß·k ÎkuòtC¾“W,9ßm4¼Mö¸â|±'¹_ÜÀ¢M¾ŠöÂŒ÷Ìßu{­æ}DóõÞÞ€æµÐmWUhh¼"_žà‹C‰ü©,”àÏýe¯ñ.±áÙz _™û»·J¤ + +Eë¶IºÛƒõÝßk`DºFt1üyBÚ¡ ÔØ¿T;G/ +üü–`|~¿­B›ظخ»½þvºù «¯ÿª7·óèFÍ>¾OŸ¬Ï·Ôg—¡|hûüÀïÝÆy¦ò¶åQ»¡Fm}“­]GØ’v+›æe¶×—§{ÇÍ»†'Ìð\rú˜ÄlšµÉŸõrgcC®¼vÇmv»¸U]:ÜúÙín¯Žôª…wøó¤asQºÛÞ¨À§K‰Æ»yÑÚ?ÈlînòGP+®ùÕ p•_¸•S €?lâ?KTÔ¾>÷âEA¸Ôpc +'W¶Ý ØöDöz3dG‚ú=š¡Ús¼†0ãW¼ª  j"iCY:kÝ®w+—[(ÜØe*gzƒÌ©îÌØû\kÈÍúe¶öBÍY”`BÀ³5ÁvqÏ©Ó ÿTlZ[oÄÇQ@F8¬‚¼á0l^µŒ>l¼ryÖÙг3d®¸oZ7³óÎãe²ÅÐÐ8ü‚?×OÀ\ï^V_ÇÞ×¥ÚçšØÜœÿ£’½ºÓ˜°´ÐŽ³68øæ›oк¸àNm7)Ñ–äŠD^•Ú'¢2¥t6O5cNªOltãLåäÆ¡å h¹öË°=]ìÝ­ÏJ÷ÆDöô&7—nåߥúz|n=!K$.";†VíÝ¿ÀžB%Ý_h<)@ (±7¦5¼ñ’g“ênó@|˜|oÂËýª_·›l~éÈ×ÀÆUM6€—2,žtgZŽÕ38 Ü)A-qxfþÐ +ßS¿äMŸ‰Hn:hÍïy DøA7ÊßFgªãßìê½£€è ò®Œ'¸:Uü$¸Ïî3Øsµ#àÒïrŸÊ-P¿¿ü¾ç”;ñ¿3“ó¥ß¾»"lsŒ–ù¾‚åSÛð…$„L×4ºñY@óÍÁ‘iÔþ!÷RXã1¯Öf0¶þШÆß8Ñ|¡§6nup÷î‰KJ4ß3¾)]ö•;gJ³«>~«_ñ#]5ò½øEFGÌÁßËJÕ«Í ôúð߃½/xm¸WràåÖ˜=õû½”•c‘»:ª;‚ã{FGNzµ£R^º¼f,Æòs1B+,»ôê\ÚÆ•¿¹·C·ó„Õ¤w}Úƒ k“0FW_™³‰´Ñí8r•Ÿš.Ñø'ò—±"0ˆÀ¸b-Äû]߆›ì¹Ã'7àØï÷3¿t£÷?JÌ&ùv0H~§¯7__‹û`ÌfñâÊ寋+ÿ°ìzôŸ¶Að½TE‹McX6ƒOt÷®©ÿ ïY[[Kr=‚Æ]}d›2 Ô-‰³R&†±cít¿Ñ#pTù•mskj…äw[h‰a°1jÓŽñ´Jº?N¬üǼ٪ã‡hÏ£ç¸X»0„‹†$ˆkEƒ¶^høT(~ë…DB‡žÒ”®§›|VŒçñÅóšSO—Mâ%T[§çbæÍjýSIðê¨/·+¦¤Ž¾MoNÒfë´VRVæ ¬j¯W—*“?r÷~&2€ïÝÚ¤G ;’¡¬Ì/íÒLÈW`ÞÚÚªÜÉTªÌ¬ÖJÕŸY¼íý±;p-òþã¹ü¦•êGK$z£ ßÞ×ì_¶¿¶¨Wß? ʹH‡ž'bUm>e÷ø”¥ÓÏUÛ7Ò¤2…3+  Ñ4c†˜mòõÏüq×Ã%q¥Úv#@[‚O‚´·ÇâÚŒw~Ù÷e?ÀNFUÃç+GõÙ…«Õ FÃüagîÿ*++­Õ-òB)`ËìÓ/Ø7ŸråçA%Î.¾/ØÎîɘ@~NN +½ØŽjñ‡pbb6ñ=}1'f±r°vÙÜÚ(¬Xèôû‹ wÍ¥êÑqs­suíÉ{7ïp-x°ÂÈOüë¨DŸJÎ`Ú«@¾D¹˜*Ëoçú!ä"FP.rEè&].V”ɇRSj¯ÝˆD’Á6¾i)SOO‹4k Åy‚µ…%“u¤×LãIÛî(SÝÁ=UkÉr±1ãrŸÐÒa}žê**¿ŽÕ9©ýø¶l|gÂÞXX…d2&è† 7Å9Òf«šƒ¦ªŒ}ËÝ×mëÙêâ6…RËþj‚§#˶“aßüTÆ”©Ó·9¢ê¥ÎÒ¡…·@Ð_à+`=T*hÜÊÍŸË9`™M•JÞ©þŒµá¡ ·Ÿ|!9+þ9yX}ÿØ„y]%ŠàϘ…ûWôh#j§ dÌx=pÈÙ‚SÓ˵8ï’<º3Ûge*Ý<Áâ õìõÞíhßùK]îvj¸uèÂnº"›ÝŽ;¹ÛLDò'ìåe´gI\wÚ6¾´å:ýózÐ#±¥ieb~þ7 ’ý¹Y~„nKpË?ÔšøþÛýÂo^ÕÖ&ïÜ/jþ/N¤÷ ¿8­=.üñw#ú¾«Olüu-Q£Çå»uŸUýrùF¸w¿¨û¿øœñYÃ39´]‘ìQb%âŸTéHw7“nãUúÊݧ„:à îJí yz±ŒÇÜ}å^_ÃVD´FÑyßÙ#Oìvï]Jv0öb½ê¹©Í-WôY—6Ç‚_ļM¶jVåfyN<ðÒT¦Ùƒ!#Yî‘ãqƒ·É¸ö\¢šý2¥1>˜¯žÌõõÅuwŸ~(‡d:ÌúRö‚÷“¡êz!S§2íÌÇ2r³³€¡‰3Êäró׆D7@ó~O³?½žÈö§ŸË²ÓäVå¶ëên!OË…’$8[  è+°ãàÏ‹>¬œZÆP–@\17"\4N;k¿_VPøl­ölYf‡ÐäŒI—EjŸ”ÑÐyܳ]›•ÿ·XøÇ0M­hj¦Qlþ¼öú{ý§Ç§÷b­ðÏ\áŸÆʆ$¼?|¬÷{½ãÞÿ}7?îÞzïßÅÙbcåhmcÃÔš½û‡^±ædFÒÑg¢jõ_úÛ¯ùúïõãL¢'á·ïí×Óç»Õß;++µwéznI:5›ã?¨ôˆ)C(“d!{ŽbóÕ~éŒ.Ü6ÿˆ‹akzúóø c´¢'Ñy¼,Õäõ£R¹3¨‘SÑîà DÐÐz0"|Èt§I¼xQŸ\ûiþZï“‘¯]·Ïõݵ‡1rÌ¿Ü<Ì¡±h¿ì·Î–•›¹_Ûïࣜž_­tôÒßa'g†êQ™XǸ7ny´óîÙ‰®šü œþ*Ø'-v£½QPÄ9ÅÞFš0í|’Êh„–m¶ÝJ•åH°^èVA“š„3œŸ­7Èž³w•_CÔ¦ÿºÇÁ[ÿÖ·+É·üjLzWMlùÕ„ö Ýl5üß}®=º?’üºeõk††I|ÏNÊ®ÜõëÀö£êÊù]‚œ¢‘Š‰e‘î N}õ®DµXLJ8~q÷XEâÔlÅ gH÷õ _i8Á é~iÖÝîýÁ iWñ±vº}z²-Wê*Š½ãºÿøÑ!9\}Á/¾ÿ³M<ÀAd}Y†¿—ä Ê¡³>ŸÜd\{…h±D_“Û Ñ8Žù¸rô3ñÔúõ°fÀ±ä±1à›e[ª]Ü(îá«<|m..ËœÕmmt°†Gg []jgÐøÝ5"ØÎ$G“œŸï?)ôS0§ÉUáÁì ŽxSÌ^ï}”[g«;›«•×þ>ˆoTYm‰ÂqT¸"ÆÓvºÅŠ9möЉìØ[ñü…fd D‹{ÛŸ"^%ÎF, '-B ¹‹Ñ`¥m‰:'/Ðw¿8ÅUê$ÿïH<Í;„ïw0ƒ›±’Áa”)ŸŸIlWÔÔa³•l†ÅJŸH‚-B¦jÃ9YÝ]M’:ÕþØéx…Úÿ¾Õw'vwÌN=Þ0ô(²3v}Jg¢u£Åõy¡yP÷õÝ›[¡ãÂRé»>aVv«çÅÀ¹œÄ}ºCnÝŒy訑$#ûŽ¢ì£Æ«0§íÔ«£ï’ iNÉù{âá-óu1üw ^*„Óh³’¯å³™[§ ¡ÎnœŽAPaÃjC«Ž6ˆÙrÍw”<\kB—šÎQr¤ ’ZÓ âÓ·Àv‹'ZÉYÕÚd•«É©ÎsÖÉ3¡ö܃q?©ø£’ÝW»³êõ…¤úò½º¢âŒéY¢,`¿Šy#¿íìL0šåÕ iƒ|;²½^pn"úwíu_”úß´×ý£Ñ^§kãž,þ[ö:é†Ä“þU{Æãíëíõˆ©þïØëtmŽÕÏ^G}£yI Ò³ç"hqÞléˆÙsicÆtÄ칈DBgMG å"b ã*ê¶Þüa"x< Žzß?Ÿä­»Ú{|zß¾ý_6­T¤ÿáRQ+ÊšV4,ò±¨áÓí»Â?eòvQ¯·ß¡‘nc¥ÿÝ|ºÿ~úx¿íÿ¯8KžïlŸl4‹³EúvÞž+–aLb^‡¯*$BÕÅ‘v ÿˆÅüçü¿…¶Wí‘Á@ÉoëõÔ¡JÎPáÿÑ‘Ê™F*sTÂ’óñïÁ?‚h™EQPuÙ€ÿ˜¦®ÀDI3Š/øŠ(ètØÐÈÿðÁ&|z†)ü·¨wŠ—×bñ¡ß³«é†`hК¢Y‚hJRø]ÓDAÓ­¨ˆ†`Jªó5ËÐõ¢lh‚©«JQ5dAW £(›Ð‚¤XÅûÂ?ªb²­Á?Ђ®UY4“´/ +üUTK0L >ˆ²  É5]°LQ+ªš"À¤Tò{~oj‚,™fQSTÁìX>ÀÏ4CdU“Šª. š4^Óx"‰‚¢ŠðÃÈ á‡XkU®ÃÏdE—`Gßý§÷Çbyuuå¸ûðãû_f®–h,‚$K©ê0ÝL¨T– U‘E˜¼¦% PRòI‚dhfQUA4,xˆ%ãrÃà ÖÚ**Bµô¢DWð ¸hh*@E‘¬Ši Šl‹DVVeKb!uuA7৒ÍÃÛ*,¯fÓH²€ô¬“® +ªeÀ€ð-ÐV QÖ‹ÑyåL[Á°€eàg¢f‡éúJ0)]ÃI 誨SŠª‚ãŠj‚¡:¡(ü\j'‹@?IÔÅR‘·M ±jŠj°—$©)h*J'\«Â¼ÊE†Ün!Óš@(2O 6€%ª„e _!Y-ZTMX!UE²Âî´è +£ÔMJVI0ey˜×Bá‹lˆ"4.õEØÌÑÙåγŸv•%Yd Š ó54BLXvòÈRmã#xKÔðLÞ4¦lä-MÞ!$HºCÞÐ `‰5ü!.ƒ›W}ŠÂ‹´¤Ô€(*nmSþÇ×LY!2´¦E  ¿ªÊt!‚°(dG«ªœMG!Ê•„~H)EÁŒšÆŸÁ—:n^7u öK¼ ‚hÐ ž¨òšAž5ÁÑqtœ6JÃ>ÁуDÔAHÂC$ ‹-"ÓÈ´}Øܸ{%ØA +²ˆ Ã0l ·6¾Ê„ªØ·|­Jd>þUU™0È5©&áK¦ Â› ‘a†( %Ø"04­³È&p-Ø8Øø7(̺÷‘a‚ +PÖ@Nâ*ƒ¬Æ©¢0׈L‡MNæ lŽ`‰$Üð ³€;áVSa…,d‘eIdµe ‘aßb§ ålG†ÙÉØ°Èox(K(óa—À’ƒB¹æµ¦‹÷oªE‹÷oŸ?ïÅÁßÛÏ^ñíã¡døORuŽ"DÑ 9[QÓM‹r½‚Ÿa¦ÀlðA×@„ˆÈmJQQ€Ëtø ÉH T@]à ‹,+0*¼ZÆ.«„ø@:eŽ¢Áv/Z ³EÜ-À!Àzˆ&£à†y›™°$Š(ÄaûàÈt €D(¥Â—–¿C½ ª&Ê#àx²€QQ VÅßcS°ÖTyÀVTD¶“Ueý Í ä[$¢êG1a)Éšâ¾q2°ìÀ»i-B®í™$£}"ñʤªq$ ÄÊaÄoTwZšÜœ.éȃ"nrˆ$Qès2e¢%E&›AÂ"êðµ¥*tö Pt +¬ªŽ“À¹’·`z2LC´È"„tÚ(J"Ž.96¢N·@K+&J)0i$4CL\= %h•0EÕaÉaKI’FŠ kDÌ*<µ_pƒ€bÒ |ìYSQ/ — ÅAãý |òøÅ€!jT´Fh–ï²ÀÞÔ è\Ñ,Ð;. ÐÀU‰ð­® 1À0U ˆ5ja +2,¾!ZtA@„Ihé¤Ãhq  nEÒ:hnSA‘$U‰êЇj‚(Ð5Bx0>Èj t׈ìCíh‘Å€ÒL« +Ý› UePD§‚qdu”OD‚‚‘ d!`M,Ý€^Q˜oM ¶¯‰ªCDQ¢ö LŽn5Í„\Gu[ÕÒ‰T)ßU«ôð¢Š6H"ª@÷Ê ÛTXÀRÐ~Ôp +ºn ð‚q‰hWÃàU4Ÿ×¨8Q-ÚȆLåˆqªz%dph$½FÕ ÌK+Z¨šaÑPI`ÃІ4´ú€*FªºÐÀ‚ET/ò¸#F—@ÅÝBlMX"D¦ý3[÷Àâ*Œ¤.Z¸:¤uØû¢J¶.ƒ˜TUÔ\m,L­|ã¨U,WŠçgÁÏ’í=ƒ„ô?Ж¨XÔÔ Hב¡u4ˉˆÃñË&>'|>:û-Ô:Ùu¨XˆM#Zô¼º;œ3«ØŽl¼Û­frfUng–ú®žúHI^ª »,LXddJ $Í@fG»ÉyøŠtrÉh}‚ÜQãCF*nýàCÁhb*Àpà-Éä!¨ +·Kàa¸óWÂMh#) +ð\E³«h¢„öÿ¶ŽhˆÁ‡Š„%$ÈNö+}ÞžhšÁ7ASYj¨M¢LT÷NçøPCm­Ó‡DßãC’‚Fˆ(Ô|äH[ P NéC7 î$42,adì&Ú1É`s‘÷Pè‹j ë(1(‘€ñÁäGþGç þ«VDo”5î2%›KÊЈ§èê¢6qb¯:êª ƒ¬àÌÂbñu ¢Uü¾~‰B6Hv ‹àSÁew†¨Gt©4tÜ'÷Ø'È$ bø^ÃŽT•0išY)E£Üíˆ Ž~ *,Ó%"iAa9| -sƒÅEÅh`½, ¸‚vÆÔÐeÒ‰¨$+šLÉÂ_â[ - ù–Œ˜•(A)L4jì÷”ˆºt_‚ŽMM¥ÓVP8bDF&P¾š¦ôw°-€Ç[d…t^¢ÿ‘•ÅHøBTá*&Y9hºW#n pb$Š#ãÄ2!°ZA€ ú µVBŒ2\¦>ÚÜhð:O0F¤bÜ xÀ{M6© ‚ˆÁ!p=È,©LgÔäšiX„P2ŠeÔjÄNAêh¸7UÜù@Uï‰ÎQÛx¯¡‹…Ì«!™2¡†;0¤•$“]‚±M³ôÀ3à<4Õ"T²ÁÄèC0Í‘ÄÉ$-ŠÔýÀ‡À¡}ÓfÖUbL@‧b†$ŽÿÐ^HÀ—Ú²$$µ‚.eopI â…ëŠjzOèêÀÚø^B™&:°Ž +"@|KÄhHK¥Î6ð=ð'HXCG]Y߇åÑ´‚‡%{Oð‡Èžšæ Ü}â,©`ÌÈXèr« +:`ɤáäÓ@Ó@&¶˜NÚ7èr+Ôäû_ ßRA¥¡£hj@š«–a¡> +Ƽ€~YXüòzà + +n ÆAvO먋%x훎iÍ.;j=6L­dÒì +·f'I£x +€-gŽ¹£˜CŠï“?oßzï½ÿÚYýÿ¹ ½,Æyç¡ð´»2AdŒ!Ôrü +Lîß>öŽû·O¯HÿÇÁízÅÛ÷w´{ŸðUñ±ß@ë½âàïÇñ þÈýÁädkL¼ÿÚ+X +endstream endobj 221 0 obj<> endobj 224 0 obj<> endobj 225 0 obj<> endobj 226 0 obj<>stream +H‰¬—Oo]Ç Å÷÷SÜåÓ£™!çß6n piÐEª ÆB­Q ʧïïðJÏŠ ]†%ͼyxxÈwýîæéîq¿þîmÞ¿úÓÛ}ûú;~\_v˱ûó–÷±êÞKÝßèÇ»í¯û¿Ù=þý‹¿î¹÷ç»7ï?ݽýéÃO÷wïo÷Çûíú›¿äýÇ_¶²,åÛKö”ÇÚ-×T»í·Û()6Š%k{©ZŠǽïÃR¾{^œœ+™ÍÝ[O³û^êJ¥•ÝgO}Ô½tLrÐGKm.”ÕRåïž|OSnX(=lû—îßnÿÜr9YîüÌ5Ð(<Ýnœº—ˆÓSÏ…8[}FœV;qŽ4e:÷d«ç$î¦8[Â✩ñζIØ°±8FêÀä¯xt»­š:놇ĢXWoÄ +Z¬=§¹Fõ±–4ë ÖœêšÄê‰U "ù2€ˆô’ìZj*g'Îà,áXì,O“›Ð-Y;ć¥Í¤:ÐHJ!qEK#áO dÇJ"I2@h —Ìa†ÒÅ~»-%¾t<œ÷€È‚-´×x0Ë  9¥µ4ÍX7 ©ºÀ'Ò8'g× !Þ¦¹á+Òªµ .„Ý É!ÛL|̤¡qBÁÖE•% ‹et.JëÊ› +S´'ÇTLzŠ´”lCkèA¶½*_¶Ë#Òà€@÷Æ®)Kð3‹$Nzê$M4ü°N/”SÇå0 M:KÌzÕ ÑÌÁ‘½ò ~+(¤5p³.‹Â¤Îê.,•… dL…¹Šà¨µÆ[žõx%ŒÊ3p"€VêFEMÚR°/yó™UEEc-•I0— :¬2ýÅ „‰» ÕÖ(#µ°½ÔzDKÔ)…lMâ@f­*CÄd”—5Ø×+«™:2A(U%…W³ ’Ì;.Î +‹S?ÊŸ¬â!G®üdHe*[0XKâu@‚³-+¥…x\¨à ‰†V”z + *dEXtÈ@¢`ã=¸H‹J)Qÿc†„`q5í€JW†rÛŠ’LI¡k.2 ŒÅ,_ °IùºÊ Ù犷3ÈØWóð-¯ˆZ‹ªB=»N $”n×™© +T%R± ©xŠ´²™SÛ0«aRAg‚ž¡ZÕEûŠÜ5oÈm°.+#"2¥UX’Ø*¡áÎ ,Š;i. ²¬vh1Pk½"­ƒ-˜nÁv‘OpPL­(–nŠ„úÎá¢%mD äaP!>áh¸sS;‘~H +ÐEébª”-HH%W‰ +jê”ð¥F¥ö¨À¸£è:Ù ©Èª3Û!G.ÄjJ:уvå?}QÙð5P ª” bÀ9èH{ñ cï*•´lò?NÃI +©ŠœTuˆj9²KчjP-¤a=+.,eÅ}0 µÞ¤€AnÜ‘¤ª*€‚ok©éŽ£É‚Nµcí‡L»ZÌx¾„êV%=4¯–£×é•ž=t¸W°q)V{Fã‹I¥“Ê÷ÿeV)ÿó¬RI¨©eº0qЮȽlÀbWBx$lÀ9¤ýw;PlR”*Ź¬ˆØìxÈ»ºi…ssØë[¤BƒÇeÃÒ¦ö +é‡ÇƒR~m[5¾üw—T]öù%vš4§ÇΡV %(Û +ÍyÅåÑDM8¹ÔšQ«èN¡Šëð*mš*†8¢š|õÒû˜•2…’þf¥±k蛤äðQÕ†’SuøªÏ/;˜ì*ÌC3ƨ¡3kFAãçþiên‡j,ã÷Tz<T³L“?¯o7)A Åx9!Ë零†€ùò”54…QaÞ/ŒxÚÐù¨œW/°2ÅAÄrJ}u6+¥‹ÛKw”mè-÷ÁP³¬rcfGœòîy}Y“&]NðÚlGXcŸ™®‡®µÖã,Ô˜LR«½ë3ÜO$GcðPR¥g¦öëDéLFèh®Àß«"pÝ\½i§OW¥Ÿnöo®ÞøéæánwÿÇ«uºyäÄ<=]ýpþvûú|Äÿýö>‡,q +endstream endobj 227 0 obj<> endobj 228 0 obj<>stream +H‰|“}PgÇwž5ôâj³„F¶—]¸Z "¶ êÐéEªç BEŠFÂËBbI¤R¢Æ^¬/T„X©X¤€­¢RÐÚzêØvÆŽõeì:gëtªs¿un¼ÍõûïöÝg~ßù}Ÿïïóg›ÕVb´ò+«ŠM³É¢³•èy>µ²’ÏuYùìk‰¥ZªþžŽ¬¼Ž·Ytú£ÎbàM¥ÿlj7ê¶ñ’f))$Õ"U„*¾¸ÄbÓIߊ­ÁªŠm‚©ÊšHHIÄóˆùÄR"XN¬ V«ˆ5D±ŽXOäùÄ‹.BFÈ ;ÑG\!“Ƚäo2½ì”ìW¹C>VöŠ¥i¨r ‰0Ra¶Ñ®…$æá³—Ä5öR6<=¥Ì8¹jln^1 èÁç7/Œp}Ã:®°ÏÕÖ~Ê17N˜‡m[Vµ'³8<¦<} ·pi&±*jaÏÛç ´ïMÜ°>`ïÞ +¶stŽqÂN–‰ä >u¬£¸25˜÷ì­æLxÚyø´k…ùß]ƒ%½Ü]q-¯Pôq¨Äa;Ù!.‘wDŠ±ÔP!ÜJ=‡ÿ«?v‚bqµÈ©ú^ƒ7AÅ8ÄòHïWµ~=û5mÅþ,»° ¿ƒS8fxîÅ¿þ4ùõÑG´Mãð–{„“¢Žª™a¬Üúnþ&¶ØÑ;zÿ*P?jõzýý/'p¡ÔNöC<äÃ<9¼ú,_7½ í®rQÉSïÌO`$ÝÛxgnæ»æMeZ/5½hÁ´^}·}üìYv¨³lâ,¬X,Ø;ú¸†ÎæCÍôˆÂ~zúëS²´’+_Ú¡/¬Ëeum]nGÀ}8yn˜9 ³£î¿óe^† ¼ÇãˆoßþüõÇ&µ©ðƒº0g;kÔ8ñ嶷³ÆÊÑ¿á ƒºA&í‘ ÷ãåjÇgëþÆ^¾Þ6xŽ›:Ý}³ýß‹±šàú̶å,°'ã…Ü¼Æ oýÌN }|æ×DíÒ¡øê\C«Ëò4sð \W§¼_[Àmí?óàÒÏ÷:ƒõn‰Áj1B""ÄËáÈS`C¿¿4Îö·Ù*ìÞêz—‡ƒ^¡ÎI”öm9ñqÔ‚Ñ%«óª +7Kì&` tĘýw>¢q¶äXk¿‹|v•tyS˜’ç±á?Q‰ÓµÒM„ÕâõØàñ‰ñ‚Íåë {Œƒ-*CYžÆ€Bõø9awÏÉn's™aHˆ< fô¨}üêÛçß"ÔxžP¨Ã8ê$lA;F¿e{?² +O(o>îDÀKyPKWËçQ·‹ÆÞ^½Æ˜^¤=U/œù3›žeJ/ä¼scÿM„gS»‹\¹ukðm ½(X ¢vU«¨b¤ôŸÁ*Ä\{thèÇûì¾}¾†¹nˆF»v6î`W¬Û¸Ràê)f ¥¯¹·©W!y¹u®”Rsð†æ‚ ÈT_À-|☻Ì5ˆPo[€Ò);–¡KT)ÎE»J=æ-Q'‹î}Úß²·C{òÖm÷Õ±†íGÚ¥:¹^HAO¨Ã C1Th=éƒ<3D[5"zóˆX«¾%†X +ºBÀRñØ€˜sæöÒÊÊÓïp_´A…††釅X,“¨ü%ùŸÌÀ¸Ô?êg©Û°Lš88äïÿ䚢™Zë+hÉÓ2Çø/!v†ÜçJC/Æ›ìÙ5Þ_7ÇC5ŸõžöLÕÁf §^Â=k~Íú9^ªù|ÓHÃDè5ô –´ˆcò!i¯GÓYM;|»ë]s¦ª +›Én¨³¹ŽãYšã{®™<ù«©¿_†HHƒ'œ@%aCu®Óà4J–õ=î>׸ íô?[äÇy­ß:ê§ °gÚäÏöÍø½¼±ýc~E¨\ú¡y¸6¸iÙÎÀse¸¶&<èT¾² á kS*•4¤GˆÔÿ`§Ìæ +endstream endobj 229 0 obj[/Indexed/DeviceRGB 255 230 0 R] endobj 230 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 231 0 obj<>stream +8;Z\tb7VW7$q)Oq',C:3#8E>M>c$-\+B]mjG!(U/PJ]SFJ&!W$7@4>\z!:X'SQu3O/ +I,#j06Orc)mgoje=#m#DSX#9=4%liGI-C-sY1lr[V8XV>V>=BJQ\]3jdq9jGA#Cgl +8UV=J9P2mhFuLBGT1D,Ien`"R41C@EoeCU+mCn5OkY/D(U:,!S1bHnnod-U0"@c`s +6J[s(jJTCYTW#%;TmOur?@8e(SMIc0s\d-bHc*om>*1g!a1p)M@AE]3+l=g^^7OmSB?_m^#:4fYE +o$amMGk,4Q9X4U"z!!(qI49kl+'uB;~> +endstream endobj 232 0 obj<>stream + + + + + application/pdf + + + Adobe Illustrator CS2 + 2005-09-04T08:26:13-06:00 + 2005-09-04T08:28:22-06:00 + 2005-09-04T08:28:22-06:00 + + + + 256 + 92 + JPEG + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAXAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FWmZVUsxCq BUk7ADFXnnmv84bDS3kt9Ls5L6ZDxa4kDR24b2JHJ/wHvm103Zcp7yNfe6bWdsRxbRiZHv5Bgdz+ c/neZy0csFuv8kcKkf8AJTmfxzZx7KwjvPxdLLt3UHlwj4I7Sfzx8x28qjUraC9g/aKAwyfQRVf+ FyrL2PjI9JIPzbsPb+QH1gEfJ6z5X83aL5kszc6bLV0p69s9FljJ6clqevYjbNJqNNPEak9HpdZj zxuB+HUJzmO5TsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVW TzwwQvNM4jijBZ3bYADqcIBJoIJAFl81f85F/nL5u0bzkvlzy5qLadDYwRyXrwhfUeeZfUCsxDUV Y2WgHjv2wMgHmtv/AM5EfnFA1V8ws4qCVktrRwadviiJH0YppPND/wCckPzHudYsre/jsNSiuLiO OaGS0QNIrsF4hk40PgadckJkciwMAeb2T83vy/ht0PmHSYFjhFBqFvGtFWuwmVRsB2b7/HN52ZrS f3cz7v1PNdtdnADxYD+sP0/rec+Xtf1DQdVh1KxfjLEfjSp4yIftI9OoObbPhjliYydDpdTLDMTj /a+m9E1e01jSbXU7Q1gukDqD1B6Mp91YEHOPy4jjkYnmHv8ABmjkgJx5FG5W2uxV2KpFrnnzyZoF 4tlrWtWenXboJVguJkjcxsSoajHoSpxVG6H5h0LXrNr3Rb6HULRXMTT27iRA6gEryXuAwxVJP+Vr /lr63of4m071uXD0/rEfLlWnGletcVZXirsVakkSNGkkYIiAs7saAAbkknFWJf8AK3vyv/6mnTP+ kmP+uKstRldQymqsAQfY4qpXl5a2VpNeXkyW9pbI0txPIQqJGg5MzMdgABU4qxhfzc/LFmCr5o01 mY0VRcRkknsN8VZbiqS+YfOvlHy4F/TmsWmnO4qkU8qrIw8VjrzI+QxVI7H86/ypvZxBB5msxIxC j1WaFST/AJUqov44qzWORJEWSNg8bgMjqagg7ggjFUj13z35M0C7Sz1vWrPTrt0EqQXEyRuYySob iTWhKkYqiNA81eW/MMU0uh6lb6lFAwWZ7aRZAjMKgEr3piqa4q7FXYq7FXYq7FXnnmfX31jz1pXl GzatrBOlzqrKdnMI9YRGnYcd/c+2bXT4PDwyynmRQ+O1um1Op8TURwR5A3L4b0+O/wAw/MH+IfPO uayG5xXl5M8DDf8AchisO/8AxjVc1Tuwx3FXoP5C+Xf07+auhQMvKCzlN/PXcAWo9RK/OQKPpxQX 3Hdx20trNHdANbPGyzq32ShBDA+1MlEkEVzYSAIIPJ8mSBBIwQ1QE8SepFds7cPm0qvbk9k/IjWX lsdQ0eRq/V3W4gB/lk+FwPYMoP05oO2cVSE+/Z6r2fz3CUD/AA7/ADeqZpXoXYq7FXwV+cfmr/E/ 5ka3qaPztVnNrZmtR6Fv+6Qr7PxL/TiyD7B/KLywnlP8tdH06ZRFOtv9avyRQiaf97IG/wBTlx+j Fi+IfL4e/wDNumiReb3d/AHUbVMky1A8OuLJ+iWLF2KvKf8AnJLzwfLX5eTWVtJw1LXibKChoVhI rcOP9geHzYYqHzn+Qfkb/Fv5iWUc8fPTNLpf39RVSsTD04z/AK8nEEeFcUl9xYoeRf8AOT/mo6N+ WsmnxMVutcmSzWnUQr+9mPyIQIf9bFQ+c/yI8q/4k/M/R7aROdpZP+kLvw9O2o6g+zS8FPzxSX0X /wA5A/nHJ5I0uPSNFkX/ABLqKF45CA31WCvH1ipBBZiCqA+BPahUB89/lb+WHmD80vMN3Pd3ksdl CRJqmrzcppGdztGpY/HIwqdzsOvYFSo/nV+W2nfl/wCabfSLC/e+huLRLr98FEkZZ3Ti3Gimvp1G Kh7h/wA4j65ql75S1bTbqVpbTTLmP6lzJPBZkJeNSf2Qy8qe+Kl4N+dXmg+ZPzM1u/ST1LWGc2dm R9n0bb90CvsxUv8ATiofUP8Azjf5X/QX5W6fLIgS61hn1GY9ystFh38PRRT9OKC9QxV2KuxV2KsT 86/mn5I8lXNnbeYr82s16C0SJFJMQimhdhGrELXb37YqiNN8/wDlbXrGZ/LWrWeqXojZre0SdBIX 4/AGjYq6gnxGSgBYvkxmSAa5vNNG8q+ddLtPN+s3VhK2trpl19QFVdpbmdWbkhQtyYFeg8ad83Ha OqxyxxjjO36nQ9k6LLDLKeUUf183yPJHJFI0cilJENGRgQQR2IOaV6Jbir6M/wCcPvL/AD1DX/MD rtDFFYQN7yt6stPl6affigvefzH1X9GeTNTnVuMssX1eLx5TH09vkGJzL0OPjzRHx+TgdpZvDwSP lXz2fNOde8C9A/JG5aLzk8QJ4z2kqEdvhZXH/Ec1fa8bw33F3fYEqzkd8T+h71nMvYuxViH5t+av 8L/l3rero/C5W3MFme/rz/uoyP8AVZ+X0Yq+Nvyh8q/4o/MXRNKdOdqZxcXg7ehb/vZAf9YLx+nF kX3R5hn+r6Bqc9QPRtJ5Kt0HGNjv7YsXwX+Wlsbr8xfK8HEsH1Wy5gdeIuELH6FriyL9BMWLsVfE f/OQvnj/ABV+Yt2lvJz0zRq6fZ0Pws0bH1pB2+KWoB7qBikPf/8AnGbyN/h7yCmq3MfHUfMBW7cn 7QtgCLdendSX/wBligvXcVfIX/OVnmr9KefoNEiflbaFbhHANR9YuaSyf8J6Y+jFIZl/zifoFvpn lzX/ADnf0iikJt4pn2C29qvqzvX+UsRX/UxUvAfPnmy782+btT1+5Lf6ZMzQRsa+nAvwxRj/AFUA GKX2p+TnkpPJ/wCX+maW8YS+lQXWpHubmcBmB/1BRP8AY4sXzr+b35ffmt5s/MPWNXh8u3klkZfQ sGotDbwD00Zfi6Px5/ThSC9M8oWF3+U//OP+qajqERs9enWa4eJjR0uZyLe2X/YgIxA9/nih8u+W dDudf8x6bo1vUz6jcxW4bw9RwCx/1RucDJ+h9lZ29lZwWdsgjt7aNIYYx0VI1CqPoAxYq2KuxV2K uxV8T/8AOSXmE6x+a+pRq3KDSo4tPh/55rzk+6WRxikPL1ZlYMpIYGoI2IIxS+pf+cTtc8zX2m+Y X1bUZ7rSLNrdbX61I0npuVkaXgzklV4haitPxxQWN63/AM5L+Vda1a4h1ryRaaro3MpbzTFGuvTB or/vI2AJ60BFPHFaUY4P+cVvNeyvfeUr2QfZZnWMN82+tQgfSv0Yru97/Kry35J8u+V107yjfR6l YtIZ571Jo52llcAFnaL4RsgAFOgxQkv52DVbyy0/TNPtJ7oF3urj0I3kCiNeKcuIP87fdm47J4Yy MpEDo6Ht0TlCMIgnezQ7nicsUsTmOVGjdeqMCCPoOdCCDyeTlExNEUzr8lY2fzsrDpHbTM3y+Ff1 tmt7WP7n4h3HYI/f/wCaf0Pfs5h7J2Kvm3/nL7zVSPRPKsL7sW1K8UHsKxQV/wCSn4YpCj/ziD5V 5Ta15qmTaMLp1mxH7TUlnofED0x9JxUvdvzGuTbfl95nuBx5RaVeuvLoWFu/EHp1OKHxf+SVv6/5 r+WEoTS9WSg6/u1L/wDGuKS9/wDP3/OUul+WfNF5oenaL+mUsT6Vxei7EC+uP7yNVEM1eB2Jr1rt itI/Vvz2juPySuvOcNt+jNSvHl07TrT1fWK3JJRXV+EXLglZPs9qYofMv5YeTJvOfnnTNDoxt5pP Vv5BX4baL45TUdCVHEH+YjFkX35DDFDCkMShIo1CRouwVVFAAPYYsVK/vrawsbm+un9O2tInnnf+ WONSzH6AMVfnh5h1i71/zDqGrz1e51K5kuGXqayuWCgDwrQDFk+n/wAzAPy6/wCcdLTy5ERHfXsU OnS07y3FZ7w/IgSL9OLF4F+TXlgeZPzL0LTZE52y3AubodvStgZmDezcOP04pL7D81/m9+XXlPVB pXmDVxZ35jWb0BBczURyQpLQxyKK8TsTXFCE0T89Pys1zVrXSdK1o3OoXjiO2gFpeLyY705PCqjp 1Jpiry//AJy981enY6L5WhejXDtqN4o/kjrFCD7Fi5/2OKQwv/nFTyx+k/zCl1iVA1vods0ik7/v 7isUf/CeofoxUvsDFDsVdirsVWTyrDDJKwJWNS5A6kKK7Yq/OXWdTn1XV77VLj/ei/uJbqbevxzO Xbf5tiyQeKvqDyb/AM6X/wA4t6nqx/dXmsR3EqH9rneOLOEj5IFcYofL+KXYq9l/5xTh1GT8zy9s 7Jaw2M73ygni6EqiK3b+8dSPligvSfz3/PvzD5O8z2/l/wAux2/qwwpcX89whkq0hJSJVDLQcAGJ 677U7qgMb0z/AJyt0+/RIPOPlSC7SlHntSrgePGC4Df8ncnDJKP0khhkwxmKkAfez3yP+af5BnUH vtLvE0S/nj9GSK9EluvBmDfactAN1HR8ty6rJkjwyNhx8OixYpGUI0S9etrq2urdLi1lSe3lHKOa Jg6MPFWWoIzHcpUxV8FfnH5q/wAT/mRrepo/O1Wc2tma1HoW/wC6Qr7PxL/TiyD7A/Jnyr/hj8tt E0104XUkAu7wd/Wuf3rA+6Bgn0YsV35zTGH8q/NDgA1sJU3/AMscP+NsVfDvlvzBf+XtYh1fT24X tukot5B1R5YniDj3TnyGLJL2S4dDcMrsjPxaYgkFzvQt498VTO88y6hdeW9N8vM1LDTZp7iNAftS 3HHkSP8AJCUHzPjir6A/5x60WDyb+XHmD8ytSQCWa3lFiGHW3tqmg6f384C/7EYoLzDRPzZ/OHWt csdKtfMt59Y1G4jt4gOJAaZwo2p0FcK0+h/+ckfMx8v/AJVzafHMzXmsNHpyOx+No6c53NOtUTif 9bAgPnD8iPKv+JPzP0e2kTnaWT/pC78PTtqOoPs0vBT88Ul6h/zmLqcpuvLOlgkRKlzcuOzMxjRP +BCt9+KhLv8AnEDR0n8067q7LU2NnHboT2N1Jyr86W5xUrv+crvLnlnS9Ts9ThaaXzHrkrzXTyS8 lS3t41iRVjAAUEkU/wBU4qEB/wA4m+VjqHne81+VKwaLbFYmI/4+Lqsa0PtEJPvxUsJ/PDzQPMn5 na3exvztbeX6laEfZ9O2Hp1X2ZwzfTiofRX/ADiz5X/RP5cfpSVKXOuXD3Fe/oRfuYgfpV2H+tig vY8VdirsVdirsVeSeff+cavIfmZ5rzTlbQdVlLO01sOUDu3d7ckL1/kK4rbwDzH/AM45/mjo+pR2 kGm/pWCd+EN7ZHnHuaAyBuLRe5YU98U29M/5yduIvLv5deVPJlq1EqgIXvFp8KxCv+s8ob6MVD5k xS7FX1D/AM4f+X/T0jXvMDrvczx2MDH+WBfUkp8zKv3YoLwn81df/T/5i+YdVDco5rySOButYYP3 MR/5FxrikMUxV2KvsT/nFK2v4vyveW5ctBcahcSWSHcLEFjRqexlR9sUFm35t+av8L/l3rero/C5 W3MFme/rz/uoyP8AVZ+X0YofC/lyfSLfX9OuNZSWXSobiKS+igVXkeFGDOih2RasBTc4sn1R/wBD dflt/wBW3Wf+RFr/ANlOKKb/ADB/MzSfOv5B+adb0myvLezjeCzBvUjjLs1zAGZPTkmqq+p9+2Kv k7S9Mv8AVdRttN0+Fri9vJFht4U6s7mgGKXuv56fl3Y+SPyn8q6XbBHuIr2RtQugKNNczQVdt9+P 7sKPYDFAeK+VfLt75k8x6doViK3OoTpCp7KCfjc+yLVj8sUvsD87fKs0H5HXuiaBGRb6ZDbUt0Hx NbWsiF+nUgLzbxpixfKv5WeatL8qeftI1/VLd7mxsXkaaKIAyDnE8auoYqCUZw3XtiyKe/nd+bX/ ACsPXraWzhktdF05GjsYJqeozSEGSVwpZQW4qKAmgGKAHtv/ADi1+XN5oWg3fmbVITBe60FSyicU dLRDy5EGhHqvvTwVT3xUsc/5zB0O9M/l/XUQtZqk1lNIB9iTkJIwx/yxyp8sVDA/yG/N/SPy7n1v 9K2dxdW+qRwGM2oQustsZOIYSPGOLCY1O5FOmKlin5l/mDqfnvzTPrl6noR8RDZWgPJYYEJKpXap JJZj3JxS+g/y2A/Ln/nHW+8yyD09R1GKW+iJ6+pcUgsx8vsP9JxYvlJmZmLMSWJqSdyScWT6q0D/ AJyi/K3RdD0/SLXTNYFvp9vFbR0gtRURIFr/AL09TSpxRSfaD/zlB5E13W7HRrDS9Xa81CeO2g5Q 2wUNIwUFiLk0UVqTTpitPYMUOxV2KuxV2KpZq/mXR9HuLWHU5xareFlgnfaLklKqz9F+1tXbLsWC eQExF00ZtTDGQJmrSH8yfyw8tfmHpFvaao8kUlsWlsL+2ZecZkA5U5BlZHotR7bEZS3gvmfzv/zj H5/0AyXGkKvmDT1qQ1qONyF3+1bkkk/8Yy2KbeR3FvcW07wXETwzxnjJFIpR1I7MpoQcUvsXyaP8 B/8AOOC6gQY7tdMm1AE7Ez3lXgB8P7xFxYvjbFk7FXYq/QT8tfLw8u+QdB0fjxktbOP1xSn76Qep N/yUdsWKv508o+VPM+j/AFPzRALjS7Z/rbK88tuitGrDm7xPFsqsftGnfFWIQf8AOPH5I3EEdxb6 Es0EyiSKWO+vWR0YVVlYT0II3BGKbU7H8gPyK1C1S7sNGiu7WQsEuIL+8kjYoxRgHS4IPFlIPvit spb8svIzeTx5OOlgeXFbn9RWWdasJPVqZQ4lJ577v+GKEr0b8pvyn8lXo8xWOmw6XPaqVF/cXU7J GJPgJrcSvGpPLjXrvTFU48/eVvI/mDRVj85RRPpNlKLn1Z53tY43oYwzSo8VB+8pQtTFUB5W/Jz8 t/KurLq+g6OLTUURo0nM9zMQrijUWaWRdxtWlcVZmQCKHcHqMVedaz/zj5+U2rX0l7PoggnlJaT6 rLNAhJNa+nGwQf7EDFUT5f8AyL/KvQrhLqy0GGS5Q8kmumkuSCOhCzM6AjxC4qzzFUHq+j6VrGnz adqtrFe2M44y28yh0Ydtj3HY4qwCL/nHD8no7v6z+gy+/IQvc3JjBrX7Pqbj2O2Krm/5xy/JlmLH y6Kk1NLu9A38AJ6DFbT3XNC/LnzJpNr5T1J7a6sYpvQtdMju2ib1rKPeICGRJGaGM1ZOw3IxVIf+ hcPyY/6l7/p8vv8Aqvim3f8AQuH5Mf8AUvf9Pl9/1XxW0z8ufkp+WPlvWLfWdG0UW2pWvL0JzcXU vHmpRiFlldPssR0xQzfFXYq7FXYq7FWMfmP5abzB5XuLaFeV5bkXFoB1MiA1X/ZKSPnmZoNR4WQE 8jsXA7S0vjYTEfUNx73iHljz75l8tSCO1mMloG/eWM9Wj96Dqh/1c6PUaLHm3I373ktJ2jmwGgbj 3H8bPYvKv5reW9c4QTv+jtQbb0J2HBj/AJEuyn5Gh9s0Gp7NyY9x6ovUaPtbFm2Ppl3H9BTjX/Iv k7zDNFPrejWmoTwkGOWeJWcU7cqVK/5J2zXu0SX84/J+peavy41TQdI4reyLE9tCSEVzBKsnp1JA FQlBXauKvh7XvLmveX75rHWrCfT7ta/up0KVA7qTsw912xZJbirJ/wAsfL3+IfzA0HSCOUdzeRmc UrWGI+rL/wAk0bFS/QLFi8Os9U80XFp5uay0TVIPMvmfV47aK4vNNl+rQ6fzW1hZ/rEZidEtld25 Ar8W/uVS8fmL+Y9/q+r2ukxzrDpljfkaBa2cbKoDfVNM9OVEaWRnc+qzIQnFdhQE4qs8p+X/AM0b DX9C8qyXmpW3lXRbqThcW9msKO1laRSMrv6fF4J7iWURl2IanduPFVvTfNn/ADkdPZhpNOuFkTTI 76stnEjvJBLKskLKYkAnuGVB6Y3WL4gA5wKran5o/OOP8vobq/sH1DUL2/Ny9rc2ACwWNpEJWhuF lt4VBmnCrFVOfZWLb4VQP6T/ADC886JJZzXN3qOjXOrxn62thGvpQaTB9aul9OJP923XCOFZGJJW m5rirJfLd1+Zk+v6HqXmmXVUt9J0D9IX1va2YEd3e3ErBoHiSOhmSExjgByBrxC/FUKk2t6r+all f65r2kaPqNrdeYRPPaTR2Iu7mO3sEWCwtXiKyJCZGeSZ+YJpsPiwqnOqa3+eFnqms2ttFPe2ukWt nepcraxAXUiWSia0tiIqSetdlnk4/EirxXiSMCpp5T1L817zy15wvLtriXUYkMXlmC9s47FjMtqH aRY+CkxtNJROZP2aE1rirCdDTzbpD3GsaR5Uunv4rO0tpdb1OyvJdWN9dyql5LxeRhPFCquwEcYG 670rUqraVr//ADkHfyzAS39pGL+G1szeaVCpZLi7YGScCFQsUFrAS7qaVcDlWmBUdqHmj89/0Po9 tZWtyLrVLm8d9VlsQJbeFbgpaR3EKW8qxgxjmxaMEig5KanFWRa1qmoQfmtZ3WoaRql9aaFpDpY3 FlY3EtvJf3fxXDrIqsgpDGEG9asR1xVgGpQ/m2moabq9rp13D5jttLa9heHTY2t2utRuTPeJMVjC I8dtGkTDj6zsO5NcKp5qvmH89tO8s2TP9dutV1Kwm1B5LPS45XtZwB6FiUCMqt8fKRpFr8PFRy6h UTr/AJh/NfTNB8xa7d6s9hb6O9lY6daPZW6yXtwnoLdzRtLGOSzO0gjCrQ/s9MVeoeTB5lbQo7rz JIDql673LWgVFW0jlPKO1BVVLeklAzNUlq70piqeYq7FXYq7FXYq7FXlX5k/lTLe3Eus6BGDcSVe 8sQQObdS8dduR7r37b5utB2kIjgny6F57tTsgzJyY/q6jv8Ac8eubW5tZmguYngnQ0eKRSrA+4ND m/jIEWNw8tOEompCiyzyp+aHmTQCkDSfX9PWg+qzkkqo7RvuV/Ee2YOp7Ox5d/pl3h2ej7Xy4dj6 o9x/QXuHlXzVZ+Y9OF7bQT246Mk8bKK/5D04OPkfnTOc1OmOKVEgvXaXVRzQ4gCPeEVrnl7Q9esX sNZsINQtH6wzoHAPitd1PuN8x3JeF+ef+cS9Ju+d15Ovzp8xqRp14WkgJ8EmFZEH+sHxTa38gPyM 82eVfN1x5g8zQRWotYZILCFJUmaR5aBpQYywVQnIb779MVJfQ2KHEAih3BxVA6VoOh6PHJFpOnWu nRytzlS0hjgVm/mYRhanfFUdirsVQ+o6bp2p2cllqNrDe2c1BLbXEayxPQ1HJHDKaEV3GKrrOztL K2jtbOCO2tYhxighRY41HgqqAB9GKq2KuxV2KuxV2KuxV2KuxV2KuxVB32j6Vf3Nnc3tpFcz6fIZ rKSVQxikI480r0anfFUZirsVdirsVdirsVdirsVQt9pWl36hb6zgu1HRZ40kA/4MHJwySj9JIYTx RmKkAfehoPK/lm3kEkGkWUMg6PHbxK33hcnLUZDzlL5lrhpcUTYjEfAJn0ylvdirsVdirsVdirsV dirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVd irsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdir/9k= + + + + + + uuid:FF8F96D84F1DDA11BBE0D49BBEFDCBAA + uuid:112b4e8a-7599-4f4a-849d-4002d9dd1bb8 + + + + + + + + + + + + + + + + + + + + + + + + + + +endstream endobj xref +0 233 +0000000003 00000 f +0000000016 00000 n +0000000077 00000 n +0000000004 00001 f +0000000006 00000 f +0000000127 00000 n +0000000007 00001 f +0000000008 00000 f +0000000009 00000 f +0000000010 00000 f +0000000011 00000 f +0000000012 00000 f +0000000013 00000 f +0000000014 00001 f +0000000015 00001 f +0000000016 00000 f +0000000017 00000 f +0000000018 00001 f +0000000019 00001 f +0000000020 00001 f +0000000021 00000 f +0000000022 00001 f +0000000023 00000 f +0000000024 00000 f +0000000025 00001 f +0000000026 00001 f +0000000027 00001 f +0000000028 00000 f +0000000029 00001 f +0000000030 00000 f +0000000031 00000 f +0000000032 00001 f +0000000033 00001 f +0000000034 00001 f +0000000035 00000 f +0000000036 00000 f +0000000037 00000 f +0000000038 00000 f +0000000039 00000 f +0000000040 00000 f +0000000041 00000 f +0000000042 00000 f +0000000043 00000 f +0000000044 00000 f +0000000045 00000 f +0000000046 00000 f +0000000047 00000 f +0000000048 00000 f +0000000049 00000 f +0000000050 00000 f +0000000051 00000 f +0000000052 00000 f +0000000053 00000 f +0000000054 00000 f +0000000055 00000 f +0000000056 00000 f +0000000057 00000 f +0000000058 00000 f +0000000059 00000 f +0000000060 00000 f +0000000061 00000 f +0000000062 00000 f +0000000063 00000 f +0000000064 00000 f +0000000065 00000 f +0000000066 00000 f +0000000067 00000 f +0000000068 00000 f +0000000069 00000 f +0000000070 00000 f +0000000071 00000 f +0000000072 00000 f +0000000073 00000 f +0000000074 00000 f +0000000075 00000 f +0000000076 00000 f +0000000077 00000 f +0000000078 00000 f +0000000079 00000 f +0000000080 00000 f +0000000081 00000 f +0000000082 00000 f +0000000083 00000 f +0000000084 00000 f +0000000085 00000 f +0000000086 00000 f +0000000087 00000 f +0000000088 00000 f +0000000089 00000 f +0000000090 00000 f +0000000091 00000 f +0000000092 00000 f +0000000093 00000 f +0000000094 00000 f +0000000095 00000 f +0000000096 00000 f +0000000097 00000 f +0000000098 00000 f +0000000099 00000 f +0000000100 00000 f +0000000101 00000 f +0000000102 00000 f +0000000103 00000 f +0000000104 00000 f +0000000105 00000 f +0000000106 00000 f +0000000107 00000 f +0000000108 00000 f +0000000109 00000 f +0000000110 00000 f +0000000111 00000 f +0000000112 00000 f +0000000113 00000 f +0000000114 00000 f +0000000115 00000 f +0000000116 00000 f +0000000117 00000 f +0000000118 00000 f +0000000119 00000 f +0000000120 00000 f +0000000121 00000 f +0000000122 00000 f +0000000123 00000 f +0000000124 00000 f +0000000125 00000 f +0000000126 00000 f +0000000127 00000 f +0000000128 00000 f +0000000129 00000 f +0000000130 00000 f +0000000131 00000 f +0000000132 00000 f +0000000133 00000 f +0000000134 00000 f +0000000135 00000 f +0000000136 00000 f +0000000137 00000 f +0000000138 00000 f +0000000139 00000 f +0000000140 00000 f +0000000141 00000 f +0000000142 00000 f +0000000143 00000 f +0000000144 00000 f +0000000145 00000 f +0000000146 00000 f +0000000147 00000 f +0000000148 00000 f +0000000149 00000 f +0000000150 00000 f +0000000151 00000 f +0000000152 00000 f +0000000153 00000 f +0000000154 00000 f +0000000155 00000 f +0000000156 00000 f +0000000157 00000 f +0000000158 00000 f +0000000159 00000 f +0000000160 00000 f +0000000161 00000 f +0000000162 00000 f +0000000163 00000 f +0000000164 00000 f +0000000165 00000 f +0000000166 00000 f +0000000167 00000 f +0000000168 00000 f +0000000169 00000 f +0000000170 00000 f +0000000171 00000 f +0000000172 00001 f +0000000173 00001 f +0000000174 00001 f +0000000175 00000 f +0000000176 00001 f +0000000177 00001 f +0000000178 00000 f +0000000179 00000 f +0000000180 00000 f +0000000181 00001 f +0000000182 00001 f +0000000183 00000 f +0000000184 00000 f +0000000185 00000 f +0000000186 00000 f +0000000187 00000 f +0000000188 00000 f +0000000189 00000 f +0000000190 00001 f +0000000191 00000 f +0000000192 00000 f +0000000193 00000 f +0000000194 00000 f +0000000195 00000 f +0000000196 00001 f +0000000197 00000 f +0000000198 00000 f +0000000199 00000 f +0000000200 00000 f +0000000201 00000 f +0000000202 00000 f +0000000203 00000 f +0000000204 00001 f +0000000205 00001 f +0000000206 00000 f +0000000207 00000 f +0000000208 00000 f +0000000209 00001 f +0000000210 00000 f +0000000211 00000 f +0000000212 00000 f +0000000213 00000 f +0000000222 00000 f +0000000962 00000 n +0000001037 00000 n +0000001259 00000 n +0000002254 00000 n +0000010101 00000 n +0000075690 00000 n +0000141279 00000 n +0000163833 00000 n +0000000223 00001 f +0000000000 00001 f +0000163946 00000 n +0000164319 00000 n +0000164578 00000 n +0000166617 00000 n +0000166740 00000 n +0000168460 00000 n +0000168509 00000 n +0000169023 00000 n +0000169529 00000 n +trailer +<<6900ADF75DFA7C479ED6082881A1314D>]>> +startxref +184360 +%%EOF diff --git a/ext/lwjgl/res/logo/lwjgl_logo-with_jacket.svg b/ext/lwjgl/res/logo/lwjgl_logo-with_jacket.svg new file mode 100644 index 0000000..e97e236 --- /dev/null +++ b/ext/lwjgl/res/logo/lwjgl_logo-with_jacket.svg @@ -0,0 +1,198 @@ + + + + +]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ext/lwjgl/res/lwjgl_16x16.xpm b/ext/lwjgl/res/lwjgl_16x16.xpm new file mode 100644 index 0000000..a893eb2 --- /dev/null +++ b/ext/lwjgl/res/lwjgl_16x16.xpm @@ -0,0 +1,141 @@ +/* XPM */ +static char * lwjgl_logo_2a_xpm[] = { +"16 16 122 2", +" c #000000", +". c #010101", +"+ c #020202", +"@ c #030303", +"# c #0A0A0A", +"$ c #0C0C0C", +"% c #0E0E0E", +"& c #171717", +"* c #1B1B1B", +"= c #1D1D1D", +"- c #1F1F1F", +"; c #222222", +"> c #232323", +", c #2B2B2B", +"' c #2D2D2D", +") c #333333", +"! c #3D3D3D", +"~ c #404040", +"{ c #434343", +"] c #4A4A4A", +"^ c #4F4F4F", +"/ c #525252", +"( c #565656", +"_ c #585858", +": c #595959", +"< c #5C5C5C", +"[ c #5E5E5E", +"} c #5F5F5F", +"| c #666666", +"1 c #686868", +"2 c #6C6C6C", +"3 c #6D6D6D", +"4 c #397CB5", +"5 c #3A7DB5", +"6 c #3D7FB6", +"7 c #3E80B6", +"8 c #747474", +"9 c #787878", +"0 c #4886BA", +"a c #4C89BC", +"b c #4D89BC", +"c c #508BBD", +"d c #508CBD", +"e c #76828D", +"f c #548EBF", +"g c #5690C0", +"h c #848484", +"i c #5B92C1", +"j c #7D8D9B", +"k c #6096C3", +"l c #8C8C8C", +"m c #8D8F91", +"n c #669AC5", +"o c #919191", +"p c #6D9EC8", +"q c #8499AA", +"r c #959595", +"s c #71A1CA", +"t c #74A4CB", +"u c #9B9B9B", +"v c #77A5CC", +"w c #9C9EA0", +"x c #7CA8CE", +"y c #7EA9CE", +"z c #A1A1A1", +"A c #81ABCE", +"B c #82ACD0", +"C c #84ADD0", +"D c #A5A5A5", +"E c #A6A6A7", +"F c #86AFD2", +"G c #87B0D2", +"H c #A8A8A8", +"I c #8CB3D4", +"J c #8DB4D4", +"K c #91B6D5", +"L c #94B7D5", +"M c #97BAD8", +"N c #9EBFDA", +"O c #A2C1DC", +"P c #BCBCBC", +"Q c #BDBDBD", +"R c #BFBFBF", +"S c #C0C0C0", +"T c #B0C5D6", +"U c #C6C6C6", +"V c #CCCCCC", +"W c #CFCFCF", +"X c #D0D0D0", +"Y c #C2D7E8", +"Z c #D3D3D3", +"` c #C4D8E9", +" . c #D4D4D4", +".. c #C6D9E9", +"+. c #D6D6D6", +"@. c #D0E0ED", +"#. c #DADEE1", +"$. c #D2E1EE", +"%. c #DFDFDF", +"&. c #E4E4E4", +"*. c #DAE7F1", +"=. c #E5E5E5", +"-. c #E6E6E6", +";. c #E0EAF3", +">. c #E2ECF4", +",. c #E4EDF5", +"'. c #E7EFF6", +"). c #EEEEEE", +"!. c #E8F0F6", +"~. c #EFEFEF", +"{. c #EBF2F8", +"]. c #EDF3F8", +"^. c #F2F2F2", +"/. c #F5F5F5", +"(. c #F6F6F6", +"_. c #F4F8FB", +":. c #FBFBFB", +"<. c #FAFCFD", +"[. c #FCFDFE", +"}. c #FEFEFE", +"|. c #FEFFFF", +"1. c #FFFFFF", +"1.1.1.1.1.1.|.Y t c f F '.1.1.1.", +"1.1.1.1.1./.m e j q L 5 0 *.1.1.", +"1.1.1.1._.w &.C 5 i [.1.", +"1.1.1.1.K } ; 1.!.4 5 ` 1.", +"1.1.1...A = | 1.@.5 5 J 1.", +"^.D %.O T # H 1.M 5 5 s 1.", +"z . > V X D h < ~.1.k 5 5 p 1.", +"[ W - @ , 2 U <.N v a x 1.", +"* & +.. / =.! 1 E #.1.", +"8 % : r o u + { ", +"1.).l R ( Z _ ) ", +"1.1.[.1.S ~ + * :.& 9 ", +"1.1.$.y ;.1. .3 h Z Q ", +"1.1.}.n 6 B ,.1.1.=.' $ (.", +"1.1.1.>.b 5 7 G '.1.}.P ^ @ ] 1.", +"1.1.1.1.{.I g d v ].1.1.1.%.-.1."}; diff --git a/ext/lwjgl/res/lwjgl_32x32.xpm b/ext/lwjgl/res/lwjgl_32x32.xpm new file mode 100644 index 0000000..1b835e8 --- /dev/null +++ b/ext/lwjgl/res/lwjgl_32x32.xpm @@ -0,0 +1,275 @@ +/* XPM */ +static char * lwjgl_32x32_xpm[] = { +"32 32 240 2", +" c #FFFFFF", +". c #FAFCFD", +"+ c #CBDDEC", +"@ c #93B7D6", +"# c #6FA0C9", +"$ c #5C93C2", +"% c #6096C3", +"& c #7AA7CD", +"* c #ACC8E0", +"= c #EDF3F8", +"- c #FCFDFE", +"; c #B5CEE3", +"> c #5A92C1", +", c #3A7DB5", +"' c #4585B9", +") c #A1C1DC", +"! c #FCFCFC", +"~ c #E2EAF1", +"{ c #B8CFE3", +"] c #AEC9E0", +"^ c #A3C2DC", +"/ c #98BAD8", +"( c #8CB3D3", +"_ c #81ACCF", +": c #6E9FC8", +"< c #3D7FB6", +"[ c #72A2CA", +"} c #F5F8FB", +"| c #D8D8D8", +"1 c #313131", +"2 c #242424", +"3 c #404040", +"4 c #525252", +"5 c #656565", +"6 c #747474", +"7 c #8F8F8F", +"8 c #F7F8F8", +"9 c #ACC8DF", +"0 c #3B7DB5", +"a c #79A7CD", +"b c #FEFEFE", +"c c #686868", +"d c #000000", +"e c #B6B6B6", +"f c #86AED1", +"g c #AECAE1", +"h c #D3E2EE", +"i c #E1EAF2", +"j c #282828", +"k c #020202", +"l c #DEDEDE", +"m c #5B92C1", +"n c #4886BA", +"o c #F4F8FB", +"p c #ECF3F8", +"q c #6499C5", +"r c #D7D9DB", +"s c #050505", +"t c #C0D5E7", +"u c #397CB5", +"v c #ABC8E0", +"w c #FEFFFF", +"x c #78A6CC", +"y c #7BA7CD", +"z c #9E9E9E", +"A c #E2ECF4", +"B c #377BB4", +"C c #669AC5", +"D c #C3D7E8", +"E c #397DB5", +"F c #B2CCE1", +"G c #585858", +"H c #A9A9A9", +"I c #BCD2E5", +"J c #4081B7", +"K c #EEF4F9", +"L c #DDE5ED", +"M c #1C1C1C", +"N c #070707", +"O c #E7E7E7", +"P c #85AED1", +"Q c #CCDEEC", +"R c #BAD2E5", +"S c #5790BF", +"T c #D0D1D1", +"U c #030303", +"V c #333333", +"W c #FDFDFD", +"X c #FBFCFD", +"Y c #508BBD", +"Z c #B1CCE2", +"` c #CACACA", +" . c #373737", +".. c #616161", +"+. c #A2A2A2", +"@. c #DDDDDD", +"#. c #E4ECF3", +"$. c #B1CBE1", +"%. c #B4CDE2", +"&. c #E6E6E6", +"*. c #232323", +"=. c #727272", +"-. c #D8E5F0", +";. c #A0C0DB", +">. c #666666", +",. c #060606", +"'. c #272727", +"). c #D4D4D4", +"!. c #F6F6F6", +"~. c #D0D0D0", +"{. c #979797", +"]. c #5E5E5E", +"^. c #040404", +"/. c #C0C0C0", +"(. c #A2C1DB", +"_. c #9EBFDA", +":. c #FBFBFB", +"<. c #252525", +"[. c #5C5C5C", +"}. c #EAEAEA", +"|. c #B5B5B5", +"1. c #E9E9E9", +"2. c #B2B2B2", +"3. c #BDBDBD", +"4. c #6B9DC7", +"5. c #DBDBDB", +"6. c #838383", +"7. c #5F5F5F", +"8. c #0B0B0B", +"9. c #767676", +"0. c #B7B7B7", +"a. c #F0F0F0", +"b. c #F0F5F9", +"c. c #4B88BB", +"d. c #B2CCE2", +"e. c #9A9A9A", +"f. c #C5C5C5", +"g. c #F7F7F7", +"h. c #1D1D1D", +"i. c #3C3C3C", +"j. c #DFDFDF", +"k. c #F3F7FA", +"l. c #CCDDEB", +"m. c #9EBEDA", +"n. c #72A1C9", +"o. c #4C89BB", +"p. c #C9DCEB", +"q. c #565656", +"r. c #0F0F0F", +"s. c #F9F9F9", +"t. c #D1D1D1", +"u. c #8E8E8E", +"v. c #848484", +"w. c #707070", +"x. c #B0B0B0", +"y. c #F9FBFC", +"z. c #DCE8F2", +"A. c #F6F9FC", +"B. c #151515", +"C. c #4E4E4E", +"D. c #919191", +"E. c #BBBBBB", +"F. c #2F2F2F", +"G. c #393939", +"H. c #909090", +"I. c #4D4D4D", +"J. c #101010", +"K. c #5A5A5A", +"L. c #090909", +"M. c #323232", +"N. c #B1B1B1", +"O. c #A1A1A1", +"P. c #353535", +"Q. c #F5F5F5", +"R. c #131313", +"S. c #454545", +"T. c #F8F8F8", +"U. c #2B2B2B", +"V. c #BEBEBE", +"W. c #505050", +"X. c #C2C2C2", +"Y. c #868686", +"Z. c #D2D2D2", +"`. c #010101", +" + c #434343", +".+ c #DCDCDC", +"++ c #E0E0E0", +"@+ c #959595", +"#+ c #C8C8C8", +"$+ c #888888", +"%+ c #292929", +"&+ c #181818", +"*+ c #4A4A4A", +"=+ c #CFCFCF", +"-+ c #F1F6FA", +";+ c #4B4B4B", +">+ c #86AFD1", +",+ c #90B5D5", +"'+ c #EAF1F7", +")+ c #787878", +"!+ c #1B1B1B", +"~+ c #969696", +"{+ c #C6C6C6", +"]+ c #C3D8E9", +"^+ c #4383B8", +"/+ c #94B8D6", +"(+ c #F4F4F4", +"_+ c #A7A7A7", +":+ c #858585", +"<+ c #9D9D9D", +"[+ c #FAFBFD", +"}+ c #5D94C2", +"|+ c #4685B9", +"1+ c #98BBD8", +"2+ c #EFF4F9", +"3+ c #9B9B9B", +"4+ c #C5D9E9", +"5+ c #3B7EB6", +"6+ c #F2F6FA", +"7+ c #2C2C2C", +"8+ c #8FB5D5", +"9+ c #4A87BB", +"0+ c #FAFAFA", +"a+ c #6E6E6E", +"b+ c #85AFD1", +"c+ c #4D89BC", +"d+ c #A8C5DE", +"e+ c #0E0E0E", +"f+ c #B8B8B8", +"g+ c #FEFEFF", +"h+ c #B1CBE2", +"i+ c #4C89BC", +"j+ c #BFD4E7", +"k+ c #ECECEC", +"l+ c #939393", +"m+ c #81ACD0", +"n+ c #6599C5", +"o+ c #6197C4", +"p+ c #6C9EC8", +"q+ c #F7FAFC", +" . + @ # $ % & * = ", +" - ; > , , , , , , , ' ) - ", +" ! ~ { ] ^ / ( _ : < , , , [ } ", +" | 1 2 1 3 4 5 6 7 8 9 0 , , , a - ", +" b c d d d d d d d d e b f , , , , g ", +" h i j d d d d d d d k l } m , , , n o ", +" p q r s d d d d d d d 2 b t u , , , v ", +" w x y z d d d d d d d d 5 A B , , , C ", +" D E F G d d d d d d d d H I , , , , J K ", +" . > < L M d d d d d d d N O P , , , , E Q ", +" b R , S T U d d d d d d d V W X Y , , , , E Z ", +" ` ...+.@.#.$.%.&.*.d d d d d d d =. -., , , , , , ;. ", +" >.d d d ,.'.>.). !.~.{.].'.^.d U /. (., , , , , , _. ", +":.<.d d d d d d [. }.7 |.1.W O 2.3.b 4., , , , , , ) ", +"5.U d d d d d d 6. 7.d d 8. .9.0.a. b.c.< u , , , , d. ", +"e.d d d d d d d f.g.h.d d d d d d N i.j. X - k.l.m.n.o., p. ", +"q.d d d d d d r.s.t.U d d d d d d d d u. s.v.w.x.}. y.z.A. ", +"B.d d d d d d C. D.d d d d d d d d d E. z d d d ^.F.w.x.&.b ", +"G.d d d d d d H. I.d d d d d d d d J.a. K.d d d d d d d L.M.N.", +"!.O.P.k d d k t.Q.R.d d d d d d d d S.b T.h.d d d d d d d d d U.", +" ! V.W.s <.s.X.d d d d d d d d d Y. Z.`.d d d d d d d d d +", +" b .+++ @+d d d d d d d d `.#+ u.d d d d d d d d d d $+", +" j.%+d d d d d d d &+Q. *+d d d d d d d d d d =+", +" -+ s.|.;+,.d d d d 4 T.J.d d d d d d d d d M T.", +" >+,+'+ 5.)+!+d d ~+ {+d d d d d d d d d d K. ", +" ]+, ^+/+= (+_+:+(+ Y.d d d d d d d d d d <+ ", +" [+}+, , |+1+2+ 3+d d d d d d d d d s @. ", +" 4+5+, , , n m.6+ :.u.<.`.d d d d d d 7+! ", +" 8+, , , , , 9+^ k. 0+x.S.^.d d d d a+ ", +" - b+, , , , , , c+d+A. b Z.c e+d `.f+ ", +" g+h+i+E , , , , , c+j+ k+l+3+W ", +" k.; m+n+o+p+8+4+q+ "}; diff --git a/ext/lwjgl/res/resources.txt b/ext/lwjgl/res/resources.txt new file mode 100644 index 0000000..5f359ca --- /dev/null +++ b/ext/lwjgl/res/resources.txt @@ -0,0 +1,11 @@ +The following files are included in the resource archive: +Footsteps.wav - from openal.org cvs +ding.wav - from openal.org cvs +left.wav - sampled by matzon +right.wav - sampled by matzon +center.wav - sampled by matzon + +optional: +Missing_you.mod - Missing You, by Doh (Nicolas Desessart, http://doh.av7.net) +phero.mp3 - snipped from unreleased Phero track +phero2.ogg - snipped from unreleased Phero track \ No newline at end of file diff --git a/ext/lwjgl/res/right.wav b/ext/lwjgl/res/right.wav new file mode 100644 index 0000000..f23dbed Binary files /dev/null and b/ext/lwjgl/res/right.wav differ diff --git a/ext/lwjgl/res/spaceinvaders/alien.gif b/ext/lwjgl/res/spaceinvaders/alien.gif new file mode 100644 index 0000000..19bf8fc Binary files /dev/null and b/ext/lwjgl/res/spaceinvaders/alien.gif differ diff --git a/ext/lwjgl/res/spaceinvaders/alien2.gif b/ext/lwjgl/res/spaceinvaders/alien2.gif new file mode 100644 index 0000000..199b211 Binary files /dev/null and b/ext/lwjgl/res/spaceinvaders/alien2.gif differ diff --git a/ext/lwjgl/res/spaceinvaders/alien3.gif b/ext/lwjgl/res/spaceinvaders/alien3.gif new file mode 100644 index 0000000..3035868 Binary files /dev/null and b/ext/lwjgl/res/spaceinvaders/alien3.gif differ diff --git a/ext/lwjgl/res/spaceinvaders/gotyou.gif b/ext/lwjgl/res/spaceinvaders/gotyou.gif new file mode 100644 index 0000000..1c373e2 Binary files /dev/null and b/ext/lwjgl/res/spaceinvaders/gotyou.gif differ diff --git a/ext/lwjgl/res/spaceinvaders/hit.wav b/ext/lwjgl/res/spaceinvaders/hit.wav new file mode 100644 index 0000000..33dc4bd Binary files /dev/null and b/ext/lwjgl/res/spaceinvaders/hit.wav differ diff --git a/ext/lwjgl/res/spaceinvaders/loose.wav b/ext/lwjgl/res/spaceinvaders/loose.wav new file mode 100644 index 0000000..e11886a Binary files /dev/null and b/ext/lwjgl/res/spaceinvaders/loose.wav differ diff --git a/ext/lwjgl/res/spaceinvaders/pressanykey.gif b/ext/lwjgl/res/spaceinvaders/pressanykey.gif new file mode 100644 index 0000000..7212521 Binary files /dev/null and b/ext/lwjgl/res/spaceinvaders/pressanykey.gif differ diff --git a/ext/lwjgl/res/spaceinvaders/ship.gif b/ext/lwjgl/res/spaceinvaders/ship.gif new file mode 100644 index 0000000..cb1db98 Binary files /dev/null and b/ext/lwjgl/res/spaceinvaders/ship.gif differ diff --git a/ext/lwjgl/res/spaceinvaders/shot.gif b/ext/lwjgl/res/spaceinvaders/shot.gif new file mode 100644 index 0000000..7a76f2e Binary files /dev/null and b/ext/lwjgl/res/spaceinvaders/shot.gif differ diff --git a/ext/lwjgl/res/spaceinvaders/shot.wav b/ext/lwjgl/res/spaceinvaders/shot.wav new file mode 100644 index 0000000..dda47d2 Binary files /dev/null and b/ext/lwjgl/res/spaceinvaders/shot.wav differ diff --git a/ext/lwjgl/res/spaceinvaders/start.wav b/ext/lwjgl/res/spaceinvaders/start.wav new file mode 100644 index 0000000..3cb25b9 Binary files /dev/null and b/ext/lwjgl/res/spaceinvaders/start.wav differ diff --git a/ext/lwjgl/res/spaceinvaders/win.wav b/ext/lwjgl/res/spaceinvaders/win.wav new file mode 100644 index 0000000..76250e4 Binary files /dev/null and b/ext/lwjgl/res/spaceinvaders/win.wav differ diff --git a/ext/lwjgl/res/spaceinvaders/youwin.gif b/ext/lwjgl/res/spaceinvaders/youwin.gif new file mode 100644 index 0000000..697318b Binary files /dev/null and b/ext/lwjgl/res/spaceinvaders/youwin.gif differ diff --git a/ext/slick/OpenAL32.dll b/ext/slick/OpenAL32.dll new file mode 100644 index 0000000..22eb15f Binary files /dev/null and b/ext/slick/OpenAL32.dll differ diff --git a/ext/slick/applet/applet.html b/ext/slick/applet/applet.html new file mode 100644 index 0000000..9cb1098 --- /dev/null +++ b/ext/slick/applet/applet.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/ext/slick/applet/lwjgl_util_applet.jar b/ext/slick/applet/lwjgl_util_applet.jar new file mode 100644 index 0000000..098631a Binary files /dev/null and b/ext/slick/applet/lwjgl_util_applet.jar differ diff --git a/ext/slick/build.xml b/ext/slick/build.xml new file mode 100644 index 0000000..41d53fa --- /dev/null +++ b/ext/slick/build.xml @@ -0,0 +1,695 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Slick]]> + Copyright © 2006 New Dawn Software. All Rights Reserved.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Slick]]> + Copyright © 2006 New Dawn Software. All Rights Reserved.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + VERSION|0.4.8|SUBJECT|Webstart Demos|CONTENT|[html] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [/html]|DATE|1162965600 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <a href="http://slick.cokeandcode.com/demos/@{name}.jnlp"> @{title} </a> + <br/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ext/slick/javadoc.zip b/ext/slick/javadoc.zip new file mode 100644 index 0000000..6a37cae Binary files /dev/null and b/ext/slick/javadoc.zip differ diff --git a/ext/slick/jinput-dx8.dll b/ext/slick/jinput-dx8.dll new file mode 100644 index 0000000..0ad824b Binary files /dev/null and b/ext/slick/jinput-dx8.dll differ diff --git a/ext/slick/jinput-raw.dll b/ext/slick/jinput-raw.dll new file mode 100644 index 0000000..2a014f6 Binary files /dev/null and b/ext/slick/jinput-raw.dll differ diff --git a/ext/slick/lib/hiero.jar b/ext/slick/lib/hiero.jar new file mode 100644 index 0000000..fa05723 Binary files /dev/null and b/ext/slick/lib/hiero.jar differ diff --git a/ext/slick/lib/ibxm.jar b/ext/slick/lib/ibxm.jar new file mode 100644 index 0000000..619d26e Binary files /dev/null and b/ext/slick/lib/ibxm.jar differ diff --git a/ext/slick/lib/jinput.jar b/ext/slick/lib/jinput.jar new file mode 100644 index 0000000..e12e523 Binary files /dev/null and b/ext/slick/lib/jinput.jar differ diff --git a/ext/slick/lib/jnlp.jar b/ext/slick/lib/jnlp.jar new file mode 100644 index 0000000..8fd98f2 Binary files /dev/null and b/ext/slick/lib/jnlp.jar differ diff --git a/ext/slick/lib/jogg-0.0.7.jar b/ext/slick/lib/jogg-0.0.7.jar new file mode 100644 index 0000000..ecb0260 Binary files /dev/null and b/ext/slick/lib/jogg-0.0.7.jar differ diff --git a/ext/slick/lib/jorbis-0.0.15.jar b/ext/slick/lib/jorbis-0.0.15.jar new file mode 100644 index 0000000..4cf51f9 Binary files /dev/null and b/ext/slick/lib/jorbis-0.0.15.jar differ diff --git a/ext/slick/lib/lwjgl.jar b/ext/slick/lib/lwjgl.jar new file mode 100644 index 0000000..10602a4 Binary files /dev/null and b/ext/slick/lib/lwjgl.jar differ diff --git a/ext/slick/lib/natives-linux.jar b/ext/slick/lib/natives-linux.jar new file mode 100644 index 0000000..15d28f0 Binary files /dev/null and b/ext/slick/lib/natives-linux.jar differ diff --git a/ext/slick/lib/natives-mac.jar b/ext/slick/lib/natives-mac.jar new file mode 100644 index 0000000..de5e473 Binary files /dev/null and b/ext/slick/lib/natives-mac.jar differ diff --git a/ext/slick/lib/natives-win32.jar b/ext/slick/lib/natives-win32.jar new file mode 100644 index 0000000..dc0c9eb Binary files /dev/null and b/ext/slick/lib/natives-win32.jar differ diff --git a/ext/slick/lib/packulike.jar b/ext/slick/lib/packulike.jar new file mode 100644 index 0000000..15a56ea Binary files /dev/null and b/ext/slick/lib/packulike.jar differ diff --git a/ext/slick/lib/pedigree.jar b/ext/slick/lib/pedigree.jar new file mode 100644 index 0000000..c75da2c Binary files /dev/null and b/ext/slick/lib/pedigree.jar differ diff --git a/ext/slick/lib/scalar.jar b/ext/slick/lib/scalar.jar new file mode 100644 index 0000000..82268fc Binary files /dev/null and b/ext/slick/lib/scalar.jar differ diff --git a/ext/slick/lib/slick-examples.jar b/ext/slick/lib/slick-examples.jar new file mode 100644 index 0000000..27fe577 Binary files /dev/null and b/ext/slick/lib/slick-examples.jar differ diff --git a/ext/slick/lib/slick-sources.jar b/ext/slick/lib/slick-sources.jar new file mode 100644 index 0000000..0214ed0 Binary files /dev/null and b/ext/slick/lib/slick-sources.jar differ diff --git a/ext/slick/lib/slick-util-src.zip b/ext/slick/lib/slick-util-src.zip new file mode 100644 index 0000000..58e6d02 Binary files /dev/null and b/ext/slick/lib/slick-util-src.zip differ diff --git a/ext/slick/lib/slick-util.jar b/ext/slick/lib/slick-util.jar new file mode 100644 index 0000000..768941d Binary files /dev/null and b/ext/slick/lib/slick-util.jar differ diff --git a/ext/slick/lib/slick.jar b/ext/slick/lib/slick.jar new file mode 100644 index 0000000..214033d Binary files /dev/null and b/ext/slick/lib/slick.jar differ diff --git a/ext/slick/lib/tinylinepp.jar b/ext/slick/lib/tinylinepp.jar new file mode 100644 index 0000000..7e0075d Binary files /dev/null and b/ext/slick/lib/tinylinepp.jar differ diff --git a/ext/slick/lib/win32/OpenAL32.dll b/ext/slick/lib/win32/OpenAL32.dll new file mode 100644 index 0000000..22eb15f Binary files /dev/null and b/ext/slick/lib/win32/OpenAL32.dll differ diff --git a/ext/slick/lib/win32/jinput-dx8.dll b/ext/slick/lib/win32/jinput-dx8.dll new file mode 100644 index 0000000..0ad824b Binary files /dev/null and b/ext/slick/lib/win32/jinput-dx8.dll differ diff --git a/ext/slick/lib/win32/jinput-raw.dll b/ext/slick/lib/win32/jinput-raw.dll new file mode 100644 index 0000000..2a014f6 Binary files /dev/null and b/ext/slick/lib/win32/jinput-raw.dll differ diff --git a/ext/slick/lib/win32/lwjgl.dll b/ext/slick/lib/win32/lwjgl.dll new file mode 100644 index 0000000..280e89a Binary files /dev/null and b/ext/slick/lib/win32/lwjgl.dll differ diff --git a/ext/slick/lwjgl.dll b/ext/slick/lwjgl.dll new file mode 100644 index 0000000..280e89a Binary files /dev/null and b/ext/slick/lwjgl.dll differ diff --git a/ext/slick/readme.txt b/ext/slick/readme.txt new file mode 100644 index 0000000..098d84d --- /dev/null +++ b/ext/slick/readme.txt @@ -0,0 +1,19 @@ +Slick - A 2D LWJGL Java Utility Library (out of date) +----------------------------------------------------------- + +This is a very early version. The tests work but no demo has been written yet. Yes the windows natives +are included in the main distribution in the right place. This is just meant to make it easier for quick +start windows users. All the natives required can be found in jars in the 'lib' directory. + + +Tools +~~~~~ + +AngelCode bitmap font tool - seems to be the best around at the moment and provided clean kerning + information. Available from http://www.angelcode.com/products/bmfont/. +TilED tile map tool - Java alternative to Mappy (which just seems borked to me). Available + from http://www.mapeditor.org. +ImagePacker - ImagePacker (http://homepage.ntlworld.com/config/imagepacker/index.htm) packs + single images into large images for efficent rendering and memory usage. +Pedigree - Particle Editor written as part of the Slick Project (Java) +Hiero - Bitmap Font tool with effects written as part fo the Slick Project (Java) \ No newline at end of file diff --git a/ext/slick/scripts/game-build-example.xml b/ext/slick/scripts/game-build-example.xml new file mode 100644 index 0000000..80ed24c --- /dev/null +++ b/ext/slick/scripts/game-build-example.xml @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <a href="${webstartURL}/@{name}.jnlp"> @{title} </a> + <br/> + + + + + \ No newline at end of file diff --git a/ext/slick/scripts/slickdemo.jnlp b/ext/slick/scripts/slickdemo.jnlp new file mode 100644 index 0000000..021acc2 --- /dev/null +++ b/ext/slick/scripts/slickdemo.jnlp @@ -0,0 +1,40 @@ + + + + @title@ + @vendor@ + + @title@ + @title@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ext/slick/scripts/slickexample.jnlp b/ext/slick/scripts/slickexample.jnlp new file mode 100644 index 0000000..68a3968 --- /dev/null +++ b/ext/slick/scripts/slickexample.jnlp @@ -0,0 +1,41 @@ + + + + @title@ + @vendor@ + + @title@ + @title@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ext/slick/scripts/slicktemplate.jnlp b/ext/slick/scripts/slicktemplate.jnlp new file mode 100644 index 0000000..3ae3cf5 --- /dev/null +++ b/ext/slick/scripts/slicktemplate.jnlp @@ -0,0 +1,41 @@ + + + + @title@ + @vendor@ + + @title@ + @title@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ext/slick/scripts/slicktemplatewithdata.jnlp b/ext/slick/scripts/slicktemplatewithdata.jnlp new file mode 100644 index 0000000..99c79b5 --- /dev/null +++ b/ext/slick/scripts/slicktemplatewithdata.jnlp @@ -0,0 +1,42 @@ + + + + @title@ + @vendor@ + + @title@ + @title@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ext/slick/scripts/slickwithextension.jnlp b/ext/slick/scripts/slickwithextension.jnlp new file mode 100644 index 0000000..8c4d259 --- /dev/null +++ b/ext/slick/scripts/slickwithextension.jnlp @@ -0,0 +1,20 @@ + + + + @title@ + @vendor@ + + @title@ + @title@ + + + + + + + + + diff --git a/ext/slick/src/org/newdawn/slick/AngelCodeFont.java b/ext/slick/src/org/newdawn/slick/AngelCodeFont.java new file mode 100644 index 0000000..2581918 --- /dev/null +++ b/ext/slick/src/org/newdawn/slick/AngelCodeFont.java @@ -0,0 +1,635 @@ +package org.newdawn.slick; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.Map.Entry; + +import org.newdawn.slick.opengl.renderer.Renderer; +import org.newdawn.slick.opengl.renderer.SGL; +import org.newdawn.slick.util.Log; +import org.newdawn.slick.util.ResourceLoader; + +/** + * A font implementation that will parse BMFont format font files. The font files can be output + * by Hiero, which is included with Slick, and also the AngelCode font tool available at: + * + * http://www.angelcode.com/products/bmfont/ + * + * This implementation copes with both the font display and kerning information + * allowing nicer looking paragraphs of text. Note that this utility only + * supports the text BMFont format definition file. + * + * @author kevin + * @author Nathan Sweet + */ +public class AngelCodeFont implements Font { + /** The renderer to use for all GL operations */ + private static SGL GL = Renderer.get(); + + /** + * The line cache size, this is how many lines we can render before starting + * to regenerate lists + */ + private static final int DISPLAY_LIST_CACHE_SIZE = 200; + + /** The highest character that AngelCodeFont will support. */ + private static final int MAX_CHAR = 255; + + /** True if this font should use display list caching */ + private boolean displayListCaching = true; + + /** The image containing the bitmap font */ + private Image fontImage; + /** The characters building up the font */ + private CharDef[] chars; + /** The height of a line */ + private int lineHeight; + /** The first display list ID */ + private int baseDisplayListID = -1; + /** The eldest display list ID */ + private int eldestDisplayListID; + /** The eldest display list */ + private DisplayList eldestDisplayList; + + /** The display list cache for rendered lines */ + private final LinkedHashMap displayLists = new LinkedHashMap(DISPLAY_LIST_CACHE_SIZE, 1, true) { + protected boolean removeEldestEntry(Entry eldest) { + eldestDisplayList = (DisplayList)eldest.getValue(); + eldestDisplayListID = eldestDisplayList.id; + + return false; + } + }; + + + /** + * Create a new font based on a font definition from AngelCode's tool and + * the font image generated from the tool. + * + * @param fntFile + * The location of the font defnition file + * @param image + * The image to use for the font + * @throws SlickException + * Indicates a failure to load either file + */ + public AngelCodeFont(String fntFile, Image image) throws SlickException { + fontImage = image; + + parseFnt(ResourceLoader.getResourceAsStream(fntFile)); + } + + /** + * Create a new font based on a font definition from AngelCode's tool and + * the font image generated from the tool. + * + * @param fntFile + * The location of the font defnition file + * @param imgFile + * The location of the font image + * @throws SlickException + * Indicates a failure to load either file + */ + public AngelCodeFont(String fntFile, String imgFile) throws SlickException { + fontImage = new Image(imgFile); + + parseFnt(ResourceLoader.getResourceAsStream(fntFile)); + } + + /** + * Create a new font based on a font definition from AngelCode's tool and + * the font image generated from the tool. + * + * @param fntFile + * The location of the font defnition file + * @param image + * The image to use for the font + * @param caching + * True if this font should use display list caching + * @throws SlickException + * Indicates a failure to load either file + */ + public AngelCodeFont(String fntFile, Image image, boolean caching) + throws SlickException { + fontImage = image; + displayListCaching = caching; + parseFnt(ResourceLoader.getResourceAsStream(fntFile)); + } + + /** + * Create a new font based on a font definition from AngelCode's tool and + * the font image generated from the tool. + * + * @param fntFile + * The location of the font defnition file + * @param imgFile + * The location of the font image + * @param caching + * True if this font should use display list caching + * @throws SlickException + * Indicates a failure to load either file + */ + public AngelCodeFont(String fntFile, String imgFile, boolean caching) + throws SlickException { + fontImage = new Image(imgFile); + displayListCaching = caching; + parseFnt(ResourceLoader.getResourceAsStream(fntFile)); + } + + /** + * Create a new font based on a font definition from AngelCode's tool and + * the font image generated from the tool. + * + * @param name + * The name to assign to the font image in the image store + * @param fntFile + * The stream of the font defnition file + * @param imgFile + * The stream of the font image + * @throws SlickException + * Indicates a failure to load either file + */ + public AngelCodeFont(String name, InputStream fntFile, InputStream imgFile) + throws SlickException { + fontImage = new Image(imgFile, name, false); + + parseFnt(fntFile); + } + + /** + * Create a new font based on a font definition from AngelCode's tool and + * the font image generated from the tool. + * + * @param name + * The name to assign to the font image in the image store + * @param fntFile + * The stream of the font defnition file + * @param imgFile + * The stream of the font image + * @param caching + * True if this font should use display list caching + * @throws SlickException + * Indicates a failure to load either file + */ + public AngelCodeFont(String name, InputStream fntFile, InputStream imgFile, + boolean caching) throws SlickException { + fontImage = new Image(imgFile, name, false); + + displayListCaching = caching; + parseFnt(fntFile); + } + + /** + * Parse the font definition file + * + * @param fntFile + * The stream from which the font file can be read + * @throws SlickException + */ + private void parseFnt(InputStream fntFile) throws SlickException { + if (displayListCaching) { + baseDisplayListID = GL.glGenLists(DISPLAY_LIST_CACHE_SIZE); + if (baseDisplayListID == 0) displayListCaching = false; + } + + try { + // now parse the font file + BufferedReader in = new BufferedReader(new InputStreamReader( + fntFile)); + String info = in.readLine(); + String common = in.readLine(); + String page = in.readLine(); + + Map kerning = new HashMap(64); + List charDefs = new ArrayList(MAX_CHAR); + int maxChar = 0; + boolean done = false; + while (!done) { + String line = in.readLine(); + if (line == null) { + done = true; + } else { + if (line.startsWith("chars c")) { + // ignore + } else if (line.startsWith("char")) { + CharDef def = parseChar(line); + if (def != null) { + maxChar = Math.max(maxChar, def.id); + charDefs.add(def); + } + } + if (line.startsWith("kernings c")) { + // ignore + } else if (line.startsWith("kerning")) { + StringTokenizer tokens = new StringTokenizer(line, " ="); + tokens.nextToken(); // kerning + tokens.nextToken(); // first + short first = Short.parseShort(tokens.nextToken()); // first value + tokens.nextToken(); // second + int second = Integer.parseInt(tokens.nextToken()); // second value + tokens.nextToken(); // offset + int offset = Integer.parseInt(tokens.nextToken()); // offset value + List values = (List)kerning.get(new Short(first)); + if (values == null) { + values = new ArrayList(); + kerning.put(new Short(first), values); + } + // Pack the character and kerning offset into a short. + values.add(new Short((short)((offset << 8) | second))); + } + } + } + + chars = new CharDef[maxChar + 1]; + for (Iterator iter = charDefs.iterator(); iter.hasNext();) { + CharDef def = (CharDef)iter.next(); + chars[def.id] = def; + } + + // Turn each list of kerning values into a short[] and set on the chardef. + for (Iterator iter = kerning.entrySet().iterator(); iter.hasNext(); ) { + Entry entry = (Entry)iter.next(); + short first = ((Short)entry.getKey()).shortValue(); + List valueList = (List)entry.getValue(); + short[] valueArray = new short[valueList.size()]; + int i = 0; + for (Iterator valueIter = valueList.iterator(); valueIter.hasNext(); i++) + valueArray[i] = ((Short)valueIter.next()).shortValue(); + chars[first].kerning = valueArray; + } + } catch (IOException e) { + Log.error(e); + throw new SlickException("Failed to parse font file: " + fntFile); + } + } + + /** + * Parse a single character line from the definition + * + * @param line + * The line to be parsed + * @return The character definition from the line + * @throws SlickException Indicates a given character is not valid in an angel code font + */ + private CharDef parseChar(String line) throws SlickException { + CharDef def = new CharDef(); + StringTokenizer tokens = new StringTokenizer(line, " ="); + + tokens.nextToken(); // char + tokens.nextToken(); // id + def.id = Short.parseShort(tokens.nextToken()); // id value + if (def.id < 0) { + return null; + } + if (def.id > MAX_CHAR) { + throw new SlickException("Invalid character '" + def.id + + "': AngelCodeFont does not support characters above " + MAX_CHAR); + } + + tokens.nextToken(); // x + def.x = Short.parseShort(tokens.nextToken()); // x value + tokens.nextToken(); // y + def.y = Short.parseShort(tokens.nextToken()); // y value + tokens.nextToken(); // width + def.width = Short.parseShort(tokens.nextToken()); // width value + tokens.nextToken(); // height + def.height = Short.parseShort(tokens.nextToken()); // height value + tokens.nextToken(); // x offset + def.xoffset = Short.parseShort(tokens.nextToken()); // xoffset value + tokens.nextToken(); // y offset + def.yoffset = Short.parseShort(tokens.nextToken()); // yoffset value + tokens.nextToken(); // xadvance + def.xadvance = Short.parseShort(tokens.nextToken()); // xadvance + + def.init(); + + if (def.id != ' ') { + lineHeight = Math.max(def.height + def.yoffset, lineHeight); + } + + return def; + } + + /** + * @see org.newdawn.slick.Font#drawString(float, float, java.lang.String) + */ + public void drawString(float x, float y, String text) { + drawString(x, y, text, Color.white); + } + + /** + * @see org.newdawn.slick.Font#drawString(float, float, java.lang.String, + * org.newdawn.slick.Color) + */ + public void drawString(float x, float y, String text, Color col) { + drawString(x, y, text, col, 0, text.length() - 1); + } + + /** + * @see Font#drawString(float, float, String, Color, int, int) + */ + public void drawString(float x, float y, String text, Color col, + int startIndex, int endIndex) { + fontImage.bind(); + col.bind(); + + GL.glTranslatef(x, y, 0); + if (displayListCaching && startIndex == 0 && endIndex == text.length() - 1) { + DisplayList displayList = (DisplayList)displayLists.get(text); + if (displayList != null) { + GL.glCallList(displayList.id); + } else { + // Compile a new display list. + displayList = new DisplayList(); + displayList.text = text; + int displayListCount = displayLists.size(); + if (displayListCount < DISPLAY_LIST_CACHE_SIZE) { + displayList.id = baseDisplayListID + displayListCount; + } else { + displayList.id = eldestDisplayListID; + displayLists.remove(eldestDisplayList.text); + } + + displayLists.put(text, displayList); + + GL.glNewList(displayList.id, SGL.GL_COMPILE_AND_EXECUTE); + render(text, startIndex, endIndex); + GL.glEndList(); + } + } else { + render(text, startIndex, endIndex); + } + GL.glTranslatef(-x, -y, 0); + } + + /** + * Render based on immediate rendering + * + * @param text The text to be rendered + * @param start The index of the first character in the string to render + * @param end The index of the last character in the string to render + */ + private void render(String text, int start, int end) { + GL.glBegin(SGL.GL_QUADS); + + int x = 0, y = 0; + CharDef lastCharDef = null; + char[] data = text.toCharArray(); + for (int i = 0; i < data.length; i++) { + int id = data[i]; + if (id == '\n') { + x = 0; + y += getLineHeight(); + continue; + } + if (id >= chars.length) { + continue; + } + CharDef charDef = chars[id]; + if (charDef == null) { + continue; + } + + if (lastCharDef != null) x += lastCharDef.getKerning(id); + lastCharDef = charDef; + + if ((i >= start) && (i <= end)) { + charDef.draw(x, y); + } + + x += charDef.xadvance; + } + GL.glEnd(); + } + + /** + * Returns the distance from the y drawing location to the top most pixel of the specified text. + * + * @param text + * The text that is to be tested + * @return The yoffset from the y draw location at which text will start + */ + public int getYOffset(String text) { + DisplayList displayList = null; + if (displayListCaching) { + displayList = (DisplayList)displayLists.get(text); + if (displayList != null && displayList.yOffset != null) return displayList.yOffset.intValue(); + } + + int stopIndex = text.indexOf('\n'); + if (stopIndex == -1) stopIndex = text.length(); + + int minYOffset = 10000; + for (int i = 0; i < stopIndex; i++) { + int id = text.charAt(i); + CharDef charDef = chars[id]; + if (charDef == null) { + continue; + } + minYOffset = Math.min(charDef.yoffset, minYOffset); + } + + if (displayList != null) displayList.yOffset = new Short((short)minYOffset); + + return minYOffset; + } + + /** + * @see org.newdawn.slick.Font#getHeight(java.lang.String) + */ + public int getHeight(String text) { + DisplayList displayList = null; + if (displayListCaching) { + displayList = (DisplayList)displayLists.get(text); + if (displayList != null && displayList.height != null) return displayList.height.intValue(); + } + + int lines = 0; + int maxHeight = 0; + for (int i = 0; i < text.length(); i++) { + int id = text.charAt(i); + if (id == '\n') { + lines++; + maxHeight = 0; + continue; + } + // ignore space, it doesn't contribute to height + if (id == ' ') { + continue; + } + CharDef charDef = chars[id]; + if (charDef == null) { + continue; + } + + maxHeight = Math.max(charDef.height + charDef.yoffset, + maxHeight); + } + + maxHeight += lines * getLineHeight(); + + if (displayList != null) displayList.height = new Short((short)maxHeight); + + return maxHeight; + } + + /** + * @see org.newdawn.slick.Font#getWidth(java.lang.String) + */ + public int getWidth(String text) { + DisplayList displayList = null; + if (displayListCaching) { + displayList = (DisplayList)displayLists.get(text); + if (displayList != null && displayList.width != null) return displayList.width.intValue(); + } + + int maxWidth = 0; + int width = 0; + CharDef lastCharDef = null; + for (int i = 0, n = text.length(); i < n; i++) { + int id = text.charAt(i); + if (id == '\n') { + width = 0; + continue; + } + if (id >= chars.length) { + continue; + } + CharDef charDef = chars[id]; + if (charDef == null) { + continue; + } + + if (lastCharDef != null) width += lastCharDef.getKerning(id); + lastCharDef = charDef; + + if (i < n - 1) { + width += charDef.xadvance; + } else { + width += charDef.width; + } + maxWidth = Math.max(maxWidth, width); + } + + if (displayList != null) displayList.width = new Short((short)maxWidth); + + return maxWidth; + } + + /** + * The definition of a single character as defined in the AngelCode file + * format + * + * @author kevin + */ + private class CharDef { + /** The id of the character */ + public short id; + /** The x location on the sprite sheet */ + public short x; + /** The y location on the sprite sheet */ + public short y; + /** The width of the character image */ + public short width; + /** The height of the character image */ + public short height; + /** The amount the x position should be offset when drawing the image */ + public short xoffset; + /** The amount the y position should be offset when drawing the image */ + public short yoffset; + + /** The amount to move the current position after drawing the character */ + public short xadvance; + /** The image containing the character */ + public Image image; + /** The display list index for this character */ + public short dlIndex; + /** The kerning info for this character */ + public short[] kerning; + + /** + * Initialise the image by cutting the right section from the map + * produced by the AngelCode tool. + */ + public void init() { + image = fontImage.getSubImage(x, y, width, height); + } + + /** + * @see java.lang.Object#toString() + */ + public String toString() { + return "[CharDef id=" + id + " x=" + x + " y=" + y + "]"; + } + + /** + * Draw this character embedded in a image draw + * + * @param x + * The x position at which to draw the text + * @param y + * The y position at which to draw the text + */ + public void draw(float x, float y) { + image.drawEmbedded(x + xoffset, y + yoffset, width, height); + } + + /** + * Get the kerning offset between this character and the specified character. + * @param otherCodePoint The other code point + * @return the kerning offset + */ + public int getKerning (int otherCodePoint) { + if (kerning == null) return 0; + int low = 0; + int high = kerning.length - 1; + while (low <= high) { + int midIndex = (low + high) >>> 1; + int value = kerning[midIndex]; + int foundCodePoint = value & 0xff; + if (foundCodePoint < otherCodePoint) + low = midIndex + 1; + else if (foundCodePoint > otherCodePoint) + high = midIndex - 1; + else + return value >> 8; + } + return 0; + } + } + + /** + * @see org.newdawn.slick.Font#getLineHeight() + */ + public int getLineHeight() { + return lineHeight; + } + + /** + * A descriptor for a single display list + * + * @author Nathan Sweet + */ + static private class DisplayList { + /** The if of the distance list */ + int id; + /** The offset of the line rendered */ + Short yOffset; + /** The width of the line rendered */ + Short width; + /** The height of the line rendered */ + Short height; + /** The text that the display list holds */ + String text; + } +} diff --git a/ext/slick/src/org/newdawn/slick/Animation.java b/ext/slick/src/org/newdawn/slick/Animation.java new file mode 100644 index 0000000..d976d5e --- /dev/null +++ b/ext/slick/src/org/newdawn/slick/Animation.java @@ -0,0 +1,720 @@ +package org.newdawn.slick; + +import java.util.ArrayList; + +import org.lwjgl.Sys; +import org.newdawn.slick.util.Log; + +/** + * A utility to hold and render animations + * + * @author kevin + * @author DeX (speed updates) + */ +public class Animation implements Renderable { + /** The list of frames to render in this animation */ + private ArrayList frames = new ArrayList(); + /** The frame currently being displayed */ + private int currentFrame = -1; + /** The time the next frame change should take place */ + private long nextChange = 0; + /** True if the animation is stopped */ + private boolean stopped = false; + /** The time left til the next frame */ + private long timeLeft; + /** The current speed of the animation */ + private float speed = 1.0f; + /** The frame to stop at */ + private int stopAt = -2; + /** The last time the frame was automagically updated */ + private long lastUpdate; + /** True if this is the first update */ + private boolean firstUpdate = true; + /** True if we should auto update the animation - default true */ + private boolean autoUpdate = true; + /** The direction the animation is running */ + private int direction = 1; + /** True if the animation in ping ponging back and forth */ + private boolean pingPong; + /** True if the animation should loop (default) */ + private boolean loop = true; + /** The spriteSheet backing this animation */ + private SpriteSheet spriteSheet = null; + + /** + * Create an empty animation + */ + public Animation() { + this(true); + } + + /** + * Create a new animation from a set of images + * + * @param frames The images for the animation frames + * @param duration The duration to show each frame + */ + public Animation(Image[] frames, int duration) { + this(frames, duration, true); + } + + /** + * Create a new animation from a set of images + * + * @param frames The images for the animation frames + * @param durations The duration to show each frame + */ + public Animation(Image[] frames, int[] durations) { + this(frames, durations, true); + } + + /** + * Create an empty animation + * + * @param autoUpdate True if this animation should automatically update. This means that the + * current frame will be caculated based on the time between renders + */ + public Animation(boolean autoUpdate) { + currentFrame = 0; + this.autoUpdate = autoUpdate; + } + + /** + * Create a new animation from a set of images + * + * @param frames The images for the animation frames + * @param duration The duration to show each frame + * @param autoUpdate True if this animation should automatically update. This means that the + * current frame will be caculated based on the time between renders + */ + public Animation(Image[] frames, int duration, boolean autoUpdate) { + for (int i=0;iSpriteSheet ss. + * @param ss The SpriteSheet backing this animation + * @param frames An array of coordinates of sub-image locations for each frame + * @param duration The duration each frame should be displayed for + */ + public Animation(SpriteSheet ss, int[] frames, int[] duration){ + spriteSheet = ss; + int x = -1; + int y = -1; + + for(int i = 0; i < frames.length/2; i++){ + x = frames[i*2]; + y = frames[i*2 + 1]; + addFrame(duration[i], x, y); + } + } + + /** + * Add animation frame to the animation. + * @param duration The duration to display the frame for + * @param x The x location of the frame on the SpriteSheet + * @param y The y location of the frame on the spriteSheet + */ + public void addFrame(int duration, int x, int y){ + if (duration == 0) { + Log.error("Invalid duration: "+duration); + throw new RuntimeException("Invalid duration: "+duration); + } + + if (frames.isEmpty()) { + nextChange = (int) (duration / speed); + } + + frames.add(new Frame(duration, x, y)); + currentFrame = 0; + } + + /** + * Indicate if this animation should automatically update based on the + * time between renders or if it should need updating via the update() + * method. + * + * @param auto True if this animation should automatically update + */ + public void setAutoUpdate(boolean auto) { + this.autoUpdate = auto; + } + + /** + * Indicate if this animation should ping pong back and forth + * + * @param pingPong True if the animation should ping pong + */ + public void setPingPong(boolean pingPong) { + this.pingPong = pingPong; + } + + /** + * Check if this animation has stopped (either explictly or because it's reached its target frame) + * + * @see #stopAt + * @return True if the animation has stopped + */ + public boolean isStopped() { + return stopped; + } + + /** + * Adjust the overall speed of the animation. + * + * @param spd The speed to run the animation. Default: 1.0 + */ + public void setSpeed(float spd) { + if (spd > 0) { + // Adjust nextChange + nextChange = (long) (nextChange * speed / spd); + + speed = spd; + } + } + + /** + * Returns the current speed of the animation. + * + * @return The speed this animation is being played back at + */ + public float getSpeed() { + return speed; + } + + + /** + * Stop the animation + */ + public void stop() { + if (frames.size() == 0) { + return; + } + timeLeft = nextChange; + stopped = true; + } + + /** + * Start the animation playing again + */ + public void start() { + if (!stopped) { + return; + } + if (frames.size() == 0) { + return; + } + stopped = false; + nextChange = timeLeft; + } + + /** + * Restart the animation from the beginning + */ + public void restart() { + if (frames.size() == 0) { + return; + } + stopped = false; + currentFrame = 0; + nextChange = (int) (((Frame) frames.get(0)).duration / speed); + firstUpdate = true; + lastUpdate = 0; + } + + /** + * Add animation frame to the animation + * + * @param frame The image to display for the frame + * @param duration The duration to display the frame for + */ + public void addFrame(Image frame, int duration) { + if (duration == 0) { + Log.error("Invalid duration: "+duration); + throw new RuntimeException("Invalid duration: "+duration); + } + + if (frames.isEmpty()) { + nextChange = (int) (duration / speed); + } + + frames.add(new Frame(frame, duration)); + currentFrame = 0; + } + + /** + * Draw the animation to the screen + */ + public void draw() { + draw(0,0); + } + + /** + * Draw the animation at a specific location + * + * @param x The x position to draw the animation at + * @param y The y position to draw the animation at + */ + public void draw(float x,float y) { + draw(x,y,getWidth(),getHeight()); + } + + /** + * Draw the animation at a specific location + * + * @param x The x position to draw the animation at + * @param y The y position to draw the animation at + * @param filter The filter to apply + */ + public void draw(float x,float y, Color filter) { + draw(x,y,getWidth(),getHeight(), filter); + } + + /** + * Draw the animation + * + * @param x The x position to draw the animation at + * @param y The y position to draw the animation at + * @param width The width to draw the animation at + * @param height The height to draw the animation at + */ + public void draw(float x,float y,float width,float height) { + draw(x,y,width,height,Color.white); + } + + /** + * Draw the animation + * + * @param x The x position to draw the animation at + * @param y The y position to draw the animation at + * @param width The width to draw the animation at + * @param height The height to draw the animation at + * @param col The colour filter to use + */ + public void draw(float x,float y,float width,float height, Color col) { + if (frames.size() == 0) { + return; + } + + if (autoUpdate) { + long now = getTime(); + long delta = now - lastUpdate; + if (firstUpdate) { + delta = 0; + firstUpdate = false; + } + lastUpdate = now; + nextFrame(delta); + } + + Frame frame = (Frame) frames.get(currentFrame); + frame.image.draw(x,y,width,height, col); + } + + /** + * Render the appropriate frame when the spriteSheet backing this Animation is in use. + * @param x The x position to draw the animation at + * @param y The y position to draw the animation at + */ + public void renderInUse(int x, int y){ + if (frames.size() == 0) { + return; + } + + if (autoUpdate) { + long now = getTime(); + long delta = now - lastUpdate; + if (firstUpdate) { + delta = 0; + firstUpdate = false; + } + lastUpdate = now; + nextFrame(delta); + } + + Frame frame = (Frame) frames.get(currentFrame); + spriteSheet.renderInUse(x, y, frame.x, frame.y); + } + + /** + * Get the width of the current frame + * + * @return The width of the current frame + */ + public int getWidth() { + return ((Frame) frames.get(currentFrame)).image.getWidth(); + } + + /** + * Get the height of the current frame + * + * @return The height of the current frame + */ + public int getHeight() { + return ((Frame) frames.get(currentFrame)).image.getHeight(); + } + + /** + * Draw the animation + * + * @param x The x position to draw the animation at + * @param y The y position to draw the animation at + * @param width The width to draw the animation at + * @param height The height to draw the animation at + */ + public void drawFlash(float x,float y,float width,float height) { + drawFlash(x,y,width,height, Color.white); + } + + /** + * Draw the animation + * + * @param x The x position to draw the animation at + * @param y The y position to draw the animation at + * @param width The width to draw the animation at + * @param height The height to draw the animation at + * @param col The colour for the flash + */ + public void drawFlash(float x,float y,float width,float height, Color col) { + if (frames.size() == 0) { + return; + } + + if (autoUpdate) { + long now = getTime(); + long delta = now - lastUpdate; + if (firstUpdate) { + delta = 0; + firstUpdate = false; + } + lastUpdate = now; + nextFrame(delta); + } + + Frame frame = (Frame) frames.get(currentFrame); + frame.image.drawFlash(x,y,width,height,col); + } + + /** + * Update the animation cycle without draw the image, useful + * for keeping two animations in sync + * + * @deprecated + */ + public void updateNoDraw() { + if (autoUpdate) { + long now = getTime(); + long delta = now - lastUpdate; + if (firstUpdate) { + delta = 0; + firstUpdate = false; + } + lastUpdate = now; + nextFrame(delta); + } + } + + /** + * Update the animation, note that this will have odd effects if auto update + * is also turned on + * + * @see #autoUpdate + * @param delta The amount of time thats passed since last update + */ + public void update(long delta) { + nextFrame(delta); + } + + /** + * Get the index of the current frame + * + * @return The index of the current frame + */ + public int getFrame() { + return currentFrame; + } + + /** + * Set the current frame to be rendered + * + * @param index The index of the frame to rendered + */ + public void setCurrentFrame(int index) { + currentFrame = index; + } + + /** + * Get the image assocaited with a given frame index + * + * @param index The index of the frame image to retrieve + * @return The image of the specified animation frame + */ + public Image getImage(int index) { + Frame frame = (Frame) frames.get(index); + return frame.image; + } + + /** + * Get the number of frames that are in the animation + * + * @return The number of frames that are in the animation + */ + public int getFrameCount() { + return frames.size(); + } + + /** + * Get the image associated with the current animation frame + * + * @return The image associated with the current animation frame + */ + public Image getCurrentFrame() { + Frame frame = (Frame) frames.get(currentFrame); + return frame.image; + } + + /** + * Check if we need to move to the next frame + * + * @param delta The amount of time thats passed since last update + */ + private void nextFrame(long delta) { + if (stopped) { + return; + } + if (frames.size() == 0) { + return; + } + + nextChange -= delta; + + while (nextChange < 0 && (!stopped)) { + if (currentFrame == stopAt) { + stopped = true; + break; + } + if ((currentFrame == frames.size() - 1) && (!loop)) { + stopped = true; + break; + } + currentFrame = (currentFrame + direction) % frames.size(); + + if (pingPong) { + if (currentFrame <= 0) { + currentFrame = 0; + direction = 1; + } + if (currentFrame >= frames.size()-1) { + currentFrame = frames.size()-1; + direction = -1; + } + } + int realDuration = (int) (((Frame) frames.get(currentFrame)).duration / speed); + nextChange = nextChange + realDuration; + } + } + + /** + * Indicate if this animation should loop or stop at the last frame + * + * @param loop True if this animation should loop (true = default) + */ + public void setLooping(boolean loop) { + this.loop = loop; + } + + /** + * Get the accurate system time + * + * @return The system time in milliseconds + */ + private long getTime() { + return (Sys.getTime() * 1000) / Sys.getTimerResolution(); + } + + /** + * Indicate the animation should stop when it reaches the specified + * frame index (note, not frame number but index in the animation + * + * @param frameIndex The index of the frame to stop at + */ + public void stopAt(int frameIndex) { + stopAt = frameIndex; + } + + /** + * Get the duration of a particular frame + * + * @param index The index of the given frame + * @return The duration in (ms) of the given frame + */ + public int getDuration(int index) { + return ((Frame) frames.get(index)).duration; + } + + /** + * Set the duration of the given frame + * + * @param index The index of the given frame + * @param duration The duration in (ms) for the given frame + */ + public void setDuration(int index, int duration) { + ((Frame) frames.get(index)).duration = duration; + } + + /** + * Get the durations of all the frames in this animation + * + * @return The durations of all the frames in this animation + */ + public int[] getDurations() { + int[] durations = new int[frames.size()]; + for (int i=0;iSpriteSheet + * @param y the y location of the frame on the SpriteSheet + */ + public Frame(int duration, int x, int y) { + this.image = spriteSheet.getSubImage(x, y); + this.duration = duration; + this.x = x; + this.y = y; + } + } +} diff --git a/ext/slick/src/org/newdawn/slick/AppGameContainer.java b/ext/slick/src/org/newdawn/slick/AppGameContainer.java new file mode 100644 index 0000000..27d8b04 --- /dev/null +++ b/ext/slick/src/org/newdawn/slick/AppGameContainer.java @@ -0,0 +1,544 @@ +package org.newdawn.slick; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.security.AccessController; +import java.security.PrivilegedAction; + +import org.lwjgl.BufferUtils; +import org.lwjgl.LWJGLException; +import org.lwjgl.Sys; +import org.lwjgl.input.Cursor; +import org.lwjgl.input.Mouse; +import org.lwjgl.openal.AL; +import org.lwjgl.opengl.Display; +import org.lwjgl.opengl.DisplayMode; +import org.lwjgl.opengl.PixelFormat; +import org.newdawn.slick.openal.SoundStore; +import org.newdawn.slick.opengl.CursorLoader; +import org.newdawn.slick.opengl.ImageData; +import org.newdawn.slick.opengl.ImageIOImageData; +import org.newdawn.slick.opengl.InternalTextureLoader; +import org.newdawn.slick.opengl.LoadableImageData; +import org.newdawn.slick.opengl.TGAImageData; +import org.newdawn.slick.util.Log; +import org.newdawn.slick.util.ResourceLoader; + +/** + * A game container that will display the game as an stand alone + * application. + * + * @author kevin + */ +public class AppGameContainer extends GameContainer { + static { + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + try { + Display.getDisplayMode(); + } catch (Exception e) { + Log.error(e); + } + return null; + }}); + } + + /** The original display mode before we tampered with things */ + protected DisplayMode originalDisplayMode; + /** The display mode we're going to try and use */ + protected DisplayMode targetDisplayMode; + /** True if we should update the game only when the display is visible */ + protected boolean updateOnlyOnVisible = true; + /** Alpha background supported */ + protected boolean alphaSupport = false; + + /** + * Create a new container wrapping a game + * + * @param game The game to be wrapped + * @throws SlickException Indicates a failure to initialise the display + */ + public AppGameContainer(Game game) throws SlickException { + this(game,640,480,false); + } + + /** + * Create a new container wrapping a game + * + * @param game The game to be wrapped + * @param width The width of the display required + * @param height The height of the display required + * @param fullscreen True if we want fullscreen mode + * @throws SlickException Indicates a failure to initialise the display + */ + public AppGameContainer(Game game,int width,int height,boolean fullscreen) throws SlickException { + super(game); + + originalDisplayMode = Display.getDisplayMode(); + + setDisplayMode(width,height,fullscreen); + } + + /** + * Check if the display created supported alpha in the back buffer + * + * @return True if the back buffer supported alpha + */ + public boolean supportsAlphaInBackBuffer() { + return alphaSupport; + } + + /** + * Set the title of the window + * + * @param title The title to set on the window + */ + public void setTitle(String title) { + Display.setTitle(title); + } + + /** + * Set the display mode to be used + * + * @param width The width of the display required + * @param height The height of the display required + * @param fullscreen True if we want fullscreen mode + * @throws SlickException Indicates a failure to initialise the display + */ + public void setDisplayMode(int width, int height, boolean fullscreen) throws SlickException { + if ((this.width == width) && (this.height == height) && (isFullscreen() == fullscreen)) { + return; + } + + try { + targetDisplayMode = null; + if (fullscreen) { + DisplayMode[] modes = Display.getAvailableDisplayModes(); + int freq = 0; + + for (int i=0;i= freq)) { + if ((targetDisplayMode == null) || (current.getBitsPerPixel() > targetDisplayMode.getBitsPerPixel())) { + targetDisplayMode = current; + freq = targetDisplayMode.getFrequency(); + } + } + + // if we've found a match for bpp and frequence against the + // original display mode then it's probably best to go for this one + // since it's most likely compatible with the monitor + if ((current.getBitsPerPixel() == originalDisplayMode.getBitsPerPixel()) && + (current.getFrequency() == originalDisplayMode.getFrequency())) { + targetDisplayMode = current; + break; + } + } + } + } else { + targetDisplayMode = new DisplayMode(width,height); + } + + if (targetDisplayMode == null) { + throw new SlickException("Failed to find value mode: "+width+"x"+height+" fs="+fullscreen); + } + + this.width = width; + this.height = height; + + Display.setDisplayMode(targetDisplayMode); + Display.setFullscreen(fullscreen); + + if (Display.isCreated()) { + initGL(); + enterOrtho(); + } + + if (targetDisplayMode.getBitsPerPixel() == 16) { + InternalTextureLoader.get().set16BitMode(); + } + } catch (LWJGLException e) { + throw new SlickException("Unable to setup mode "+width+"x"+height+" fullscreen="+fullscreen, e); + } + + getDelta(); + } + + /** + * Check if the display is in fullscreen mode + * + * @return True if the display is in fullscreen mode + */ + public boolean isFullscreen() { + return Display.isFullscreen(); + } + + /** + * Indicate whether we want to be in fullscreen mode. Note that the current + * display mode must be valid as a fullscreen mode for this to work + * + * @param fullscreen True if we want to be in fullscreen mode + * @throws SlickException Indicates we failed to change the display mode + */ + public void setFullscreen(boolean fullscreen) throws SlickException { + if (isFullscreen() == fullscreen) { + return; + } + + if (!fullscreen) { + try { + Display.setFullscreen(fullscreen); + } catch (LWJGLException e) { + throw new SlickException("Unable to set fullscreen="+fullscreen, e); + } + } else { + setDisplayMode(width, height, fullscreen); + } + getDelta(); + } + + /** + * @see org.newdawn.slick.GameContainer#setMouseCursor(java.lang.String, int, int) + */ + public void setMouseCursor(String ref, int hotSpotX, int hotSpotY) throws SlickException { + try { + Cursor cursor = CursorLoader.get().getCursor(ref, hotSpotX, hotSpotY); + Mouse.setNativeCursor(cursor); + } catch (Exception e) { + Log.error("Failed to load and apply cursor.", e); + } + } + + /** + * @see org.newdawn.slick.GameContainer#setMouseCursor(org.newdawn.slick.opengl.ImageData, int, int) + */ + public void setMouseCursor(ImageData data, int hotSpotX, int hotSpotY) throws SlickException { + try { + Cursor cursor = CursorLoader.get().getCursor(data, hotSpotX, hotSpotY); + Mouse.setNativeCursor(cursor); + } catch (Exception e) { + Log.error("Failed to load and apply cursor.", e); + } + } + + /** + * @see org.newdawn.slick.GameContainer#setMouseCursor(org.lwjgl.input.Cursor, int, int) + */ + public void setMouseCursor(Cursor cursor, int hotSpotX, int hotSpotY) throws SlickException { + try { + Mouse.setNativeCursor(cursor); + } catch (Exception e) { + Log.error("Failed to load and apply cursor.", e); + } + } + + /** + * Get the closest greater power of 2 to the fold number + * + * @param fold The target number + * @return The power of 2 + */ + private int get2Fold(int fold) { + int ret = 2; + while (ret < fold) { + ret *= 2; + } + return ret; + } + + /** + * @see org.newdawn.slick.GameContainer#setMouseCursor(org.newdawn.slick.Image, int, int) + */ + public void setMouseCursor(Image image, int hotSpotX, int hotSpotY) throws SlickException { + try { + Image temp = new Image(get2Fold(image.getWidth()), get2Fold(image.getHeight())); + Graphics g = temp.getGraphics(); + + ByteBuffer buffer = BufferUtils.createByteBuffer(temp.getWidth() * temp.getHeight() * 4); + g.drawImage(image.getFlippedCopy(false, true), 0, 0); + g.flush(); + g.getArea(0,0,temp.getWidth(),temp.getHeight(),buffer); + + Cursor cursor = CursorLoader.get().getCursor(buffer, hotSpotX, hotSpotY,temp.getWidth(),image.getHeight()); + Mouse.setNativeCursor(cursor); + } catch (Exception e) { + Log.error("Failed to load and apply cursor.", e); + } + } + + /** + * @see org.newdawn.slick.GameContainer#reinit() + */ + public void reinit() throws SlickException { + InternalTextureLoader.get().clear(); + SoundStore.get().clear(); + initSystem(); + enterOrtho(); + + try { + game.init(this); + } catch (SlickException e) { + Log.error(e); + running = false; + } + } + + /** + * Try creating a display with the given format + * + * @param format The format to attempt + * @throws LWJGLException Indicates a failure to support the given format + */ + private void tryCreateDisplay(PixelFormat format) throws LWJGLException { + + if (SHARED_DRAWABLE == null) + { + Display.create(format); + } + else + { + Display.create(format, SHARED_DRAWABLE); + } + } + + /** + * Start running the game + * + * @throws SlickException Indicates a failure to initialise the system + */ + public void start() throws SlickException { + try { + setup(); + + getDelta(); + while (running()) { + gameLoop(); + } + } finally { + destroy(); + } + + if (forceExit) { + System.exit(0); + } + } + + /** + * Setup the environment + * + * @throws SlickException Indicates a failure + */ + protected void setup() throws SlickException { + if (targetDisplayMode == null) { + setDisplayMode(640,480,false); + } + + Display.setTitle(game.getTitle()); + + Log.info("LWJGL Version: "+Sys.getVersion()); + Log.info("OriginalDisplayMode: "+originalDisplayMode); + Log.info("TargetDisplayMode: "+targetDisplayMode); + + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + try { + PixelFormat format = new PixelFormat(8,8,0,samples); + + tryCreateDisplay(format); + supportsMultiSample = true; + } catch (Exception e) { + Display.destroy(); + + try { + PixelFormat format = new PixelFormat(8,8,0); + + tryCreateDisplay(format); + alphaSupport = false; + } catch (Exception e2) { + Display.destroy(); + // if we couldn't get alpha, let us know + try { + tryCreateDisplay(new PixelFormat()); + } catch (Exception e3) { + Log.error(e3); + } + } + } + + return null; + }}); + + if (!Display.isCreated()) { + throw new SlickException("Failed to initialise the LWJGL display"); + } + + initSystem(); + enterOrtho(); + + try { + getInput().initControllers(); + } catch (SlickException e) { + Log.info("Controllers not available"); + } catch (Throwable e) { + Log.info("Controllers not available"); + } + + try { + game.init(this); + } catch (SlickException e) { + Log.error(e); + running = false; + } + } + + /** + * Strategy for overloading game loop context handling + * + * @throws SlickException Indicates a game failure + */ + protected void gameLoop() throws SlickException { + int delta = getDelta(); + if (!Display.isVisible() && updateOnlyOnVisible) { + try { Thread.sleep(100); } catch (Exception e) {} + } else { + try { + updateAndRender(delta); + } catch (SlickException e) { + Log.error(e); + running = false; + return; + } + } + + updateFPS(); + + Display.update(); + + if (Display.isCloseRequested()) { + if (game.closeRequested()) { + running = false; + } + } + } + + /** + * @see org.newdawn.slick.GameContainer#setUpdateOnlyWhenVisible(boolean) + */ + public void setUpdateOnlyWhenVisible(boolean updateOnlyWhenVisible) { + updateOnlyOnVisible = updateOnlyWhenVisible; + } + + /** + * @see org.newdawn.slick.GameContainer#isUpdatingOnlyWhenVisible() + */ + public boolean isUpdatingOnlyWhenVisible() { + return updateOnlyOnVisible; + } + + /** + * @see org.newdawn.slick.GameContainer#setIcon(java.lang.String) + */ + public void setIcon(String ref) throws SlickException { + setIcons(new String[] {ref}); + } + + /** + * @see org.newdawn.slick.GameContainer#setMouseGrabbed(boolean) + */ + public void setMouseGrabbed(boolean grabbed) { + Mouse.setGrabbed(grabbed); + } + + /** + * @see org.newdawn.slick.GameContainer#isMouseGrabbed() + */ + public boolean isMouseGrabbed() { + return Mouse.isGrabbed(); + } + + /** + * @see org.newdawn.slick.GameContainer#hasFocus() + */ + public boolean hasFocus() { + // hmm, not really the right thing, talk to the LWJGL guys + return Display.isActive(); + } + + /** + * @see org.newdawn.slick.GameContainer#getScreenHeight() + */ + public int getScreenHeight() { + return originalDisplayMode.getHeight(); + } + + /** + * @see org.newdawn.slick.GameContainer#getScreenWidth() + */ + public int getScreenWidth() { + return originalDisplayMode.getWidth(); + } + + /** + * Destroy the app game container + */ + public void destroy() { + Display.destroy(); + AL.destroy(); + } + + /** + * A null stream to clear out those horrid errors + * + * @author kevin + */ + private class NullOutputStream extends OutputStream { + /** + * @see java.io.OutputStream#write(int) + */ + public void write(int b) throws IOException { + // null implemetnation + } + + } + + /** + * @see org.newdawn.slick.GameContainer#setIcons(java.lang.String[]) + */ + public void setIcons(String[] refs) throws SlickException { + ByteBuffer[] bufs = new ByteBuffer[refs.length]; + for (int i=0;i= screenAspectRatio) { + newWidth = screenWidth; + newHeight = (int) (height / ((float) width / screenWidth)); + } else { + newWidth = (int) (width / ((float) height / screenHeight)); + newHeight = screenHeight; + } + + // center new screen + int xoffset = (screenWidth - newWidth) / 2; + int yoffset = (screenHeight - newHeight) / 2; + + // scale game to match new resolution + GL11.glViewport(xoffset, yoffset, newWidth, newHeight); + + enterOrtho(); + + // fix input to match new resolution + this.getInput().setOffset( + -xoffset * (float) width / newWidth, + -yoffset * (float) height / newHeight); + + this.getInput().setScale((float) width / newWidth, + (float) height / newHeight); + + width = screenWidth; + height = screenHeight; + Display.setFullscreen(true); + } else { + // restore input + this.getInput().setOffset(0, 0); + this.getInput().setScale(1, 1); + width = AppletGameContainer.this.getWidth(); + height = AppletGameContainer.this.getHeight(); + GL11.glViewport(0, 0, width, height); + + enterOrtho(); + + Display.setFullscreen(false); + } + } catch (LWJGLException e) { + Log.error(e); + } + + } + + /** + * The running game loop + * + * @throws Exception Indicates a failure within the game's loop rather than the framework + */ + public void runloop() throws Exception { + while (running) { + int delta = getDelta(); + + updateAndRender(delta); + + updateFPS(); + Display.update(); + } + + Display.destroy(); + } + } + + /** + * A basic console to display an error message if the applet crashes. + * This will prevent the applet from just freezing in the browser + * and give the end user an a nice gui where the error message can easily + * be viewed and copied. + */ + public class ConsolePanel extends Panel { + /** The area display the console output */ + TextArea textArea = new TextArea(); + + /** + * Create a new panel to display the console output + * + * @param e The exception causing the console to be displayed + */ + public ConsolePanel(Exception e) { + setLayout(new BorderLayout()); + setBackground(Color.black); + setForeground(Color.white); + + Font consoleFont = new Font("Arial", Font.BOLD, 14); + + Label slickLabel = new Label("SLICK CONSOLE", Label.CENTER); + slickLabel.setFont(consoleFont); + add(slickLabel, BorderLayout.PAGE_START); + + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw)); + + textArea.setText(sw.toString()); + textArea.setEditable(false); + add(textArea, BorderLayout.CENTER); + + // add a border on both sides of the console + add(new Panel(), BorderLayout.LINE_START); + add(new Panel(), BorderLayout.LINE_END); + + Panel bottomPanel = new Panel(); + bottomPanel.setLayout(new GridLayout(0, 1)); + Label infoLabel1 = new Label("An error occured while running the applet.", Label.CENTER); + Label infoLabel2 = new Label("Plese contact support to resolve this issue.", Label.CENTER); + infoLabel1.setFont(consoleFont); + infoLabel2.setFont(consoleFont); + bottomPanel.add(infoLabel1); + bottomPanel.add(infoLabel2); + add(bottomPanel, BorderLayout.PAGE_END); + } + } +} \ No newline at end of file diff --git a/ext/slick/src/org/newdawn/slick/BasicGame.java b/ext/slick/src/org/newdawn/slick/BasicGame.java new file mode 100644 index 0000000..34fb1fc --- /dev/null +++ b/ext/slick/src/org/newdawn/slick/BasicGame.java @@ -0,0 +1,205 @@ +package org.newdawn.slick; + + +/** + * A basic implementation of a game to take out the boring bits + * + * @author kevin + */ +public abstract class BasicGame implements Game, InputListener { + /** The maximum number of controllers supported by the basic game */ + private static final int MAX_CONTROLLERS = 20; + /** The maximum number of controller buttons supported by the basic game */ + private static final int MAX_CONTROLLER_BUTTONS = 100; + /** The title of the game */ + private String title; + /** The state of the left control */ + protected boolean[] controllerLeft = new boolean[MAX_CONTROLLERS]; + /** The state of the right control */ + protected boolean[] controllerRight = new boolean[MAX_CONTROLLERS]; + /** The state of the up control */ + protected boolean[] controllerUp = new boolean[MAX_CONTROLLERS]; + /** The state of the down control */ + protected boolean[] controllerDown = new boolean[MAX_CONTROLLERS]; + /** The state of the button controlls */ + protected boolean[][] controllerButton = new boolean[MAX_CONTROLLERS][MAX_CONTROLLER_BUTTONS]; + + /** + * Create a new basic game + * + * @param title The title for the game + */ + public BasicGame(String title) { + this.title = title; + } + + /** + * @see org.newdawn.slick.InputListener#setInput(org.newdawn.slick.Input) + */ + public void setInput(Input input) { + } + + /** + * @see org.newdawn.slick.Game#closeRequested() + */ + public boolean closeRequested() { + return true; + } + + /** + * @see org.newdawn.slick.Game#getTitle() + */ + public String getTitle() { + return title; + } + + /** + * @see org.newdawn.slick.Game#init(org.newdawn.slick.GameContainer) + */ + public abstract void init(GameContainer container) throws SlickException; + + /** + * @see org.newdawn.slick.InputListener#keyPressed(int, char) + */ + public void keyPressed(int key, char c) { + } + + /** + * @see org.newdawn.slick.InputListener#keyReleased(int, char) + */ + public void keyReleased(int key, char c) { + } + + /** + * @see org.newdawn.slick.InputListener#mouseMoved(int, int, int, int) + */ + public void mouseMoved(int oldx, int oldy, int newx, int newy) { + } + + /** + * @see org.newdawn.slick.InputListener#mouseDragged(int, int, int, int) + */ + public void mouseDragged(int oldx, int oldy, int newx, int newy) { + } + + /** + * @see org.newdawn.slick.InputListener#mouseClicked(int, int, int, int) + */ + public void mouseClicked(int button, int x, int y, int clickCount) { + } + + /** + * @see org.newdawn.slick.InputListener#mousePressed(int, int, int) + */ + public void mousePressed(int button, int x, int y) { + + } + + /** + * @see org.newdawn.slick.InputListener#controllerButtonPressed(int, int) + */ + public void controllerButtonPressed(int controller, int button) { + controllerButton[controller][button] = true; + } + + /** + * @see org.newdawn.slick.InputListener#controllerButtonReleased(int, int) + */ + public void controllerButtonReleased(int controller, int button) { + controllerButton[controller][button] = false; + } + + /** + * @see org.newdawn.slick.InputListener#controllerDownPressed(int) + */ + public void controllerDownPressed(int controller) { + controllerDown[controller] = true; + } + + /** + * @see org.newdawn.slick.InputListener#controllerDownReleased(int) + */ + public void controllerDownReleased(int controller) { + controllerDown[controller] = false; + } + + /** + * @see org.newdawn.slick.InputListener#controllerLeftPressed(int) + */ + public void controllerLeftPressed(int controller) { + controllerLeft[controller] = true; + } + + /** + * @see org.newdawn.slick.InputListener#controllerLeftReleased(int) + */ + public void controllerLeftReleased(int controller) { + controllerLeft[controller] = false; + } + + /** + * @see org.newdawn.slick.InputListener#controllerRightPressed(int) + */ + public void controllerRightPressed(int controller) { + controllerRight[controller] = true; + } + + /** + * @see org.newdawn.slick.InputListener#controllerRightReleased(int) + */ + public void controllerRightReleased(int controller) { + controllerRight[controller] = false; + } + + /** + * @see org.newdawn.slick.InputListener#controllerUpPressed(int) + */ + public void controllerUpPressed(int controller) { + controllerUp[controller] = true; + } + + /** + * @see org.newdawn.slick.InputListener#controllerUpReleased(int) + */ + public void controllerUpReleased(int controller) { + controllerUp[controller] = false; + } + + /** + * @see org.newdawn.slick.InputListener#mouseReleased(int, int, int) + */ + public void mouseReleased(int button, int x, int y) { + } + + /** + * @see org.newdawn.slick.Game#update(org.newdawn.slick.GameContainer, int) + */ + public abstract void update(GameContainer container, int delta) throws SlickException; + + /** + * @see org.newdawn.slick.InputListener#mouseWheelMoved(int) + */ + public void mouseWheelMoved(int change) { + } + + /** + * @see org.newdawn.slick.InputListener#isAcceptingInput() + */ + public boolean isAcceptingInput() { + return true; + } + + /** + * @see org.newdawn.slick.InputListener#inputEnded() + */ + public void inputEnded() { + + } + + /** + * @see org.newdawn.slick.ControlledInputReciever#inputStarted() + */ + public void inputStarted() { + + } +} diff --git a/ext/slick/src/org/newdawn/slick/BigImage.java b/ext/slick/src/org/newdawn/slick/BigImage.java new file mode 100644 index 0000000..5d7ad19 --- /dev/null +++ b/ext/slick/src/org/newdawn/slick/BigImage.java @@ -0,0 +1,768 @@ +package org.newdawn.slick; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; + +import org.lwjgl.BufferUtils; +import org.newdawn.slick.opengl.ImageData; +import org.newdawn.slick.opengl.ImageDataFactory; +import org.newdawn.slick.opengl.LoadableImageData; +import org.newdawn.slick.opengl.Texture; +import org.newdawn.slick.opengl.renderer.SGL; +import org.newdawn.slick.opengl.renderer.Renderer; +import org.newdawn.slick.util.OperationNotSupportedException; +import org.newdawn.slick.util.ResourceLoader; + +/** + * An image implementation that handles loaded images that are larger than the + * maximum texture size supported by the card. In most cases it makes sense + * to make sure all of your image resources are less than 512x512 in size when + * using OpenGL. However, in the rare circumstances where this isn't possible + * this implementation can be used to draw a tiled version of the image built + * from several smaller textures. + * + * This implementation does come with limitations and some performance impact + * however - so use only when absolutely required. + * + * TODO: The code in here isn't pretty, really needs revisiting with a comment stick. + * + * @author kevin + */ +public class BigImage extends Image { + /** The renderer to use for all GL operations */ + protected static SGL GL = Renderer.get(); + + /** + * Get the maximum size of an image supported by the underlying + * hardware. + * + * @return The maximum size of the textures supported by the underlying + * hardware. + */ + public static final int getMaxSingleImageSize() { + IntBuffer buffer = BufferUtils.createIntBuffer(16); + GL.glGetInteger(SGL.GL_MAX_TEXTURE_SIZE, buffer); + + return buffer.get(0); + } + + /** The last image that we put into "in use" mode */ + private static Image lastBind; + + /** The images building up this sub-image */ + private Image[][] images; + /** The number of images on the xaxis */ + private int xcount; + /** The number of images on the yaxis */ + private int ycount; + /** The real width of the whole image - maintained even when scaled */ + private int realWidth; + /** The real hieght of the whole image - maintained even when scaled */ + private int realHeight; + + /** + * Create a new big image. Empty contructor for cloning only + */ + private BigImage() { + inited = true; + } + + /** + * Create a new big image by loading it from the specified reference + * + * @param ref The reference to the image to load + * @throws SlickException Indicates we were unable to locate the resource + */ + public BigImage(String ref) throws SlickException { + this(ref, Image.FILTER_NEAREST); + } + + /** + * Create a new big image by loading it from the specified reference + * + * @param ref The reference to the image to load + * @param filter The image filter to apply (@see #Image.FILTER_NEAREST) + * @throws SlickException Indicates we were unable to locate the resource + */ + public BigImage(String ref,int filter) throws SlickException { + + build(ref, filter, getMaxSingleImageSize()); + } + + /** + * Create a new big image by loading it from the specified reference + * + * @param ref The reference to the image to load + * @param filter The image filter to apply (@see #Image.FILTER_NEAREST) + * @param tileSize The maximum size of the tiles to use to build the bigger image + * @throws SlickException Indicates we were unable to locate the resource + */ + public BigImage(String ref, int filter, int tileSize) throws SlickException { + build(ref, filter, tileSize); + } + + /** + * Create a new big image by loading it from the specified image data + * + * @param data The pixelData to use to create the image + * @param imageBuffer The buffer containing texture data + * @param filter The image filter to apply (@see #Image.FILTER_NEAREST) + */ + public BigImage(LoadableImageData data, ByteBuffer imageBuffer, int filter) { + build(data, imageBuffer, filter, getMaxSingleImageSize()); + } + + /** + * Create a new big image by loading it from the specified image data + * + * @param data The pixelData to use to create the image + * @param imageBuffer The buffer containing texture data + * @param filter The image filter to apply (@see #Image.FILTER_NEAREST) + * @param tileSize The maximum size of the tiles to use to build the bigger image + */ + public BigImage(LoadableImageData data, ByteBuffer imageBuffer, int filter, int tileSize) { + build(data, imageBuffer, filter, tileSize); + } + + /** + * Get a sub tile of this big image. Useful for debugging + * + * @param x The x tile index + * @param y The y tile index + * @return The image used for this tile + */ + public Image getTile(int x, int y) { + return images[x][y]; + } + + /** + * Create a new big image by loading it from the specified reference + * + * @param ref The reference to the image to load + * @param filter The image filter to apply (@see #Image.FILTER_NEAREST) + * @param tileSize The maximum size of the tiles to use to build the bigger image + * @throws SlickException Indicates we were unable to locate the resource + */ + private void build(String ref, int filter, int tileSize) throws SlickException { + try { + final LoadableImageData data = ImageDataFactory.getImageDataFor(ref); + final ByteBuffer imageBuffer = data.loadImage(ResourceLoader.getResourceAsStream(ref), false, null); + build(data, imageBuffer, filter, tileSize); + } catch (IOException e) { + throw new SlickException("Failed to load: "+ref, e); + } + } + + /** + * Create an big image from a image data source. + * + * @param data The pixelData to use to create the image + * @param imageBuffer The buffer containing texture data + * @param filter The filter to use when scaling this image + * @param tileSize The maximum size of the tiles to use to build the bigger image + */ + private void build(final LoadableImageData data, final ByteBuffer imageBuffer, int filter, int tileSize) { + final int dataWidth = data.getTexWidth(); + final int dataHeight = data.getTexHeight(); + + realWidth = width = data.getWidth(); + realHeight = height = data.getHeight(); + + if ((dataWidth <= tileSize) && (dataHeight <= tileSize)) { + images = new Image[1][1]; + ImageData tempData = new ImageData() { + public int getDepth() { + return data.getDepth(); + } + + public int getHeight() { + return dataHeight; + } + + public ByteBuffer getImageBufferData() { + return imageBuffer; + } + + public int getTexHeight() { + return dataHeight; + } + + public int getTexWidth() { + return dataWidth; + } + + public int getWidth() { + return dataWidth; + } + }; + images[0][0] = new Image(tempData, filter); + xcount = 1; + ycount = 1; + inited = true; + return; + } + + xcount = ((realWidth-1) / tileSize) + 1; + ycount = ((realHeight-1) / tileSize) + 1; + + images = new Image[xcount][ycount]; + int components = data.getDepth() / 8; + + for (int x=0;x 0) && (targetHeight > 0)) { + Image subImage = current.getSubImage((int) (targetX1 - xp), (int) (targetY1 - yp), + (targetX2 - targetX1), + (targetY2 - targetY1)); + foundStart = true; + image.images[startx][starty] = subImage; + starty++; + image.ycount = Math.max(image.ycount, starty); + } + + yp += current.getHeight(); + if (yt == ycount - 1) { + xp += current.getWidth(); + } + } + if (foundStart) { + startx++; + image.xcount++; + } + } + + return image; + } + + /** + * Not supported in BigImage + * + * @see org.newdawn.slick.Image#getTexture() + */ + public Texture getTexture() { + throw new OperationNotSupportedException("Can't use big images as offscreen buffers"); + } + + /** + * @see org.newdawn.slick.Image#initImpl() + */ + protected void initImpl() { + throw new OperationNotSupportedException("Can't use big images as offscreen buffers"); + } + + /** + * @see org.newdawn.slick.Image#reinit() + */ + protected void reinit() { + throw new OperationNotSupportedException("Can't use big images as offscreen buffers"); + } + + /** + * Not supported in BigImage + * + * @see org.newdawn.slick.Image#setTexture(org.newdawn.slick.opengl.Texture) + */ + public void setTexture(Texture texture) { + throw new OperationNotSupportedException("Can't use big images as offscreen buffers"); + } + + /** + * Get a sub-image that builds up this image. Note that the offsets + * used will depend on the maximum texture size on the OpenGL hardware + * + * @param offsetX The x position of the image to return + * @param offsetY The y position of the image to return + * @return The image at the specified offset into the big image + */ + public Image getSubImage(int offsetX, int offsetY) { + return images[offsetX][offsetY]; + } + + /** + * Get a count of the number images that build this image up horizontally + * + * @return The number of sub-images across the big image + */ + public int getHorizontalImageCount() { + return xcount; + } + + /** + * Get a count of the number images that build this image up vertically + * + * @return The number of sub-images down the big image + */ + public int getVerticalImageCount() { + return ycount; + } + + /** + * @see org.newdawn.slick.Image#toString() + */ + public String toString() { + return "[BIG IMAGE]"; + } + + /** + * Destroy the image and release any native resources. + * Calls on a destroyed image have undefined results + */ + public void destroy() throws SlickException { + for (int tx=0;tx 1.0) + * @param g The green component of the colour (0.0 -> 1.0) + * @param b The blue component of the colour (0.0 -> 1.0) + */ + public Color(float r,float g,float b) { + this.r = r; + this.g = g; + this.b = b; + this.a = 1; + } + + /** + * Create a 4 component colour + * + * @param r The red component of the colour (0.0 -> 1.0) + * @param g The green component of the colour (0.0 -> 1.0) + * @param b The blue component of the colour (0.0 -> 1.0) + * @param a The alpha component of the colour (0.0 -> 1.0) + */ + public Color(float r,float g,float b,float a) { + this.r = Math.min(r, 1); + this.g = Math.min(g, 1); + this.b = Math.min(b, 1); + this.a = Math.min(a, 1); + } + + /** + * Create a 3 component colour + * + * @param r The red component of the colour (0 -> 255) + * @param g The green component of the colour (0 -> 255) + * @param b The blue component of the colour (0 -> 255) + */ + public Color(int r,int g,int b) { + this.r = r / 255.0f; + this.g = g / 255.0f; + this.b = b / 255.0f; + this.a = 1; + } + + /** + * Create a 4 component colour + * + * @param r The red component of the colour (0 -> 255) + * @param g The green component of the colour (0 -> 255) + * @param b The blue component of the colour (0 -> 255) + * @param a The alpha component of the colour (0 -> 255) + */ + public Color(int r,int g,int b,int a) { + this.r = r / 255.0f; + this.g = g / 255.0f; + this.b = b / 255.0f; + this.a = a / 255.0f; + } + + /** + * Create a colour from an evil integer packed 0xAARRGGBB. If AA + * is specified as zero then it will be interpreted as unspecified + * and hence a value of 255 will be recorded. + * + * @param value The value to interpret for the colour + */ + public Color(int value) { + int r = (value & 0x00FF0000) >> 16; + int g = (value & 0x0000FF00) >> 8; + int b = (value & 0x000000FF); + int a = (value & 0xFF000000) >> 24; + + if (a < 0) { + a += 256; + } + if (a == 0) { + a = 255; + } + + this.r = r / 255.0f; + this.g = g / 255.0f; + this.b = b / 255.0f; + this.a = a / 255.0f; + } + + /** + * Decode a number in a string and process it as a colour + * reference. + * + * @param nm The number string to decode + * @return The color generated from the number read + */ + public static Color decode(String nm) { + return new Color(Integer.decode(nm).intValue()); + } + + /** + * Bind this colour to the GL context + */ + public void bind() { + GL.glColor4f(r,g,b,a); + } + + /** + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return ((int) (r+g+b+a)*255); + } + + /** + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object other) { + if (other instanceof Color) { + Color o = (Color) other; + return ((o.r == r) && (o.g == g) && (o.b == b) && (o.a == a)); + } + + return false; + } + + /** + * @see java.lang.Object#toString() + */ + public String toString() { + return "Color ("+r+","+g+","+b+","+a+")"; + } + + /** + * Make a darker instance of this colour + * + * @return The darker version of this colour + */ + public Color darker() { + return darker(0.5f); + } + + /** + * Make a darker instance of this colour + * + * @param scale The scale down of RGB (i.e. if you supply 0.03 the colour will be darkened by 3%) + * @return The darker version of this colour + */ + public Color darker(float scale) { + scale = 1 - scale; + Color temp = new Color(r * scale,g * scale,b * scale,a); + + return temp; + } + + /** + * Make a brighter instance of this colour + * + * @return The brighter version of this colour + */ + public Color brighter() { + return brighter(0.2f); + } + + /** + * Get the red byte component of this colour + * + * @return The red component (range 0-255) + */ + public int getRed() { + return (int) (r * 255); + } + + /** + * Get the green byte component of this colour + * + * @return The green component (range 0-255) + */ + public int getGreen() { + return (int) (g * 255); + } + + /** + * Get the blue byte component of this colour + * + * @return The blue component (range 0-255) + */ + public int getBlue() { + return (int) (b * 255); + } + + /** + * Get the alpha byte component of this colour + * + * @return The alpha component (range 0-255) + */ + public int getAlpha() { + return (int) (a * 255); + } + + /** + * Get the red byte component of this colour + * + * @return The red component (range 0-255) + */ + public int getRedByte() { + return (int) (r * 255); + } + + /** + * Get the green byte component of this colour + * + * @return The green component (range 0-255) + */ + public int getGreenByte() { + return (int) (g * 255); + } + + /** + * Get the blue byte component of this colour + * + * @return The blue component (range 0-255) + */ + public int getBlueByte() { + return (int) (b * 255); + } + + /** + * Get the alpha byte component of this colour + * + * @return The alpha component (range 0-255) + */ + public int getAlphaByte() { + return (int) (a * 255); + } + + /** + * Make a brighter instance of this colour + * + * @param scale The scale up of RGB (i.e. if you supply 0.03 the colour will be brightened by 3%) + * @return The brighter version of this colour + */ + public Color brighter(float scale) { + scale += 1; + Color temp = new Color(r * scale,g * scale,b * scale,a); + + return temp; + } + + /** + * Multiply this color by another + * + * @param c the other color + * @return product of the two colors + */ + public Color multiply(Color c) { + return new Color(r * c.r, g * c.g, b * c.b, a * c.a); + } + + /** + * Add another colour to this one + * + * @param c The colour to add + */ + public void add(Color c) { + r += c.r; + g += c.g; + b += c.b; + a += c.a; + } + + /** + * Scale the components of the colour by the given value + * + * @param value The value to scale by + */ + public void scale(float value) { + r *= value; + g *= value; + b *= value; + a *= value; + } + + /** + * Add another colour to this one + * + * @param c The colour to add + * @return The copy which has had the color added to it + */ + public Color addToCopy(Color c) { + Color copy = new Color(r,g,b,a); + copy.r += c.r; + copy.g += c.g; + copy.b += c.b; + copy.a += c.a; + + return copy; + } + + /** + * Scale the components of the colour by the given value + * + * @param value The value to scale by + * @return The copy which has been scaled + */ + public Color scaleCopy(float value) { + Color copy = new Color(r,g,b,a); + copy.r *= value; + copy.g *= value; + copy.b *= value; + copy.a *= value; + + return copy; + } +} diff --git a/ext/slick/src/org/newdawn/slick/ControlledInputReciever.java b/ext/slick/src/org/newdawn/slick/ControlledInputReciever.java new file mode 100644 index 0000000..b355c94 --- /dev/null +++ b/ext/slick/src/org/newdawn/slick/ControlledInputReciever.java @@ -0,0 +1,42 @@ +package org.newdawn.slick; + +/** + * Description of any class capable of recieving and controlling it's own + * reception of input + * + * You'll shouldn't really need to implement this one for your self, use one of the sub-interfaces: + * + * {@link InputListener} + * {@link MouseListener} + * {@link KeyListener} + * {@link ControllerListener} + * + * @author kevin + */ +public interface ControlledInputReciever { + + /** + * Set the input that events are being sent from + * + * @param input The input instance sending events + */ + public abstract void setInput(Input input); + + /** + * Check if this input listener is accepting input + * + * @return True if the input listener should recieve events + */ + public abstract boolean isAcceptingInput(); + + /** + * Notification that all input events have been sent for this frame + */ + public abstract void inputEnded(); + + /** + * Notification that input is about to be processed + */ + public abstract void inputStarted(); + +} \ No newline at end of file diff --git a/ext/slick/src/org/newdawn/slick/ControllerListener.java b/ext/slick/src/org/newdawn/slick/ControllerListener.java new file mode 100644 index 0000000..42dabd3 --- /dev/null +++ b/ext/slick/src/org/newdawn/slick/ControllerListener.java @@ -0,0 +1,102 @@ +package org.newdawn.slick; + +/** + * Description of classes capable of responding to controller events + * + * @author kevin + */ +public interface ControllerListener extends ControlledInputReciever { + + /** + * Notification that the left control has been pressed on + * the controller. + * + * @param controller The index of the controller on which the control + * was pressed. + */ + public abstract void controllerLeftPressed(int controller); + + /** + * Notification that the left control has been released on + * the controller. + * + * @param controller The index of the controller on which the control + * was released. + */ + public abstract void controllerLeftReleased(int controller); + + /** + * Notification that the right control has been pressed on + * the controller. + * + * @param controller The index of the controller on which the control + * was pressed. + */ + public abstract void controllerRightPressed(int controller); + + /** + * Notification that the right control has been released on + * the controller. + * + * @param controller The index of the controller on which the control + * was released. + */ + public abstract void controllerRightReleased(int controller); + + /** + * Notification that the up control has been pressed on + * the controller. + * + * @param controller The index of the controller on which the control + * was pressed. + */ + public abstract void controllerUpPressed(int controller); + + /** + * Notification that the up control has been released on + * the controller. + * + * @param controller The index of the controller on which the control + * was released. + */ + public abstract void controllerUpReleased(int controller); + + /** + * Notification that the down control has been pressed on + * the controller. + * + * @param controller The index of the controller on which the control + * was pressed. + */ + public abstract void controllerDownPressed(int controller); + + /** + * Notification that the down control has been released on + * the controller. + * + * @param controller The index of the controller on which the control + * was released. + */ + public abstract void controllerDownReleased(int controller); + + /** + * Notification that a button control has been pressed on + * the controller. + * + * @param controller The index of the controller on which the control + * was pressed. + * @param button The index of the button pressed (starting at 1) + */ + public abstract void controllerButtonPressed(int controller, int button); + + /** + * Notification that a button control has been released on + * the controller. + * + * @param controller The index of the controller on which the control + * was released. + * @param button The index of the button released (starting at 1) + */ + public abstract void controllerButtonReleased(int controller, int button); + +} \ No newline at end of file diff --git a/ext/slick/src/org/newdawn/slick/Font.java b/ext/slick/src/org/newdawn/slick/Font.java new file mode 100644 index 0000000..a107bce --- /dev/null +++ b/ext/slick/src/org/newdawn/slick/Font.java @@ -0,0 +1,65 @@ +package org.newdawn.slick; + + +/** + * The proprites of any font implementation + * + * @author Kevin Glass + */ +public interface Font { + /** + * Get the width of the given string + * + * @param str The string to obtain the rendered with of + * @return The width of the given string + */ + public abstract int getWidth(String str); + + /** + * Get the height of the given string + * + * @param str The string to obtain the rendered with of + * @return The width of the given string + */ + public abstract int getHeight(String str); + + /** + * Get the maximum height of any line drawn by this font + * + * @return The maxium height of any line drawn by this font + */ + public int getLineHeight(); + + /** + * Draw a string to the screen + * + * @param x The x location at which to draw the string + * @param y The y location at which to draw the string + * @param text The text to be displayed + */ + public abstract void drawString(float x, float y, String text); + + /** + * Draw a string to the screen + * + * @param x The x location at which to draw the string + * @param y The y location at which to draw the string + * @param text The text to be displayed + * @param col The colour to draw with + */ + public abstract void drawString(float x, float y, String text, Color col); + + + /** + * Draw part of a string to the screen. Note that this will + * still position the text as though it's part of the bigger string. + * + * @param x The x location at which to draw the string + * @param y The y location at which to draw the string + * @param text The text to be displayed + * @param col The colour to draw with + * @param startIndex The index of the first character to draw + * @param endIndex The index of the last character from the string to draw + */ + public abstract void drawString(float x, float y, String text, Color col, int startIndex, int endIndex); +} \ No newline at end of file diff --git a/ext/slick/src/org/newdawn/slick/Game.java b/ext/slick/src/org/newdawn/slick/Game.java new file mode 100644 index 0000000..bd91eb4 --- /dev/null +++ b/ext/slick/src/org/newdawn/slick/Game.java @@ -0,0 +1,55 @@ +package org.newdawn.slick; + +/** + * The main game interface that should be implemented by any game being developed + * using the container system. There will be some utility type sub-classes as development + * continues. + * + * @see org.newdawn.slick.BasicGame + * + * @author kevin + */ +public interface Game { + /** + * Initialise the game. This can be used to load static resources. It's called + * before the game loop starts + * + * @param container The container holding the game + * @throws SlickException Throw to indicate an internal error + */ + public void init(GameContainer container) throws SlickException; + + /** + * Update the game logic here. No rendering should take place in this method + * though it won't do any harm. + * + * @param container The container holing this game + * @param delta The amount of time thats passed since last update in milliseconds + * @throws SlickException Throw to indicate an internal error + */ + public void update(GameContainer container, int delta) throws SlickException; + + /** + * Render the game's screen here. + * + * @param container The container holing this game + * @param g The graphics context that can be used to render. However, normal rendering + * routines can also be used. + * @throws SlickException Throw to indicate a internal error + */ + public void render(GameContainer container, Graphics g) throws SlickException; + + /** + * Notification that a game close has been requested + * + * @return True if the game should close + */ + public boolean closeRequested(); + + /** + * Get the title of this game + * + * @return The title of the game + */ + public String getTitle(); +} diff --git a/ext/slick/src/org/newdawn/slick/GameContainer.java b/ext/slick/src/org/newdawn/slick/GameContainer.java new file mode 100644 index 0000000..cb01fd2 --- /dev/null +++ b/ext/slick/src/org/newdawn/slick/GameContainer.java @@ -0,0 +1,861 @@ +package org.newdawn.slick; + +import java.io.IOException; +import java.util.Properties; + +import org.lwjgl.LWJGLException; +import org.lwjgl.Sys; +import org.lwjgl.input.Cursor; +import org.lwjgl.opengl.Display; +import org.lwjgl.opengl.Drawable; +import org.lwjgl.opengl.Pbuffer; +import org.lwjgl.opengl.PixelFormat; +import org.newdawn.slick.gui.GUIContext; +import org.newdawn.slick.openal.SoundStore; +import org.newdawn.slick.opengl.CursorLoader; +import org.newdawn.slick.opengl.ImageData; +import org.newdawn.slick.opengl.renderer.Renderer; +import org.newdawn.slick.opengl.renderer.SGL; +import org.newdawn.slick.util.Log; +import org.newdawn.slick.util.ResourceLoader; + +/** + * A generic game container that handles the game loop, fps recording and + * managing the input system + * + * @author kevin + */ +public abstract class GameContainer implements GUIContext { + /** The renderer to use for all GL operations */ + protected static SGL GL = Renderer.get(); + /** The shared drawable if any */ + protected static Drawable SHARED_DRAWABLE; + + /** The time the last frame was rendered */ + protected long lastFrame; + /** The last time the FPS recorded */ + protected long lastFPS; + /** The last recorded FPS */ + protected int recordedFPS; + /** The current count of FPS */ + protected int fps; + /** True if we're currently running the game loop */ + protected boolean running = true; + + /** The width of the display */ + protected int width; + /** The height of the display */ + protected int height; + /** The game being managed */ + protected Game game; + + /** The default font to use in the graphics context */ + private Font defaultFont; + /** The graphics context to be passed to the game */ + private Graphics graphics; + + /** The input system to pass to the game */ + protected Input input; + /** The FPS we want to lock to */ + protected int targetFPS = -1; + /** True if we should show the fps */ + private boolean showFPS = true; + /** The minimum logic update interval */ + protected long minimumLogicInterval = 1; + /** The stored delta */ + protected long storedDelta; + /** The maximum logic update interval */ + protected long maximumLogicInterval = 0; + /** The last game started */ + protected Game lastGame; + /** True if we should clear the screen each frame */ + protected boolean clearEachFrame = true; + + /** True if the game is paused */ + protected boolean paused; + /** True if we should force exit */ + protected boolean forceExit = true; + /** True if vsync has been requested */ + protected boolean vsync; + /** Smoothed deltas requested */ + protected boolean smoothDeltas; + /** The number of samples we'll attempt through hardware */ + protected int samples; + + /** True if this context supports multisample */ + protected boolean supportsMultiSample; + + /** True if we should render when not focused */ + protected boolean alwaysRender; + + /** + * Create a new game container wrapping a given game + * + * @param game The game to be wrapped + */ + protected GameContainer(Game game) { + this.game = game; + lastFrame = getTime(); + + getBuildVersion(); + Log.checkVerboseLogSetting(); + } + + /** + * Set the default font that will be intialised in the graphics held in this container + * + * @param font The font to use as default + */ + public void setDefaultFont(Font font) { + if (font != null) { + this.defaultFont = font; + } else { + Log.warn("Please provide a non null font"); + } + } + + /** + * Indicate whether we want to try to use fullscreen multisampling. This will + * give antialiasing across the whole scene using a hardware feature. + * + * @param samples The number of samples to attempt (2 is safe) + */ + public void setMultiSample(int samples) { + this.samples = samples; + } + + /** + * Check if this hardware can support multi-sampling + * + * @return True if the hardware supports multi-sampling + */ + public boolean supportsMultiSample() { + return supportsMultiSample; + } + + /** + * The number of samples we're attempting to performing using + * hardware multisampling + * + * @return The number of samples requested + */ + public int getSamples() { + return samples; + } + + /** + * Indicate if we should force exitting the VM at the end + * of the game (default = true) + * + * @param forceExit True if we should force the VM exit + */ + public void setForceExit(boolean forceExit) { + this.forceExit = forceExit; + } + + /** + * Indicate if we want to smooth deltas. This feature will report + * a delta based on the FPS not the time passed. This works well with + * vsync. + * + * @param smoothDeltas True if we should report smooth deltas + */ + public void setSmoothDeltas(boolean smoothDeltas) { + this.smoothDeltas = smoothDeltas; + } + + /** + * Check if the display is in fullscreen mode + * + * @return True if the display is in fullscreen mode + */ + public boolean isFullscreen() { + return false; + } + + /** + * Get the aspect ratio of the screen + * + * @return The aspect ratio of the display + */ + public float getAspectRatio() { + return getWidth() / getHeight(); + } + + /** + * Indicate whether we want to be in fullscreen mode. Note that the current + * display mode must be valid as a fullscreen mode for this to work + * + * @param fullscreen True if we want to be in fullscreen mode + * @throws SlickException Indicates we failed to change the display mode + */ + public void setFullscreen(boolean fullscreen) throws SlickException { + } + + /** + * Enable shared OpenGL context. After calling this all containers created + * will shared a single parent context + * + * @throws SlickException Indicates a failure to create the shared drawable + */ + public static void enableSharedContext() throws SlickException { + try { + SHARED_DRAWABLE = new Pbuffer(64, 64, new PixelFormat(8, 0, 0), null); + } catch (LWJGLException e) { + throw new SlickException("Unable to create the pbuffer used for shard context, buffers not supported", e); + } + } + + /** + * Get the context shared by all containers + * + * @return The context shared by all the containers or null if shared context isn't enabled + */ + public static Drawable getSharedContext() { + return SHARED_DRAWABLE; + } + + /** + * Indicate if we should clear the screen at the beginning of each frame. If you're + * rendering to the whole screen each frame then setting this to false can give + * some performance improvements + * + * @param clear True if the the screen should be cleared each frame + */ + public void setClearEachFrame(boolean clear) { + this.clearEachFrame = clear; + } + + /** + * Renitialise the game and the context in which it's being rendered + * + * @throws SlickException Indicates a failure rerun initialisation routines + */ + public void reinit() throws SlickException { + } + + /** + * Pause the game - i.e. suspend updates + */ + public void pause() + { + setPaused(true); + } + + /** + * Resumt the game - i.e. continue updates + */ + public void resume() + { + setPaused(false); + } + + /** + * Check if the container is currently paused. + * + * @return True if the container is paused + */ + public boolean isPaused() { + return paused; + } + + /** + * Indicates if the game should be paused, i.e. if updates + * should be propogated through to the game. + * + * @param paused True if the game should be paused + */ + public void setPaused(boolean paused) + { + this.paused = paused; + } + + /** + * True if this container should render when it has focus + * + * @return True if this container should render when it has focus + */ + public boolean getAlwaysRender () { + return alwaysRender; + } + + /** + * Indicate whether we want this container to render when it has focus + * + * @param alwaysRender True if this container should render when it has focus + */ + public void setAlwaysRender (boolean alwaysRender) { + this.alwaysRender = alwaysRender; + } + + /** + * Get the build number of slick + * + * @return The build number of slick + */ + public static int getBuildVersion() { + try { + Properties props = new Properties(); + props.load(ResourceLoader.getResourceAsStream("version")); + + int build = Integer.parseInt(props.getProperty("build")); + Log.info("Slick Build #"+build); + + return build; + } catch (Exception e) { + Log.error("Unable to determine Slick build number"); + return -1; + } + } + + /** + * Get the default system font + * + * @return The default system font + */ + public Font getDefaultFont() { + return defaultFont; + } + + /** + * Check if sound effects are enabled + * + * @return True if sound effects are enabled + */ + public boolean isSoundOn() { + return SoundStore.get().soundsOn(); + } + + /** + * Check if music is enabled + * + * @return True if music is enabled + */ + public boolean isMusicOn() { + return SoundStore.get().musicOn(); + } + + /** + * Indicate whether music should be enabled + * + * @param on True if music should be enabled + */ + public void setMusicOn(boolean on) { + SoundStore.get().setMusicOn(on); + } + + /** + * Indicate whether sound effects should be enabled + * + * @param on True if sound effects should be enabled + */ + public void setSoundOn(boolean on) { + SoundStore.get().setSoundsOn(on); + } + + /** + * Retrieve the current default volume for music + * @return the current default volume for music + */ + public float getMusicVolume() { + return SoundStore.get().getMusicVolume(); + } + + /** + * Retrieve the current default volume for sound fx + * @return the current default volume for sound fx + */ + public float getSoundVolume() { + return SoundStore.get().getSoundVolume(); + } + + /** + * Set the default volume for sound fx + * @param volume the new default value for sound fx volume + */ + public void setSoundVolume(float volume) { + SoundStore.get().setSoundVolume(volume); + } + + /** + * Set the default volume for music + * @param volume the new default value for music volume + */ + public void setMusicVolume(float volume) { + SoundStore.get().setMusicVolume(volume); + } + + /** + * Get the width of the standard screen resolution + * + * @return The screen width + */ + public abstract int getScreenWidth(); + + /** + * Get the height of the standard screen resolution + * + * @return The screen height + */ + public abstract int getScreenHeight(); + + /** + * Get the width of the game canvas + * + * @return The width of the game canvas + */ + public int getWidth() { + return width; + } + + /** + * Get the height of the game canvas + * + * @return The height of the game canvas + */ + public int getHeight() { + return height; + } + + /** + * Set the icon to be displayed if possible in this type of + * container + * + * @param ref The reference to the icon to be displayed + * @throws SlickException Indicates a failure to load the icon + */ + public abstract void setIcon(String ref) throws SlickException; + + /** + * Set the icons to be used for this application. Note that the size of the icon + * defines how it will be used. Important ones to note + * + * Windows window icon must be 16x16 + * Windows alt-tab icon must be 24x24 or 32x32 depending on Windows version (XP=32) + * + * @param refs The reference to the icon to be displayed + * @throws SlickException Indicates a failure to load the icon + */ + public abstract void setIcons(String[] refs) throws SlickException; + + /** + * Get the accurate system time + * + * @return The system time in milliseconds + */ + public long getTime() { + return (Sys.getTime() * 1000) / Sys.getTimerResolution(); + } + + /** + * Sleep for a given period + * + * @param milliseconds The period to sleep for in milliseconds + */ + public void sleep(int milliseconds) { + long target = getTime()+milliseconds; + while (getTime() < target) { + try { Thread.sleep(1); } catch (Exception e) {} + } + } + + /** + * Set the mouse cursor to be displayed - this is a hardware cursor and hence + * shouldn't have any impact on FPS. + * + * @param ref The location of the image to be loaded for the cursor + * @param hotSpotX The x coordinate of the hotspot within the cursor image + * @param hotSpotY The y coordinate of the hotspot within the cursor image + * @throws SlickException Indicates a failure to load the cursor image or create the hardware cursor + */ + public abstract void setMouseCursor(String ref, int hotSpotX, int hotSpotY) throws SlickException; + + /** + * Set the mouse cursor to be displayed - this is a hardware cursor and hence + * shouldn't have any impact on FPS. + * + * @param data The image data from which the cursor can be construted + * @param hotSpotX The x coordinate of the hotspot within the cursor image + * @param hotSpotY The y coordinate of the hotspot within the cursor image + * @throws SlickException Indicates a failure to load the cursor image or create the hardware cursor + */ + public abstract void setMouseCursor(ImageData data, int hotSpotX, int hotSpotY) throws SlickException; + + /** + * Set the mouse cursor based on the contents of the image. Note that this will not take + * account of render state type changes to images (rotation and such). If these effects + * are required it is recommended that an offscreen buffer be used to produce an appropriate + * image. An offscreen buffer will always be used to produce the new cursor and as such + * this operation an be very expensive + * + * @param image The image to use as the cursor + * @param hotSpotX The x coordinate of the hotspot within the cursor image + * @param hotSpotY The y coordinate of the hotspot within the cursor image + * @throws SlickException Indicates a failure to load the cursor image or create the hardware cursor + */ + public abstract void setMouseCursor(Image image, int hotSpotX, int hotSpotY) throws SlickException; + + /** + * Set the mouse cursor to be displayed - this is a hardware cursor and hence + * shouldn't have any impact on FPS. + * + * @param cursor The cursor to use + * @param hotSpotX The x coordinate of the hotspot within the cursor image + * @param hotSpotY The y coordinate of the hotspot within the cursor image + * @throws SlickException Indicates a failure to load the cursor image or create the hardware cursor + */ + public abstract void setMouseCursor(Cursor cursor, int hotSpotX, int hotSpotY) throws SlickException; + + /** + * Get a cursor based on a image reference on the classpath. The image + * is assumed to be a set/strip of cursor animation frames running from top to + * bottom. + * + * @param ref The reference to the image to be loaded + * @param x The x-coordinate of the cursor hotspot (left -> right) + * @param y The y-coordinate of the cursor hotspot (bottom -> top) + * @param width The x width of the cursor + * @param height The y height of the cursor + * @param cursorDelays image delays between changing frames in animation + * + * @throws SlickException Indicates a failure to load the image or a failure to create the hardware cursor + */ + public void setAnimatedMouseCursor(String ref, int x, int y, int width, int height, int[] cursorDelays) throws SlickException + { + try { + Cursor cursor; + cursor = CursorLoader.get().getAnimatedCursor(ref, x, y, width, height, cursorDelays); + setMouseCursor(cursor, x, y); + } catch (IOException e) { + throw new SlickException("Failed to set mouse cursor", e); + } catch (LWJGLException e) { + throw new SlickException("Failed to set mouse cursor", e); + } + } + + /** + * Set the default mouse cursor - i.e. the original cursor before any native + * cursor was set + */ + public abstract void setDefaultMouseCursor(); + + /** + * Get the input system + * + * @return The input system available to this game container + */ + public Input getInput() { + return input; + } + + /** + * Get the current recorded FPS (frames per second) + * + * @return The current FPS + */ + public int getFPS() { + return recordedFPS; + } + + /** + * Indicate whether mouse cursor should be grabbed or not + * + * @param grabbed True if mouse cursor should be grabbed + */ + public abstract void setMouseGrabbed(boolean grabbed); + + /** + * Check if the mouse cursor is current grabbed. This will cause it not + * to be seen. + * + * @return True if the mouse is currently grabbed + */ + public abstract boolean isMouseGrabbed(); + + /** + * Retrieve the time taken to render the last frame, i.e. the change in time - delta. + * + * @return The time taken to render the last frame + */ + protected int getDelta() { + long time = getTime(); + int delta = (int) (time - lastFrame); + lastFrame = time; + + return delta; + } + + /** + * Updated the FPS counter + */ + protected void updateFPS() { + if (getTime() - lastFPS > 1000) { + lastFPS = getTime(); + recordedFPS = fps; + fps = 0; + } + fps++; + } + + /** + * Set the minimum amount of time in milliseonds that has to + * pass before update() is called on the container game. This gives + * a way to limit logic updates compared to renders. + * + * @param interval The minimum interval between logic updates + */ + public void setMinimumLogicUpdateInterval(int interval) { + minimumLogicInterval = interval; + } + + /** + * Set the maximum amount of time in milliseconds that can passed + * into the update method. Useful for collision detection without + * sweeping. + * + * @param interval The maximum interval between logic updates + */ + public void setMaximumLogicUpdateInterval(int interval) { + maximumLogicInterval = interval; + } + + /** + * Update and render the game + * + * @param delta The change in time since last update and render + * @throws SlickException Indicates an internal fault to the game. + */ + protected void updateAndRender(int delta) throws SlickException { + if (smoothDeltas) { + if (getFPS() != 0) { + delta = 1000 / getFPS(); + } + } + + input.poll(width, height); + + Music.poll(delta); + if (!paused) { + storedDelta += delta; + + if (storedDelta >= minimumLogicInterval) { + try { + if (maximumLogicInterval != 0) { + long cycles = storedDelta / maximumLogicInterval; + for (int i=0;i minimumLogicInterval) { + game.update(this, (int) (delta % maximumLogicInterval)); + storedDelta = 0; + } else { + storedDelta = remainder; + } + } else { + game.update(this, (int) storedDelta); + storedDelta = 0; + } + + } catch (Throwable e) { + Log.error(e); + throw new SlickException("Game.update() failure - check the game code."); + } + } + } else { + game.update(this, 0); + } + + if (hasFocus() || getAlwaysRender()) { + if (clearEachFrame) { + GL.glClear(SGL.GL_COLOR_BUFFER_BIT | SGL.GL_DEPTH_BUFFER_BIT); + } + + GL.glLoadIdentity(); + + graphics.resetFont(); + graphics.resetLineWidth(); + graphics.setAntiAlias(false); + try { + game.render(this, graphics); + } catch (Throwable e) { + Log.error(e); + throw new SlickException("Game.render() failure - check the game code."); + } + graphics.resetTransform(); + + if (showFPS) { + defaultFont.drawString(10, 10, "FPS: "+recordedFPS); + } + + GL.flush(); + } + + if (targetFPS != -1) { + Display.sync(targetFPS); + } + } + + /** + * Indicate if the display should update only when the game is visible + * (the default is true) + * + * @param updateOnlyWhenVisible True if we should updated only when the display is visible + */ + public void setUpdateOnlyWhenVisible(boolean updateOnlyWhenVisible) { + } + + /** + * Check if this game is only updating when visible to the user (default = true) + * + * @return True if the game is only updated when the display is visible + */ + public boolean isUpdatingOnlyWhenVisible() { + return true; + } + + /** + * Initialise the GL context + */ + protected void initGL() { + Log.info("Starting display "+width+"x"+height); + GL.initDisplay(width, height); + + if (input == null) { + input = new Input(height); + } + input.init(height); + // no need to remove listeners? + //input.removeAllListeners(); + if (game instanceof InputListener) { + input.removeListener((InputListener) game); + input.addListener((InputListener) game); + } + + if (graphics != null) { + graphics.setDimensions(getWidth(), getHeight()); + } + lastGame = game; + } + + /** + * Initialise the system components, OpenGL and OpenAL. + * + * @throws SlickException Indicates a failure to create a native handler + */ + protected void initSystem() throws SlickException { + initGL(); + setMusicVolume(1.0f); + setSoundVolume(1.0f); + + graphics = new Graphics(width, height); + defaultFont = graphics.getFont(); + } + + /** + * Enter the orthographic mode + */ + protected void enterOrtho() { + enterOrtho(width, height); + } + + /** + * Indicate whether the container should show the FPS + * + * @param show True if the container should show the FPS + */ + public void setShowFPS(boolean show) { + showFPS = show; + } + + /** + * Check if the FPS is currently showing + * + * @return True if the FPS is showing + */ + public boolean isShowingFPS() { + return showFPS; + } + + /** + * Set the target fps we're hoping to get + * + * @param fps The target fps we're hoping to get + */ + public void setTargetFrameRate(int fps) { + targetFPS = fps; + } + + /** + * Indicate whether the display should be synced to the + * vertical refresh (stops tearing) + * + * @param vsync True if we want to sync to vertical refresh + */ + public void setVSync(boolean vsync) { + this.vsync = vsync; + Display.setVSyncEnabled(vsync); + } + + /** + * True if vsync is requested + * + * @return True if vsync is requested + */ + public boolean isVSyncRequested() { + return vsync; + } + + /** + * True if the game is running + * + * @return True if the game is running + */ + protected boolean running() { + return running; + } + + /** + * Inidcate we want verbose logging + * + * @param verbose True if we want verbose logging (INFO and DEBUG) + */ + public void setVerbose(boolean verbose) { + Log.setVerbose(verbose); + } + + /** + * Cause the game to exit and shutdown cleanly + */ + public void exit() { + running = false; + } + + /** + * Check if the game currently has focus + * + * @return True if the game currently has focus + */ + public abstract boolean hasFocus(); + + /** + * Get the graphics context used by this container. Note that this + * value may vary over the life time of the game. + * + * @return The graphics context used by this container + */ + public Graphics getGraphics() { + return graphics; + } + + /** + * Enter the orthographic mode + * + * @param xsize The size of the panel being used + * @param ysize The size of the panel being used + */ + protected void enterOrtho(int xsize, int ysize) { + GL.enterOrtho(xsize, ysize); + } +} diff --git a/ext/slick/src/org/newdawn/slick/Graphics.java b/ext/slick/src/org/newdawn/slick/Graphics.java new file mode 100644 index 0000000..8dc379e --- /dev/null +++ b/ext/slick/src/org/newdawn/slick/Graphics.java @@ -0,0 +1,1779 @@ +package org.newdawn.slick; + +import java.nio.ByteBuffer; +import java.nio.DoubleBuffer; +import java.nio.FloatBuffer; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; + +import org.lwjgl.BufferUtils; +import org.lwjgl.opengl.GL11; +import org.newdawn.slick.geom.Rectangle; +import org.newdawn.slick.geom.Shape; +import org.newdawn.slick.geom.ShapeRenderer; +import org.newdawn.slick.opengl.TextureImpl; +import org.newdawn.slick.opengl.renderer.LineStripRenderer; +import org.newdawn.slick.opengl.renderer.Renderer; +import org.newdawn.slick.opengl.renderer.SGL; +import org.newdawn.slick.util.FastTrig; +import org.newdawn.slick.util.Log; + +/** + * A graphics context that can be used to render primatives to the accelerated + * canvas provided by LWJGL. + * + * @author kevin + */ +public class Graphics { + /** The renderer to use for all GL operations */ + protected static SGL GL = Renderer.get(); + /** The renderer to use line strips */ + private static LineStripRenderer LSR = Renderer.getLineStripRenderer(); + + /** The normal drawing mode */ + public static int MODE_NORMAL = 1; + + /** Draw to the alpha map */ + public static int MODE_ALPHA_MAP = 2; + + /** Draw using the alpha blending */ + public static int MODE_ALPHA_BLEND = 3; + + /** Draw multiplying the source and destination colours */ + public static int MODE_COLOR_MULTIPLY = 4; + + /** Draw adding the existing colour to the new colour */ + public static int MODE_ADD = 5; + + /** Draw blending the new image into the old one by a factor of it's colour */ + public static int MODE_SCREEN = 6; + + /** The default number of segments that will be used when drawing an oval */ + private static final int DEFAULT_SEGMENTS = 50; + + /** The last graphics context in use */ + protected static Graphics currentGraphics = null; + + /** The default font to use */ + protected static Font DEFAULT_FONT; + + /** The last set scale */ + private float sx = 1; + /** The last set scale */ + private float sy = 1; + + /** + * Set the current graphics context in use + * + * @param current The graphics context that should be considered current + */ + public static void setCurrent(Graphics current) { + if (currentGraphics != current) { + if (currentGraphics != null) { + currentGraphics.disable(); + } + currentGraphics = current; + currentGraphics.enable(); + } + } + + /** The font in use */ + private Font font; + + /** The current color */ + private Color currentColor = Color.white; + + /** The width of the screen */ + protected int screenWidth; + + /** The height of the screen */ + protected int screenHeight; + + /** True if the matrix has been pushed to the stack */ + private boolean pushed; + + /** The graphics context clipping */ + private Rectangle clip; + + /** Buffer used for setting the world clip */ + private DoubleBuffer worldClip = BufferUtils.createDoubleBuffer(4); + + /** The buffer used to read a screen pixel */ + private ByteBuffer readBuffer = BufferUtils.createByteBuffer(4); + + /** True if we're antialias */ + private boolean antialias; + + /** The world clip recorded since last set */ + private Rectangle worldClipRecord; + + /** The current drawing mode */ + private int currentDrawingMode = MODE_NORMAL; + + /** The current line width */ + private float lineWidth = 1; + + /** The matrix stack */ + private ArrayList stack = new ArrayList(); + /** The index into the stack we're using */ + private int stackIndex; + + /** + * Default constructor for sub-classes + */ + public Graphics() { + } + + /** + * Create a new graphics context. Only the container should be doing this + * really + * + * @param width + * The width of the screen for this context + * @param height + * The height of the screen for this context + */ + public Graphics(int width, int height) { + if (DEFAULT_FONT == null) { + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + try { + DEFAULT_FONT = new AngelCodeFont( + "org/newdawn/slick/data/defaultfont.fnt", + "org/newdawn/slick/data/defaultfont.png"); + } catch (SlickException e) { + Log.error(e); + } + return null; // nothing to return + } + }); + } + + this.font = DEFAULT_FONT; + screenWidth = width; + screenHeight = height; + } + + /** + * Set the dimensions considered by the graphics context + * + * @param width The width of the graphics context + * @param height The height of the graphics context + */ + void setDimensions(int width, int height) { + screenWidth = width; + screenHeight = height; + } + + /** + * Set the drawing mode to use. This mode defines how pixels are drawn to + * the graphics context. It can be used to draw into the alpha map. + * + * The mode supplied should be one of {@link Graphics#MODE_NORMAL} or + * {@link Graphics#MODE_ALPHA_MAP} or {@link Graphics#MODE_ALPHA_BLEND} + * + * @param mode + * The mode to apply. + */ + public void setDrawMode(int mode) { + predraw(); + currentDrawingMode = mode; + if (currentDrawingMode == MODE_NORMAL) { + GL.glEnable(SGL.GL_BLEND); + GL.glColorMask(true, true, true, true); + GL.glBlendFunc(SGL.GL_SRC_ALPHA, SGL.GL_ONE_MINUS_SRC_ALPHA); + } + if (currentDrawingMode == MODE_ALPHA_MAP) { + GL.glDisable(SGL.GL_BLEND); + GL.glColorMask(false, false, false, true); + } + if (currentDrawingMode == MODE_ALPHA_BLEND) { + GL.glEnable(SGL.GL_BLEND); + GL.glColorMask(true, true, true, false); + GL.glBlendFunc(GL11.GL_DST_ALPHA, GL11.GL_ONE_MINUS_DST_ALPHA); + } + if (currentDrawingMode == MODE_COLOR_MULTIPLY) { + GL.glEnable(SGL.GL_BLEND); + GL.glColorMask(true, true, true, true); + GL.glBlendFunc(GL11.GL_ONE_MINUS_SRC_COLOR, GL11.GL_SRC_COLOR); + } + if (currentDrawingMode == MODE_ADD) { + GL.glEnable(SGL.GL_BLEND); + GL.glColorMask(true, true, true, true); + GL.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE); + } + if (currentDrawingMode == MODE_SCREEN) { + GL.glEnable(SGL.GL_BLEND); + GL.glColorMask(true, true, true, true); + GL.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_COLOR); + } + postdraw(); + } + + /** + * Clear the state of the alpha map across the entire screen. This sets + * alpha to 0 everywhere, meaning in {@link Graphics#MODE_ALPHA_BLEND} + * nothing will be drawn. + */ + public void clearAlphaMap() { + pushTransform(); + GL.glLoadIdentity(); + + int originalMode = currentDrawingMode; + setDrawMode(MODE_ALPHA_MAP); + setColor(new Color(0,0,0,0)); + fillRect(0, 0, screenWidth, screenHeight); + setColor(currentColor); + setDrawMode(originalMode); + + popTransform(); + } + + /** + * Must be called before all OpenGL operations to maintain context for + * dynamic images + */ + private void predraw() { + setCurrent(this); + } + + /** + * Must be called after all OpenGL operations to maintain context for + * dynamic images + */ + private void postdraw() { + } + + /** + * Enable rendering to this graphics context + */ + protected void enable() { + } + + /** + * Flush this graphics context to the underlying rendering context + */ + public void flush() { + if (currentGraphics == this) { + currentGraphics.disable(); + currentGraphics = null; + } + } + + /** + * Disable rendering to this graphics context + */ + protected void disable() { + } + + /** + * Get the current font + * + * @return The current font + */ + public Font getFont() { + return font; + } + + /** + * Set the background colour of the graphics context + * + * @param color + * The background color of the graphics context + */ + public void setBackground(Color color) { + predraw(); + GL.glClearColor(color.r, color.g, color.b, color.a); + postdraw(); + } + + /** + * Get the current graphics context background color + * + * @return The background color of this graphics context + */ + public Color getBackground() { + predraw(); + FloatBuffer buffer = BufferUtils.createFloatBuffer(16); + GL.glGetFloat(SGL.GL_COLOR_CLEAR_VALUE, buffer); + postdraw(); + + return new Color(buffer); + } + + /** + * Clear the graphics context + */ + public void clear() { + predraw(); + GL.glClear(SGL.GL_COLOR_BUFFER_BIT); + postdraw(); + } + + /** + * Reset the transformation on this graphics context + */ + public void resetTransform() { + sx = 1; + sy = 1; + + if (pushed) { + predraw(); + GL.glPopMatrix(); + pushed = false; + postdraw(); + } + } + + /** + * Check if we've pushed the previous matrix, if not then push it now. + */ + private void checkPush() { + if (!pushed) { + predraw(); + GL.glPushMatrix(); + pushed = true; + postdraw(); + } + } + + /** + * Apply a scaling factor to everything drawn on the graphics context + * + * @param sx + * The scaling factor to apply to the x axis + * @param sy + * The scaling factor to apply to the y axis + */ + public void scale(float sx, float sy) { + this.sx = this.sx * sx; + this.sy = this.sy * sy; + + checkPush(); + + predraw(); + GL.glScalef(sx, sy, 1); + postdraw(); + } + + /** + * Apply a rotation to everything draw on the graphics context + * + * @param rx + * The x coordinate of the center of rotation + * @param ry + * The y coordinate of the center of rotation + * @param ang + * The angle (in degrees) to rotate by + */ + public void rotate(float rx, float ry, float ang) { + checkPush(); + + predraw(); + translate(rx, ry); + GL.glRotatef(ang, 0, 0, 1); + translate(-rx, -ry); + postdraw(); + } + + /** + * Apply a translation to everything drawn to the context + * + * @param x + * The amount to translate on the x-axis + * @param y + * The amount of translate on the y-axis + */ + public void translate(float x, float y) { + checkPush(); + + predraw(); + GL.glTranslatef(x, y, 0); + postdraw(); + } + + /** + * Set the font to be used when rendering text + * + * @param font + * The font to be used when rendering text + */ + public void setFont(Font font) { + this.font = font; + } + + /** + * Reset to using the default font for this context + */ + public void resetFont() { + font = DEFAULT_FONT; + } + + /** + * Set the color to use when rendering to this context + * + * @param color + * The color to use when rendering to this context + */ + public void setColor(Color color) { + if (color == null) { + return; + } + + currentColor = new Color(color); + predraw(); + currentColor.bind(); + postdraw(); + } + + /** + * Get the color in use by this graphics context + * + * @return The color in use by this graphics context + */ + public Color getColor() { + return new Color(currentColor); + } + + /** + * Draw a line on the canvas in the current colour + * + * @param x1 + * The x coordinate of the start point + * @param y1 + * The y coordinate of the start point + * @param x2 + * The x coordinate of the end point + * @param y2 + * The y coordinate of the end point + */ + public void drawLine(float x1, float y1, float x2, float y2) { + float lineWidth = this.lineWidth - 1; + + if (LSR.applyGLLineFixes()) { + if (x1 == x2) { + if (y1 > y2) { + float temp = y2; + y2 = y1; + y1 = temp; + } + float step = 1 / sy; + lineWidth = lineWidth / sy; + fillRect(x1-(lineWidth/2.0f),y1-(lineWidth/2.0f),lineWidth+step,(y2-y1)+lineWidth+step); + return; + } else if (y1 == y2) { + if (x1 > x2) { + float temp = x2; + x2 = x1; + x1 = temp; + } + float step = 1 / sx; + lineWidth = lineWidth / sx; + fillRect(x1-(lineWidth/2.0f),y1-(lineWidth/2.0f),(x2-x1)+lineWidth+step,lineWidth+step); + return; + } + } + + predraw(); + currentColor.bind(); + TextureImpl.bindNone(); + + LSR.start(); + LSR.vertex(x1,y1); + LSR.vertex(x2,y2); + LSR.end(); + + postdraw(); + } + + /** + * Draw the outline of the given shape. + * + * @param shape + * The shape to draw. + * @param fill + * The fill type to apply + */ + public void draw(Shape shape, ShapeFill fill) { + predraw(); + TextureImpl.bindNone(); + + ShapeRenderer.draw(shape, fill); + + currentColor.bind(); + postdraw(); + } + + /** + * Draw the the given shape filled in. + * + * @param shape + * The shape to fill. + * @param fill + * The fill type to apply + */ + public void fill(Shape shape, ShapeFill fill) { + predraw(); + TextureImpl.bindNone(); + + ShapeRenderer.fill(shape, fill); + + currentColor.bind(); + postdraw(); + } + + /** + * Draw the outline of the given shape. + * + * @param shape + * The shape to draw. + */ + public void draw(Shape shape) { + predraw(); + TextureImpl.bindNone(); + currentColor.bind(); + + ShapeRenderer.draw(shape); + + postdraw(); + } + + /** + * Draw the the given shape filled in. + * + * @param shape + * The shape to fill. + */ + public void fill(Shape shape) { + predraw(); + TextureImpl.bindNone(); + currentColor.bind(); + + ShapeRenderer.fill(shape); + + postdraw(); + } + + /** + * Draw the the given shape filled in with a texture + * + * @param shape + * The shape to texture. + * @param image + * The image to tile across the shape + */ + public void texture(Shape shape, Image image) { + texture(shape, image, 0.01f, 0.01f, false); + } + + /** + * Draw the the given shape filled in with a texture + * + * @param shape + * The shape to texture. + * @param image + * The image to tile across the shape + * @param fill + * The shape fill to apply + */ + public void texture(Shape shape, Image image, ShapeFill fill) { + texture(shape, image, 0.01f, 0.01f, fill); + } + + /** + * Draw the the given shape filled in with a texture + * + * @param shape + * The shape to texture. + * @param image + * The image to tile across the shape + * @param fit + * True if we want to fit the image on to the shape + */ + public void texture(Shape shape, Image image, boolean fit) { + if (fit) { + texture(shape, image, 1, 1, true); + } else { + texture(shape, image, 0.01f, 0.01f, false); + } + } + + /** + * Draw the the given shape filled in with a texture + * + * @param shape + * The shape to texture. + * @param image + * The image to tile across the shape + * @param scaleX + * The scale to apply on the x axis for texturing + * @param scaleY + * The scale to apply on the y axis for texturing + */ + public void texture(Shape shape, Image image, float scaleX, float scaleY) { + texture(shape, image, scaleX, scaleY, false); + } + + /** + * Draw the the given shape filled in with a texture + * + * @param shape + * The shape to texture. + * @param image + * The image to tile across the shape + * @param scaleX + * The scale to apply on the x axis for texturing + * @param scaleY + * The scale to apply on the y axis for texturing + * @param fit + * True if we want to fit the image on to the shape + */ + public void texture(Shape shape, Image image, float scaleX, float scaleY, + boolean fit) { + predraw(); + TextureImpl.bindNone(); + currentColor.bind(); + + if (fit) { + ShapeRenderer.textureFit(shape, image, scaleX, scaleY); + } else { + ShapeRenderer.texture(shape, image, scaleX, scaleY); + } + + postdraw(); + } + + /** + * Draw the the given shape filled in with a texture + * + * @param shape + * The shape to texture. + * @param image + * The image to tile across the shape + * @param scaleX + * The scale to apply on the x axis for texturing + * @param scaleY + * The scale to apply on the y axis for texturing + * @param fill + * The shape fill to apply + */ + public void texture(Shape shape, Image image, float scaleX, float scaleY, + ShapeFill fill) { + predraw(); + TextureImpl.bindNone(); + currentColor.bind(); + + ShapeRenderer.texture(shape, image, scaleX, scaleY, fill); + + postdraw(); + } + + /** + * Draw a rectangle to the canvas in the current colour + * + * @param x1 + * The x coordinate of the top left corner + * @param y1 + * The y coordinate of the top left corner + * @param width + * The width of the rectangle to draw + * @param height + * The height of the rectangle to draw + */ + public void drawRect(float x1, float y1, float width, float height) { + float lineWidth = getLineWidth(); + + drawLine(x1,y1,x1+width,y1); + drawLine(x1+width,y1,x1+width,y1+height); + drawLine(x1+width,y1+height,x1,y1+height); + drawLine(x1,y1+height,x1,y1); + } + + /** + * Clear the clipping being applied. This will allow graphics to be drawn + * anywhere on the screen + */ + public void clearClip() { + clip = null; + predraw(); + GL.glDisable(SGL.GL_SCISSOR_TEST); + postdraw(); + } + + /** + * Set clipping that controls which areas of the world will be drawn to. + * Note that world clip is different from standard screen clip in that it's + * defined in the space of the current world coordinate - i.e. it's affected + * by translate, rotate, scale etc. + * + * @param x + * The x coordinate of the top left corner of the allowed area + * @param y + * The y coordinate of the top left corner of the allowed area + * @param width + * The width of the allowed area + * @param height + * The height of the allowed area + */ + public void setWorldClip(float x, float y, float width, float height) { + predraw(); + worldClipRecord = new Rectangle(x, y, width, height); + + GL.glEnable(SGL.GL_CLIP_PLANE0); + worldClip.put(1).put(0).put(0).put(-x).flip(); + GL.glClipPlane(SGL.GL_CLIP_PLANE0, worldClip); + GL.glEnable(SGL.GL_CLIP_PLANE1); + worldClip.put(-1).put(0).put(0).put(x + width).flip(); + GL.glClipPlane(SGL.GL_CLIP_PLANE1, worldClip); + + GL.glEnable(SGL.GL_CLIP_PLANE2); + worldClip.put(0).put(1).put(0).put(-y).flip(); + GL.glClipPlane(SGL.GL_CLIP_PLANE2, worldClip); + GL.glEnable(SGL.GL_CLIP_PLANE3); + worldClip.put(0).put(-1).put(0).put(y + height).flip(); + GL.glClipPlane(SGL.GL_CLIP_PLANE3, worldClip); + postdraw(); + } + + /** + * Clear world clipping setup. This does not effect screen clipping + */ + public void clearWorldClip() { + predraw(); + worldClipRecord = null; + GL.glDisable(SGL.GL_CLIP_PLANE0); + GL.glDisable(SGL.GL_CLIP_PLANE1); + GL.glDisable(SGL.GL_CLIP_PLANE2); + GL.glDisable(SGL.GL_CLIP_PLANE3); + postdraw(); + } + + /** + * Set the world clip to be applied + * + * @see #setWorldClip(float, float, float, float) + * @param clip + * The area still visible + */ + public void setWorldClip(Rectangle clip) { + if (clip == null) { + clearWorldClip(); + } else { + setWorldClip(clip.getX(), clip.getY(), clip.getWidth(), clip + .getHeight()); + } + } + + /** + * Get the last set world clip or null of the world clip isn't set + * + * @return The last set world clip rectangle + */ + public Rectangle getWorldClip() { + return worldClipRecord; + } + + /** + * Set the clipping to apply to the drawing. Note that this clipping takes + * no note of the transforms that have been applied to the context and is + * always in absolute screen space coordinates. + * + * @param x + * The x coordinate of the top left corner of the allowed area + * @param y + * The y coordinate of the top left corner of the allowed area + * @param width + * The width of the allowed area + * @param height + * The height of the allowed area + */ + public void setClip(int x, int y, int width, int height) { + predraw(); + + if (clip == null) { + GL.glEnable(SGL.GL_SCISSOR_TEST); + clip = new Rectangle(x, y, width, height); + } else { + clip.setBounds(x,y,width,height); + } + + GL.glScissor(x, screenHeight - y - height, width, height); + postdraw(); + } + + /** + * Set the clipping to apply to the drawing. Note that this clipping takes + * no note of the transforms that have been applied to the context and is + * always in absolute screen space coordinates. + * + * @param rect + * The rectangle describing the clipped area in screen + * coordinates + */ + public void setClip(Rectangle rect) { + if (rect == null) { + clearClip(); + return; + } + + setClip((int) rect.getX(), (int) rect.getY(), (int) rect.getWidth(), + (int) rect.getHeight()); + } + + /** + * Return the currently applied clipping rectangle + * + * @return The current applied clipping rectangle or null if no clipping is + * applied + */ + public Rectangle getClip() { + return clip; + } + + /** + * Tile a rectangle with a pattern specifing the offset from the top corner + * that one tile should match + * + * @param x + * The x coordinate of the rectangle + * @param y + * The y coordinate of the rectangle + * @param width + * The width of the rectangle + * @param height + * The height of the rectangle + * @param pattern + * The image to tile across the rectangle + * @param offX + * The offset on the x axis from the top left corner + * @param offY + * The offset on the y axis from the top left corner + */ + public void fillRect(float x, float y, float width, float height, + Image pattern, float offX, float offY) { + int cols = ((int) Math.ceil(width / pattern.getWidth())) + 2; + int rows = ((int) Math.ceil(height / pattern.getHeight())) + 2; + + Rectangle preClip = getWorldClip(); + setWorldClip(x, y, width, height); + + predraw(); + // Draw all the quads we need + for (int c = 0; c < cols; c++) { + for (int r = 0; r < rows; r++) { + pattern.draw(c * pattern.getWidth() + x - offX, r + * pattern.getHeight() + y - offY); + } + } + postdraw(); + + setWorldClip(preClip); + } + + /** + * Fill a rectangle on the canvas in the current color + * + * @param x1 + * The x coordinate of the top left corner + * @param y1 + * The y coordinate of the top left corner + * @param width + * The width of the rectangle to fill + * @param height + * The height of the rectangle to fill + */ + public void fillRect(float x1, float y1, float width, float height) { + predraw(); + TextureImpl.bindNone(); + currentColor.bind(); + + GL.glBegin(SGL.GL_QUADS); + GL.glVertex2f(x1, y1); + GL.glVertex2f(x1 + width, y1); + GL.glVertex2f(x1 + width, y1 + height); + GL.glVertex2f(x1, y1 + height); + GL.glEnd(); + postdraw(); + } + + /** + * Draw an oval to the canvas + * + * @param x1 + * The x coordinate of the top left corner of a box containing + * the oval + * @param y1 + * The y coordinate of the top left corner of a box containing + * the oval + * @param width + * The width of the oval + * @param height + * The height of the oval + */ + public void drawOval(float x1, float y1, float width, float height) { + drawOval(x1, y1, width, height, DEFAULT_SEGMENTS); + } + + /** + * Draw an oval to the canvas + * + * @param x1 + * The x coordinate of the top left corner of a box containing + * the oval + * @param y1 + * The y coordinate of the top left corner of a box containing + * the oval + * @param width + * The width of the oval + * @param height + * The height of the oval + * @param segments + * The number of line segments to use when drawing the oval + */ + public void drawOval(float x1, float y1, float width, float height, + int segments) { + drawArc(x1, y1, width, height, segments, 0, 360); + } + + /** + * Draw an oval to the canvas + * + * @param x1 + * The x coordinate of the top left corner of a box containing + * the arc + * @param y1 + * The y coordinate of the top left corner of a box containing + * the arc + * @param width + * The width of the arc + * @param height + * The height of the arc + * @param start + * The angle the arc starts at + * @param end + * The angle the arc ends at + */ + public void drawArc(float x1, float y1, float width, float height, + float start, float end) { + drawArc(x1, y1, width, height, DEFAULT_SEGMENTS, start, end); + } + + /** + * Draw an oval to the canvas + * + * @param x1 + * The x coordinate of the top left corner of a box containing + * the arc + * @param y1 + * The y coordinate of the top left corner of a box containing + * the arc + * @param width + * The width of the arc + * @param height + * The height of the arc + * @param segments + * The number of line segments to use when drawing the arc + * @param start + * The angle the arc starts at + * @param end + * The angle the arc ends at + */ + public void drawArc(float x1, float y1, float width, float height, + int segments, float start, float end) { + predraw(); + TextureImpl.bindNone(); + currentColor.bind(); + + while (end < start) { + end += 360; + } + + float cx = x1 + (width / 2.0f); + float cy = y1 + (height / 2.0f); + + LSR.start(); + int step = 360 / segments; + + for (int a = (int) start; a < (int) (end + step); a += step) { + float ang = a; + if (ang > end) { + ang = end; + } + float x = (float) (cx + (FastTrig.cos(Math.toRadians(ang)) * width / 2.0f)); + float y = (float) (cy + (FastTrig.sin(Math.toRadians(ang)) * height / 2.0f)); + + LSR.vertex(x,y); + } + LSR.end(); + postdraw(); + } + + /** + * Fill an oval to the canvas + * + * @param x1 + * The x coordinate of the top left corner of a box containing + * the oval + * @param y1 + * The y coordinate of the top left corner of a box containing + * the oval + * @param width + * The width of the oval + * @param height + * The height of the oval + */ + public void fillOval(float x1, float y1, float width, float height) { + fillOval(x1, y1, width, height, DEFAULT_SEGMENTS); + } + + /** + * Fill an oval to the canvas + * + * @param x1 + * The x coordinate of the top left corner of a box containing + * the oval + * @param y1 + * The y coordinate of the top left corner of a box containing + * the oval + * @param width + * The width of the oval + * @param height + * The height of the oval + * @param segments + * The number of line segments to use when filling the oval + */ + public void fillOval(float x1, float y1, float width, float height, + int segments) { + fillArc(x1, y1, width, height, segments, 0, 360); + } + + /** + * Fill an arc to the canvas (a wedge) + * + * @param x1 + * The x coordinate of the top left corner of a box containing + * the arc + * @param y1 + * The y coordinate of the top left corner of a box containing + * the arc + * @param width + * The width of the arc + * @param height + * The height of the arc + * @param start + * The angle the arc starts at + * @param end + * The angle the arc ends at + */ + public void fillArc(float x1, float y1, float width, float height, + float start, float end) { + fillArc(x1, y1, width, height, DEFAULT_SEGMENTS, start, end); + } + + /** + * Fill an arc to the canvas (a wedge) + * + * @param x1 + * The x coordinate of the top left corner of a box containing + * the arc + * @param y1 + * The y coordinate of the top left corner of a box containing + * the arc + * @param width + * The width of the arc + * @param height + * The height of the arc + * @param segments + * The number of line segments to use when filling the arc + * @param start + * The angle the arc starts at + * @param end + * The angle the arc ends at + */ + public void fillArc(float x1, float y1, float width, float height, + int segments, float start, float end) { + predraw(); + TextureImpl.bindNone(); + currentColor.bind(); + + while (end < start) { + end += 360; + } + + float cx = x1 + (width / 2.0f); + float cy = y1 + (height / 2.0f); + + GL.glBegin(SGL.GL_TRIANGLE_FAN); + int step = 360 / segments; + + GL.glVertex2f(cx, cy); + + for (int a = (int) start; a < (int) (end + step); a += step) { + float ang = a; + if (ang > end) { + ang = end; + } + + float x = (float) (cx + (FastTrig.cos(Math.toRadians(ang)) * width / 2.0f)); + float y = (float) (cy + (FastTrig.sin(Math.toRadians(ang)) * height / 2.0f)); + + GL.glVertex2f(x, y); + } + GL.glEnd(); + + if (antialias) { + GL.glBegin(SGL.GL_TRIANGLE_FAN); + GL.glVertex2f(cx, cy); + if (end != 360) { + end -= 10; + } + + for (int a = (int) start; a < (int) (end + step); a += step) { + float ang = a; + if (ang > end) { + ang = end; + } + + float x = (float) (cx + (FastTrig.cos(Math.toRadians(ang + 10)) + * width / 2.0f)); + float y = (float) (cy + (FastTrig.sin(Math.toRadians(ang + 10)) + * height / 2.0f)); + + GL.glVertex2f(x, y); + } + GL.glEnd(); + } + + postdraw(); + } + + /** + * Draw a rounded rectangle + * + * @param x + * The x coordinate of the top left corner of the rectangle + * @param y + * The y coordinate of the top left corner of the rectangle + * @param width + * The width of the rectangle + * @param height + * The height of the rectangle + * @param cornerRadius + * The radius of the rounded edges on the corners + */ + public void drawRoundRect(float x, float y, float width, float height, + int cornerRadius) { + drawRoundRect(x, y, width, height, cornerRadius, DEFAULT_SEGMENTS); + } + + /** + * Draw a rounded rectangle + * + * @param x + * The x coordinate of the top left corner of the rectangle + * @param y + * The y coordinate of the top left corner of the rectangle + * @param width + * The width of the rectangle + * @param height + * The height of the rectangle + * @param cornerRadius + * The radius of the rounded edges on the corners + * @param segs + * The number of segments to make the corners out of + */ + public void drawRoundRect(float x, float y, float width, float height, + int cornerRadius, int segs) { + if (cornerRadius < 0) + throw new IllegalArgumentException("corner radius must be > 0"); + if (cornerRadius == 0) { + drawRect(x, y, width, height); + return; + } + + int mr = (int) Math.min(width, height) / 2; + // make sure that w & h are larger than 2*cornerRadius + if (cornerRadius > mr) { + cornerRadius = mr; + } + + drawLine(x + cornerRadius, y, x + width - cornerRadius, y); + drawLine(x, y + cornerRadius, x, y + height - cornerRadius); + drawLine(x + width, y + cornerRadius, x + width, y + height + - cornerRadius); + drawLine(x + cornerRadius, y + height, x + width - cornerRadius, y + + height); + + float d = cornerRadius * 2; + // bottom right - 0, 90 + drawArc(x + width - d, y + height - d, d, d, segs, 0, 90); + // bottom left - 90, 180 + drawArc(x, y + height - d, d, d, segs, 90, 180); + // top right - 270, 360 + drawArc(x + width - d, y, d, d, segs, 270, 360); + // top left - 180, 270 + drawArc(x, y, d, d, segs, 180, 270); + } + + /** + * Fill a rounded rectangle + * + * @param x + * The x coordinate of the top left corner of the rectangle + * @param y + * The y coordinate of the top left corner of the rectangle + * @param width + * The width of the rectangle + * @param height + * The height of the rectangle + * @param cornerRadius + * The radius of the rounded edges on the corners + */ + public void fillRoundRect(float x, float y, float width, float height, + int cornerRadius) { + fillRoundRect(x, y, width, height, cornerRadius, DEFAULT_SEGMENTS); + } + + /** + * Fill a rounded rectangle + * + * @param x + * The x coordinate of the top left corner of the rectangle + * @param y + * The y coordinate of the top left corner of the rectangle + * @param width + * The width of the rectangle + * @param height + * The height of the rectangle + * @param cornerRadius + * The radius of the rounded edges on the corners + * @param segs + * The number of segments to make the corners out of + */ + public void fillRoundRect(float x, float y, float width, float height, + int cornerRadius, int segs) { + if (cornerRadius < 0) + throw new IllegalArgumentException("corner radius must be > 0"); + if (cornerRadius == 0) { + fillRect(x, y, width, height); + return; + } + + int mr = (int) Math.min(width, height) / 2; + // make sure that w & h are larger than 2*cornerRadius + if (cornerRadius > mr) { + cornerRadius = mr; + } + + float d = cornerRadius * 2; + + fillRect(x + cornerRadius, y, width - d, cornerRadius); + fillRect(x, y + cornerRadius, cornerRadius, height - d); + fillRect(x + width - cornerRadius, y + cornerRadius, cornerRadius, + height - d); + fillRect(x + cornerRadius, y + height - cornerRadius, width - d, + cornerRadius); + fillRect(x + cornerRadius, y + cornerRadius, width - d, height - d); + + // bottom right - 0, 90 + fillArc(x + width - d, y + height - d, d, d, segs, 0, 90); + // bottom left - 90, 180 + fillArc(x, y + height - d, d, d, segs, 90, 180); + // top right - 270, 360 + fillArc(x + width - d, y, d, d, segs, 270, 360); + // top left - 180, 270 + fillArc(x, y, d, d, segs, 180, 270); + } + + /** + * Set the with of the line to be used when drawing line based primitives + * + * @param width + * The width of the line to be used when drawing line based + * primitives + */ + public void setLineWidth(float width) { + predraw(); + this.lineWidth = width; + LSR.setWidth(width); + GL.glPointSize(width); + postdraw(); + } + + /** + * Get the width of lines being drawn in this context + * + * @return The width of lines being draw in this context + */ + public float getLineWidth() { + return lineWidth; + } + + /** + * Reset the line width in use to the default for this graphics context + */ + public void resetLineWidth() { + predraw(); + + Renderer.getLineStripRenderer().setWidth(1.0f); + GL.glLineWidth(1.0f); + GL.glPointSize(1.0f); + + postdraw(); + } + + /** + * Indicate if we should antialias as we draw primitives + * + * @param anti + * True if we should antialias + */ + public void setAntiAlias(boolean anti) { + predraw(); + antialias = anti; + LSR.setAntiAlias(anti); + if (anti) { + GL.glEnable(SGL.GL_POLYGON_SMOOTH); + } else { + GL.glDisable(SGL.GL_POLYGON_SMOOTH); + } + postdraw(); + } + + /** + * True if antialiasing has been turned on for this graphics context + * + * @return True if antialiasing has been turned on for this graphics context + */ + public boolean isAntiAlias() { + return antialias; + } + + /** + * Draw a string to the screen using the current font + * + * @param str + * The string to draw + * @param x + * The x coordinate to draw the string at + * @param y + * The y coordinate to draw the string at + */ + public void drawString(String str, float x, float y) { + predraw(); + font.drawString(x, y, str, currentColor); + postdraw(); + } + + /** + * Draw an image to the screen + * + * @param image + * The image to draw to the screen + * @param x + * The x location at which to draw the image + * @param y + * The y location at which to draw the image + * @param col + * The color to apply to the image as a filter + */ + public void drawImage(Image image, float x, float y, Color col) { + predraw(); + image.draw(x, y, col); + currentColor.bind(); + postdraw(); + } + + /** + * Draw an animation to this graphics context + * + * @param anim + * The animation to be drawn + * @param x + * The x position to draw the animation at + * @param y + * The y position to draw the animation at + */ + public void drawAnimation(Animation anim, float x, float y) { + drawAnimation(anim, x, y, Color.white); + } + + /** + * Draw an animation to this graphics context + * + * @param anim + * The animation to be drawn + * @param x + * The x position to draw the animation at + * @param y + * The y position to draw the animation at + * @param col + * The color to apply to the animation as a filter + */ + public void drawAnimation(Animation anim, float x, float y, Color col) { + predraw(); + anim.draw(x, y, col); + currentColor.bind(); + postdraw(); + } + + /** + * Draw an image to the screen + * + * @param image + * The image to draw to the screen + * @param x + * The x location at which to draw the image + * @param y + * The y location at which to draw the image + */ + public void drawImage(Image image, float x, float y) { + drawImage(image, x, y, Color.white); + } + + /** + * Draw a section of an image at a particular location and scale on the + * screen + * + * @param image + * The image to draw a section of + * @param x + * The x position to draw the image + * @param y + * The y position to draw the image + * @param x2 + * The x position of the bottom right corner of the drawn image + * @param y2 + * The y position of the bottom right corner of the drawn image + * @param srcx + * The x position of the rectangle to draw from this image (i.e. + * relative to the image) + * @param srcy + * The y position of the rectangle to draw from this image (i.e. + * relative to the image) + * @param srcx2 + * The x position of the bottom right cornder of rectangle to + * draw from this image (i.e. relative to the image) + * @param srcy2 + * The t position of the bottom right cornder of rectangle to + * draw from this image (i.e. relative to the image) + */ + public void drawImage(Image image, float x, float y, float x2, float y2, + float srcx, float srcy, float srcx2, float srcy2) { + predraw(); + image.draw(x, y, x2, y2, srcx, srcy, srcx2, srcy2); + currentColor.bind(); + postdraw(); + } + + /** + * Draw a section of an image at a particular location and scale on the + * screen + * + * @param image + * The image to draw a section of + * @param x + * The x position to draw the image + * @param y + * The y position to draw the image + * @param srcx + * The x position of the rectangle to draw from this image (i.e. + * relative to the image) + * @param srcy + * The y position of the rectangle to draw from this image (i.e. + * relative to the image) + * @param srcx2 + * The x position of the bottom right cornder of rectangle to + * draw from this image (i.e. relative to the image) + * @param srcy2 + * The t position of the bottom right cornder of rectangle to + * draw from this image (i.e. relative to the image) + */ + public void drawImage(Image image, float x, float y, float srcx, + float srcy, float srcx2, float srcy2) { + drawImage(image, x, y, x + image.getWidth(), y + image.getHeight(), + srcx, srcy, srcx2, srcy2); + } + + /** + * Copy an area of the rendered screen into an image. The width and height + * of the area are assumed to match that of the image + * + * @param target + * The target image + * @param x + * The x position to copy from + * @param y + * The y position to copy from + */ + public void copyArea(Image target, int x, int y) { + int format = target.getTexture().hasAlpha() ? SGL.GL_RGBA : SGL.GL_RGB; + target.bind(); + GL.glCopyTexImage2D(SGL.GL_TEXTURE_2D, 0, format, x, screenHeight + - (y + target.getHeight()), target.getTexture() + .getTextureWidth(), target.getTexture().getTextureHeight(), 0); + target.ensureInverted(); + } + + /** + * Translate an unsigned int into a signed integer + * + * @param b + * The byte to convert + * @return The integer value represented by the byte + */ + private int translate(byte b) { + if (b < 0) { + return 256 + b; + } + + return b; + } + + /** + * Get the colour of a single pixel in this graphics context + * + * @param x + * The x coordinate of the pixel to read + * @param y + * The y coordinate of the pixel to read + * @return The colour of the pixel at the specified location + */ + public Color getPixel(int x, int y) { + predraw(); + GL.glReadPixels(x, screenHeight - y, 1, 1, SGL.GL_RGBA, + SGL.GL_UNSIGNED_BYTE, readBuffer); + postdraw(); + + return new Color(translate(readBuffer.get(0)), translate(readBuffer + .get(1)), translate(readBuffer.get(2)), translate(readBuffer + .get(3))); + } + + /** + * Get an ara of pixels as RGBA values into a buffer + * + * @param x The x position in the context to grab from + * @param y The y position in the context to grab from + * @param width The width of the area to grab from + * @param height The hiehgt of the area to grab from + * @param target The target buffer to grab into + */ + public void getArea(int x, int y, int width, int height, ByteBuffer target) + { + if (target.capacity() < width * height * 4) + { + throw new IllegalArgumentException("Byte buffer provided to get area is not big enough"); + } + + predraw(); + GL.glReadPixels(x, screenHeight - y - height, width, height, SGL.GL_RGBA, + SGL.GL_UNSIGNED_BYTE, target); + postdraw(); + } + + /** + * Draw a section of an image at a particular location and scale on the + * screen + * + * @param image + * The image to draw a section of + * @param x + * The x position to draw the image + * @param y + * The y position to draw the image + * @param x2 + * The x position of the bottom right corner of the drawn image + * @param y2 + * The y position of the bottom right corner of the drawn image + * @param srcx + * The x position of the rectangle to draw from this image (i.e. + * relative to the image) + * @param srcy + * The y position of the rectangle to draw from this image (i.e. + * relative to the image) + * @param srcx2 + * The x position of the bottom right cornder of rectangle to + * draw from this image (i.e. relative to the image) + * @param srcy2 + * The t position of the bottom right cornder of rectangle to + * draw from this image (i.e. relative to the image) + * @param col + * The color to apply to the image as a filter + */ + public void drawImage(Image image, float x, float y, float x2, float y2, + float srcx, float srcy, float srcx2, float srcy2, Color col) { + predraw(); + image.draw(x, y, x2, y2, srcx, srcy, srcx2, srcy2, col); + currentColor.bind(); + postdraw(); + } + + /** + * Draw a section of an image at a particular location and scale on the + * screen + * + * @param image + * The image to draw a section of + * @param x + * The x position to draw the image + * @param y + * The y position to draw the image + * @param srcx + * The x position of the rectangle to draw from this image (i.e. + * relative to the image) + * @param srcy + * The y position of the rectangle to draw from this image (i.e. + * relative to the image) + * @param srcx2 + * The x position of the bottom right cornder of rectangle to + * draw from this image (i.e. relative to the image) + * @param srcy2 + * The t position of the bottom right cornder of rectangle to + * draw from this image (i.e. relative to the image) + * @param col + * The color to apply to the image as a filter + */ + public void drawImage(Image image, float x, float y, float srcx, + float srcy, float srcx2, float srcy2, Color col) { + drawImage(image, x, y, x + image.getWidth(), y + image.getHeight(), + srcx, srcy, srcx2, srcy2, col); + } + + /** + * Draw a line with a gradient between the two points. + * + * @param x1 + * The starting x position to draw the line + * @param y1 + * The starting y position to draw the line + * @param red1 + * The starting position's shade of red + * @param green1 + * The starting position's shade of green + * @param blue1 + * The starting position's shade of blue + * @param alpha1 + * The starting position's alpha value + * @param x2 + * The ending x position to draw the line + * @param y2 + * The ending y position to draw the line + * @param red2 + * The ending position's shade of red + * @param green2 + * The ending position's shade of green + * @param blue2 + * The ending position's shade of blue + * @param alpha2 + * The ending position's alpha value + */ + public void drawGradientLine(float x1, float y1, float red1, float green1, + float blue1, float alpha1, float x2, float y2, float red2, + float green2, float blue2, float alpha2) { + predraw(); + + TextureImpl.bindNone(); + + GL.glBegin(SGL.GL_LINES); + + GL.glColor4f(red1, green1, blue1, alpha1); + GL.glVertex2f(x1, y1); + + GL.glColor4f(red2, green2, blue2, alpha2); + GL.glVertex2f(x2, y2); + + GL.glEnd(); + + postdraw(); + } + + /** + * Draw a line with a gradient between the two points. + * + * @param x1 + * The starting x position to draw the line + * @param y1 + * The starting y position to draw the line + * @param Color1 + * The starting position's color + * @param x2 + * The ending x position to draw the line + * @param y2 + * The ending y position to draw the line + * @param Color2 + * The ending position's color + */ + public void drawGradientLine(float x1, float y1, Color Color1, float x2, + float y2, Color Color2) { + predraw(); + + TextureImpl.bindNone(); + + GL.glBegin(SGL.GL_LINES); + + Color1.bind(); + GL.glVertex2f(x1, y1); + + Color2.bind(); + GL.glVertex2f(x2, y2); + + GL.glEnd(); + + postdraw(); + } + + /** + * Push the current state of the transform from this graphics contexts + * onto the underlying graphics stack's transform stack. An associated + * popTransform() must be performed to restore the state before the end + * of the rendering loop. + */ + public void pushTransform() { + predraw(); + + FloatBuffer buffer; + if (stackIndex >= stack.size()) { + buffer = BufferUtils.createFloatBuffer(18); + stack.add(buffer); + } else { + buffer = (FloatBuffer) stack.get(stackIndex); + } + + GL.glGetFloat(GL11.GL_MODELVIEW_MATRIX, buffer); + buffer.put(16, sx); + buffer.put(17, sy); + stackIndex++; + + postdraw(); + } + + /** + * Pop a previously pushed transform from the stack to the current. This should + * only be called if a transform has been previously pushed. + */ + public void popTransform() { + if (stackIndex == 0) { + throw new RuntimeException("Attempt to pop a transform that hasn't be pushed"); + } + + predraw(); + + stackIndex--; + FloatBuffer oldBuffer = (FloatBuffer) stack.get(stackIndex); + GL.glLoadMatrix(oldBuffer); + sx = oldBuffer.get(16); + sy = oldBuffer.get(17); + + postdraw(); + } + + /** + * Dispose this graphics context, this will release any underlying resourses. However + * this will also invalidate it's use + */ + public void destroy() { + + } +} diff --git a/ext/slick/src/org/newdawn/slick/Image.java b/ext/slick/src/org/newdawn/slick/Image.java new file mode 100644 index 0000000..128b13b --- /dev/null +++ b/ext/slick/src/org/newdawn/slick/Image.java @@ -0,0 +1,1287 @@ +package org.newdawn.slick; + +import java.io.IOException; +import java.io.InputStream; + +import org.lwjgl.opengl.EXTSecondaryColor; +import org.lwjgl.opengl.EXTTextureMirrorClamp; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GLContext; +import org.newdawn.slick.opengl.ImageData; +import org.newdawn.slick.opengl.InternalTextureLoader; +import org.newdawn.slick.opengl.Texture; +import org.newdawn.slick.opengl.TextureImpl; +import org.newdawn.slick.opengl.pbuffer.GraphicsFactory; +import org.newdawn.slick.opengl.renderer.Renderer; +import org.newdawn.slick.opengl.renderer.SGL; +import org.newdawn.slick.util.Log; + +/** + * An image loaded from a file and renderable to the canvas + * + * @author kevin + */ +public class Image implements Renderable { + /** The top left corner identifier */ + public static final int TOP_LEFT = 0; + /** The top right corner identifier */ + public static final int TOP_RIGHT = 1; + /** The bottom right corner identifier */ + public static final int BOTTOM_RIGHT = 2; + /** The bottom left corner identifier */ + public static final int BOTTOM_LEFT = 3; + + /** The renderer to use for all GL operations */ + protected static SGL GL = Renderer.get(); + + /** The sprite sheet currently in use */ + protected static Image inUse; + /** Use Linear Filtering */ + public static final int FILTER_LINEAR = 1; + /** Use Nearest Filtering */ + public static final int FILTER_NEAREST = 2; + + /** The OpenGL texture for this image */ + protected Texture texture; + /** The width of the image */ + protected int width; + /** The height of the image */ + protected int height; + /** The texture coordinate width to use to find our image */ + protected float textureWidth; + /** The texture coordinate height to use to find our image */ + protected float textureHeight; + /** The x texture offset to use to find our image */ + protected float textureOffsetX; + /** The y texture offset to use to find our image */ + protected float textureOffsetY; + /** Angle to rotate the image to. */ + protected float angle; + /** The alpha to draw the image at */ + protected float alpha = 1.0f; + /** The name given for the image */ + protected String ref; + /** True if this image's state has been initialised */ + protected boolean inited = false; + /** A pixelData holding the pixel data if it's been read for this texture */ + protected byte[] pixelData; + /** True if the image has been destroyed */ + protected boolean destroyed; + + /** The x coordinate of the centre of rotation */ + protected float centerX; + /** The y coordinate of the centre of rotation */ + protected float centerY; + + /** A meaningful name provided by the user of the image to tag it */ + protected String name; + + /** The colours for each of the corners */ + protected Color[] corners; + /** The OpenGL max filter */ + private int filter = SGL.GL_LINEAR; + + /** True if the image should be flipped vertically */ + private boolean flipped; + /** The transparent colour set if any */ + private Color transparent; + + /** + * Create a texture as a copy of another + * + * @param other The other texture to copy + */ + protected Image(Image other) { + this.width = other.getWidth(); + this.height = other.getHeight(); + this.texture = other.texture; + this.textureWidth = other.textureWidth; + this.textureHeight = other.textureHeight; + this.ref = other.ref; + this.textureOffsetX = other.textureOffsetX; + this.textureOffsetY = other.textureOffsetY; + + centerX = width / 2; + centerY = height / 2; + inited = true; + } + + /** + * Cloning constructor - only used internally. + */ + protected Image() { + } + + /** + * Creates an image using the specified texture + * + * @param texture + * The texture to use + */ + public Image(Texture texture) { + this.texture = texture; + ref = texture.toString(); + clampTexture(); + } + + /** + * Create an image based on a file at the specified location + * + * @param ref + * The location of the image file to load + * @throws SlickException + * Indicates a failure to load the image + */ + public Image(String ref) throws SlickException { + this(ref, false); + } + + /** + * Create an image based on a file at the specified location + * + * @param ref The location of the image file to load + * @param trans The color to be treated as transparent + * @throws SlickException Indicates a failure to load the image + */ + public Image(String ref, Color trans) throws SlickException { + this(ref, false, FILTER_LINEAR, trans); + } + + /** + * Create an image based on a file at the specified location + * + * @param ref The location of the image file to load + * @param flipped True if the image should be flipped on the y-axis on load + * @throws SlickException Indicates a failure to load the image + */ + public Image(String ref, boolean flipped) throws SlickException { + this(ref, flipped, FILTER_LINEAR); + } + + /** + * Create an image based on a file at the specified location + * + * @param ref The location of the image file to load + * @param flipped True if the image should be flipped on the y-axis on load + * @param filter The filtering method to use when scaling this image + * @throws SlickException Indicates a failure to load the image + */ + public Image(String ref, boolean flipped, int filter) throws SlickException { + this(ref, flipped, filter, null); + } + + /** + * Create an image based on a file at the specified location + * + * @param ref The location of the image file to load + * @param flipped True if the image should be flipped on the y-axis on load + * @param f The filtering method to use when scaling this image + * @param transparent The color to treat as transparent + * @throws SlickException Indicates a failure to load the image + */ + public Image(String ref, boolean flipped, int f, Color transparent) throws SlickException { + this.filter = f == FILTER_LINEAR ? SGL.GL_LINEAR : SGL.GL_NEAREST; + this.transparent = transparent; + this.flipped = flipped; + + try { + this.ref = ref; + int[] trans = null; + if (transparent != null) { + trans = new int[3]; + trans[0] = (int) (transparent.r * 255); + trans[1] = (int) (transparent.g * 255); + trans[2] = (int) (transparent.b * 255); + } + texture = InternalTextureLoader.get().getTexture(ref, flipped, filter, trans); + } catch (IOException e) { + Log.error(e); + throw new SlickException("Failed to load image from: "+ref, e); + } + } + + /** + * Set the image filtering to be used. This will cause the image + * to be reloaded. + * + * @param f The filtering mode to use + * @throws SlickException Indicates a failure to revalidate the image source + */ + public void setFilter(int f) throws SlickException { + this.filter = f == FILTER_LINEAR ? SGL.GL_LINEAR : SGL.GL_NEAREST; + + try { + int[] trans = null; + if (transparent != null) { + trans = new int[3]; + trans[0] = (int) (transparent.r * 255); + trans[1] = (int) (transparent.g * 255); + trans[2] = (int) (transparent.b * 255); + } + texture = InternalTextureLoader.get().getTexture(ref, flipped, filter, trans); + } catch (IOException e) { + Log.error(e); + throw new SlickException("Failed to load image from: "+ref, e); + } + } + /** + * Create an empty image + * + * @param width The width of the image + * @param height The height of the image + * @throws SlickException Indicates a failure to create the underlying resource + */ + public Image(int width, int height) throws SlickException { + ref = super.toString(); + + try { + texture = InternalTextureLoader.get().createTexture(width, height); + } catch (IOException e) { + Log.error(e); + throw new SlickException("Failed to create empty image "+width+"x"+height); + } + + init(); + } + + /** + * Create an image based on a file at the specified location + * + * @param in The input stream to read the image from + * @param ref The name that should be assigned to the image + * @param flipped True if the image should be flipped on the y-axis on load + * @throws SlickException Indicates a failure to load the image + */ + public Image(InputStream in, String ref, boolean flipped) throws SlickException { + this(in, ref, flipped, FILTER_LINEAR); + } + + /** + * Create an image based on a file at the specified location + * + * @param in The input stream to read the image from + * @param ref The name that should be assigned to the image + * @param flipped True if the image should be flipped on the y-axis on load + * @param filter The filter to use when scaling this image + * @throws SlickException Indicates a failure to load the image + */ + public Image(InputStream in, String ref, boolean flipped,int filter) throws SlickException { + load(in, ref, flipped, filter, null); + } + + /** + * Create an image from a pixelData of pixels + * + * @param buffer The pixelData to use to create the image + */ + Image(ImageBuffer buffer) { + this(buffer, FILTER_LINEAR); + TextureImpl.bindNone(); + } + + /** + * Create an image from a pixelData of pixels + * + * @param buffer The pixelData to use to create the image + * @param filter The filter to use when scaling this image + */ + Image(ImageBuffer buffer, int filter) { + this((ImageData) buffer, filter); + TextureImpl.bindNone(); + } + + /** + * Create an image from a image data source + * + * @param data The pixelData to use to create the image + */ + public Image(ImageData data) { + this(data, FILTER_LINEAR); + } + + /** + * Create an image from a image data source. Note that this method uses + * + * @param data The pixelData to use to create the image + * @param f The filter to use when scaling this image + */ + public Image(ImageData data, int f) { + try { + this.filter = f == FILTER_LINEAR ? SGL.GL_LINEAR : SGL.GL_NEAREST; + texture = InternalTextureLoader.get().getTexture(data, this.filter); + ref = texture.toString(); + } catch (IOException e) { + Log.error(e); + } + } + + /** + * Get the OpenGL image filter in use + * + * @return The filter for magnification + */ + public int getFilter() { + return filter; + } + + /** + * Get the reference to the resource this image was loaded from, if any. Note that + * this can be null in the cases where an image was programatically generated. + * + * @return The reference to the resource the reference was loaded from + */ + public String getResourceReference() { + return ref; + } + + /** + * Set the color of the given corner when this image is rendered. This is + * useful lots of visual effect but especially light maps + * + * @param corner The corner identifier for the corner to be set + * @param r The red component value to set (between 0 and 1) + * @param g The green component value to set (between 0 and 1) + * @param b The blue component value to set (between 0 and 1) + * @param a The alpha component value to set (between 0 and 1) + */ + public void setColor(int corner, float r, float g, float b, float a) { + if (corners == null) { + corners = new Color[] {new Color(1,1,1,1f),new Color(1,1,1,1f), new Color(1,1,1,1f), new Color(1,1,1,1f)}; + } + + corners[corner].r = r; + corners[corner].g = g; + corners[corner].b = b; + corners[corner].a = a; + } + + /** + * Clamp the loaded texture to it's edges + */ + public void clampTexture() { + if (GLContext.getCapabilities().GL_EXT_texture_mirror_clamp) { + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, EXTTextureMirrorClamp.GL_MIRROR_CLAMP_TO_EDGE_EXT); + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, EXTTextureMirrorClamp.GL_MIRROR_CLAMP_TO_EDGE_EXT); + } else { + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP); + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP); + } + } + + /** + * Give this image a meaningful tagging name. Can be used as user data/identifier + * for the image. + * + * @param name The name to assign the image + */ + public void setName(String name) { + this.name = name; + } + + /** + * Return a meaningful tagging name that has been assigned to this image. + * + * @return A name or null if the name hasn't been set + */ + public String getName() { + return name; + } + + /** + * Get a graphics context that can be used to draw to this image + * + * @return The graphics context used to render to this image + * @throws SlickException Indicates a failure to create a graphics context + */ + public Graphics getGraphics() throws SlickException { + return GraphicsFactory.getGraphicsForImage(this); + } + + /** + * Load the image + * + * @param in The input stream to read the image from + * @param ref The name that should be assigned to the image + * @param flipped True if the image should be flipped on the y-axis on load + * @param f The filter to use when scaling this image + * @param transparent The color to treat as transparent + * @throws SlickException Indicates a failure to load the image + */ + private void load(InputStream in, String ref, boolean flipped, int f, Color transparent) throws SlickException { + this.filter = f == FILTER_LINEAR ? SGL.GL_LINEAR : SGL.GL_NEAREST; + + try { + this.ref = ref; + int[] trans = null; + if (transparent != null) { + trans = new int[3]; + trans[0] = (int) (transparent.r * 255); + trans[1] = (int) (transparent.g * 255); + trans[2] = (int) (transparent.b * 255); + } + texture = InternalTextureLoader.get().getTexture(in, ref, flipped, filter, trans); + } catch (IOException e) { + Log.error(e); + throw new SlickException("Failed to load image from: "+ref, e); + } + } + + /** + * Bind to the texture of this image + */ + public void bind() { + texture.bind(); + } + + /** + * Reinitialise internal data + */ + protected void reinit() { + inited = false; + init(); + } + + /** + * Initialise internal data + */ + protected final void init() { + if (inited) { + return; + } + + inited = true; + if (texture != null) { + width = texture.getImageWidth(); + height = texture.getImageHeight(); + textureOffsetX = 0; + textureOffsetY = 0; + textureWidth = texture.getWidth(); + textureHeight = texture.getHeight(); + } + + initImpl(); + + centerX = width / 2; + centerY = height / 2; + } + + /** + * Hook for subclasses to perform initialisation + */ + protected void initImpl() { + + } + + /** + * Draw this image at the current location + */ + public void draw() { + draw(0,0); + } + + /** + * Draw the image based on it's center + * + * @param x The x coordinate to place the image's center at + * @param y The y coordinate to place the image's center at + */ + public void drawCentered(float x, float y) { + draw(x-(getWidth()/2),y-(getHeight()/2)); + } + + /** + * Draw this image at the specified location + * + * @param x The x location to draw the image at + * @param y The y location to draw the image at + */ + public void draw(float x, float y) { + init(); + draw(x,y,width,height); + } + + /** + * Draw this image at the specified location + * + * @param x The x location to draw the image at + * @param y The y location to draw the image at + * @param filter The color to filter with when drawing + */ + public void draw(float x, float y, Color filter) { + init(); + draw(x,y,width,height, filter); + } + + /** + * Draw this image as part of a collection of images + * + * @param x The x location to draw the image at + * @param y The y location to draw the image at + * @param width The width to render the image at + * @param height The height to render the image at + */ + public void drawEmbedded(float x,float y,float width,float height) { + init(); + + if (corners == null) { + GL.glTexCoord2f(textureOffsetX, textureOffsetY); + GL.glVertex3f(x, y, 0); + GL.glTexCoord2f(textureOffsetX, textureOffsetY + textureHeight); + GL.glVertex3f(x, y + height, 0); + GL.glTexCoord2f(textureOffsetX + textureWidth, textureOffsetY + + textureHeight); + GL.glVertex3f(x + width, y + height, 0); + GL.glTexCoord2f(textureOffsetX + textureWidth, textureOffsetY); + GL.glVertex3f(x + width, y, 0); + } else { + corners[TOP_LEFT].bind(); + GL.glTexCoord2f(textureOffsetX, textureOffsetY); + GL.glVertex3f(x, y, 0); + corners[BOTTOM_LEFT].bind(); + GL.glTexCoord2f(textureOffsetX, textureOffsetY + textureHeight); + GL.glVertex3f(x, y + height, 0); + corners[BOTTOM_RIGHT].bind(); + GL.glTexCoord2f(textureOffsetX + textureWidth, textureOffsetY + + textureHeight); + GL.glVertex3f(x + width, y + height, 0); + corners[TOP_RIGHT].bind(); + GL.glTexCoord2f(textureOffsetX + textureWidth, textureOffsetY); + GL.glVertex3f(x + width, y, 0); + } + } + + /** + * Get the x offset in texels into the source texture + * + * @return The x offset + */ + public float getTextureOffsetX() { + init(); + + return textureOffsetX; + } + + /** + * Get the y offset in texels into the source texture + * + * @return The y offset + */ + public float getTextureOffsetY() { + init(); + + return textureOffsetY; + } + + /** + * Get the width in texels into the source texture + * + * @return The width + */ + public float getTextureWidth() { + init(); + + return textureWidth; + } + + /** + * Get the height in texels into the source texture + * + * @return The height + */ + public float getTextureHeight() { + init(); + + return textureHeight; + } + + /** + * Draw the image with a given scale + * + * @param x The x position to draw the image at + * @param y The y position to draw the image at + * @param scale The scaling to apply + */ + public void draw(float x,float y,float scale) { + init(); + draw(x,y,width*scale,height*scale,Color.white); + } + + /** + * Draw the image with a given scale + * + * @param x The x position to draw the image at + * @param y The y position to draw the image at + * @param scale The scaling to apply + * @param filter The colour filter to adapt the image with + */ + public void draw(float x,float y,float scale,Color filter) { + init(); + draw(x,y,width*scale,height*scale,filter); + } + + /** + * Draw this image at a specified location and size + * + * @param x + * The x location to draw the image at + * @param y + * The y location to draw the image at + * @param width + * The width to render the image at + * @param height + * The height to render the image at + */ + public void draw(float x,float y,float width,float height) { + init(); + draw(x,y,width,height,Color.white); + } + + /** + * Draw this image at a specified location and size + * + * @param x The x location to draw the image at + * @param y The y location to draw the image at + * @param hshear The amount to shear the bottom points by horizontally + * @param vshear The amount to shear the right points by vertically + */ + public void drawSheared(float x,float y, float hshear, float vshear) { + Color.white.bind(); + texture.bind(); + + GL.glTranslatef(x, y, 0); + if (angle != 0) { + GL.glTranslatef(centerX, centerY, 0.0f); + GL.glRotatef(angle, 0.0f, 0.0f, 1.0f); + GL.glTranslatef(-centerX, -centerY, 0.0f); + } + + GL.glBegin(SGL.GL_QUADS); + init(); + + GL.glTexCoord2f(textureOffsetX, textureOffsetY); + GL.glVertex3f(0, 0, 0); + GL.glTexCoord2f(textureOffsetX, textureOffsetY + textureHeight); + GL.glVertex3f(hshear, height, 0); + GL.glTexCoord2f(textureOffsetX + textureWidth, textureOffsetY + + textureHeight); + GL.glVertex3f(width + hshear, height + vshear, 0); + GL.glTexCoord2f(textureOffsetX + textureWidth, textureOffsetY); + GL.glVertex3f(width, vshear, 0); + GL.glEnd(); + + if (angle != 0) { + GL.glTranslatef(centerX, centerY, 0.0f); + GL.glRotatef(-angle, 0.0f, 0.0f, 1.0f); + GL.glTranslatef(-centerX, -centerY, 0.0f); + } + GL.glTranslatef(-x, -y, 0); + } + + /** + * Draw this image at a specified location and size + * + * @param x The x location to draw the image at + * @param y The y location to draw the image at + * @param width The width to render the image at + * @param height The height to render the image at + * @param filter The color to filter with while drawing + */ + public void draw(float x,float y,float width,float height,Color filter) { + if (alpha != 1) { + if (filter == null) { + filter = Color.white; + } + + filter = new Color(filter); + filter.a *= alpha; + } + if (filter != null) { + filter.bind(); + } + + texture.bind(); + + GL.glTranslatef(x, y, 0); + if (angle != 0) { + GL.glTranslatef(centerX, centerY, 0.0f); + GL.glRotatef(angle, 0.0f, 0.0f, 1.0f); + GL.glTranslatef(-centerX, -centerY, 0.0f); + } + + GL.glBegin(SGL.GL_QUADS); + drawEmbedded(0,0,width,height); + GL.glEnd(); + + if (angle != 0) { + GL.glTranslatef(centerX, centerY, 0.0f); + GL.glRotatef(-angle, 0.0f, 0.0f, 1.0f); + GL.glTranslatef(-centerX, -centerY, 0.0f); + } + GL.glTranslatef(-x, -y, 0); + } + + /** + * Draw this image at a specified location and size as a silohette + * + * @param x The x location to draw the image at + * @param y The y location to draw the image at + * @param width The width to render the image at + * @param height The height to render the image at + */ + public void drawFlash(float x,float y,float width,float height) { + drawFlash(x,y,width,height,Color.white); + } + + /** + * Set the centre of the rotation when applied to this image + * + * @param x The x coordinate of center of rotation relative to the top left corner of the image + * @param y The y coordinate of center of rotation relative to the top left corner of the image + */ + public void setCenterOfRotation(float x, float y) { + centerX = x; + centerY = y; + } + + /** + * Get the x component of the center of rotation of this image + * + * @return The x component of the center of rotation + */ + public float getCenterOfRotationX() { + init(); + + return centerX; + } + + /** + * Get the y component of the center of rotation of this image + * + * @return The y component of the center of rotation + */ + public float getCenterOfRotationY() { + init(); + + return centerY; + } + + /** + * Draw this image at a specified location and size as a silohette + * + * @param x The x location to draw the image at + * @param y The y location to draw the image at + * @param width The width to render the image at + * @param height The height to render the image at + * @param col The color for the sillohette + */ + public void drawFlash(float x,float y,float width,float height, Color col) { + init(); + + col.bind(); + texture.bind(); + + if (GLContext.getCapabilities().GL_EXT_secondary_color) { + GL.glEnable(EXTSecondaryColor.GL_COLOR_SUM_EXT); + EXTSecondaryColor.glSecondaryColor3ubEXT((byte)(col.r * 255), + (byte)(col.g * 255), + (byte)(col.b * 255)); + } + + GL.glTexEnvi(SGL.GL_TEXTURE_ENV, SGL.GL_TEXTURE_ENV_MODE, SGL.GL_MODULATE); + + GL.glTranslatef(x, y, 0); + if (angle != 0) { + GL.glTranslatef(centerX, centerY, 0.0f); + GL.glRotatef(angle, 0.0f, 0.0f, 1.0f); + GL.glTranslatef(-centerX, -centerY, 0.0f); + } + + GL.glBegin(SGL.GL_QUADS); + drawEmbedded(0,0,width,height); + GL.glEnd(); + + if (angle != 0) { + GL.glTranslatef(centerX, centerY, 0.0f); + GL.glRotatef(-angle, 0.0f, 0.0f, 1.0f); + GL.glTranslatef(-centerX, -centerY, 0.0f); + } + GL.glTranslatef(-x, -y, 0); + + if (GLContext.getCapabilities().GL_EXT_secondary_color) { + GL.glDisable(EXTSecondaryColor.GL_COLOR_SUM_EXT); + } + } + + /** + * Draw this image at a specified location and size in a white silohette + * + * @param x The x location to draw the image at + * @param y The y location to draw the image at + */ + public void drawFlash(float x,float y) { + drawFlash(x,y,getWidth(),getHeight()); + } + + /** + * Set the angle to rotate this image to. The angle will be normalized to + * be 0 <= angle < 360. The image will be rotated around its center. + * + * @param angle The angle to be set + */ + public void setRotation(float angle) { + this.angle = angle % 360.0f; + } + + /** + * Get the current angle of rotation for this image. + * The image will be rotated around its center. + * + * @return The current angle. + */ + public float getRotation() { + return angle; + } + + /** + * Get the alpha value to use when rendering this image + * + * @return The alpha value to use when rendering this image + */ + public float getAlpha() { + return alpha; + } + + /** + * Set the alpha value to use when rendering this image + * + * @param alpha The alpha value to use when rendering this image + */ + public void setAlpha(float alpha) { + this.alpha = alpha; + } + + /** + * Add the angle provided to the current rotation. The angle will be normalized to + * be 0 <= angle < 360. The image will be rotated around its center. + * + * @param angle The angle to add. + */ + public void rotate(float angle) { + this.angle += angle; + this.angle = this.angle % 360; + } + + /** + * Get a sub-part of this image. Note that the create image retains a reference to the + * image data so should anything change it will affect sub-images too. + * + * @param x The x coordinate of the sub-image + * @param y The y coordinate of the sub-image + * @param width The width of the sub-image + * @param height The height of the sub-image + * @return The image represent the sub-part of this image + */ + public Image getSubImage(int x,int y,int width,int height) { + init(); + + float newTextureOffsetX = ((x / (float) this.width) * textureWidth) + textureOffsetX; + float newTextureOffsetY = ((y / (float) this.height) * textureHeight) + textureOffsetY; + float newTextureWidth = ((width / (float) this.width) * textureWidth); + float newTextureHeight = ((height / (float) this.height) * textureHeight); + + Image sub = new Image(); + sub.inited = true; + sub.texture = this.texture; + sub.textureOffsetX = newTextureOffsetX; + sub.textureOffsetY = newTextureOffsetY; + sub.textureWidth = newTextureWidth; + sub.textureHeight = newTextureHeight; + + sub.width = width; + sub.height = height; + sub.ref = ref; + sub.centerX = width / 2; + sub.centerY = height / 2; + + return sub; + } + + /** + * Draw a section of this image at a particular location and scale on the screen + * + * @param x The x position to draw the image + * @param y The y position to draw the image + * @param srcx The x position of the rectangle to draw from this image (i.e. relative to this image) + * @param srcy The y position of the rectangle to draw from this image (i.e. relative to this image) + * @param srcx2 The x position of the bottom right cornder of rectangle to draw from this image (i.e. relative to this image) + * @param srcy2 The t position of the bottom right cornder of rectangle to draw from this image (i.e. relative to this image) + */ + public void draw(float x, float y, float srcx, float srcy, float srcx2, float srcy2) { + draw(x,y,x+width,y+height,srcx,srcy,srcx2,srcy2); + } + + /** + * Draw a section of this image at a particular location and scale on the screen + * + * @param x The x position to draw the image + * @param y The y position to draw the image + * @param x2 The x position of the bottom right corner of the drawn image + * @param y2 The y position of the bottom right corner of the drawn image + * @param srcx The x position of the rectangle to draw from this image (i.e. relative to this image) + * @param srcy The y position of the rectangle to draw from this image (i.e. relative to this image) + * @param srcx2 The x position of the bottom right cornder of rectangle to draw from this image (i.e. relative to this image) + * @param srcy2 The t position of the bottom right cornder of rectangle to draw from this image (i.e. relative to this image) + */ + public void draw(float x, float y, float x2, float y2, float srcx, float srcy, float srcx2, float srcy2) { + draw(x,y,x2,y2,srcx,srcy,srcx2,srcy2,Color.white); + } + + /** + * Draw a section of this image at a particular location and scale on the screen + * + * @param x The x position to draw the image + * @param y The y position to draw the image + * @param x2 The x position of the bottom right corner of the drawn image + * @param y2 The y position of the bottom right corner of the drawn image + * @param srcx The x position of the rectangle to draw from this image (i.e. relative to this image) + * @param srcy The y position of the rectangle to draw from this image (i.e. relative to this image) + * @param srcx2 The x position of the bottom right cornder of rectangle to draw from this image (i.e. relative to this image) + * @param srcy2 The t position of the bottom right cornder of rectangle to draw from this image (i.e. relative to this image) + * @param filter The colour filter to apply when drawing + */ + public void draw(float x, float y, float x2, float y2, float srcx, float srcy, float srcx2, float srcy2, Color filter) { + init(); + + if (alpha != 1) { + if (filter == null) { + filter = Color.white; + } + + filter = new Color(filter); + filter.a *= alpha; + } + filter.bind(); + texture.bind(); + + GL.glTranslatef(x, y, 0); + if (angle != 0) { + GL.glTranslatef(centerX, centerY, 0.0f); + GL.glRotatef(angle, 0.0f, 0.0f, 1.0f); + GL.glTranslatef(-centerX, -centerY, 0.0f); + } + + GL.glBegin(SGL.GL_QUADS); + drawEmbedded(0,0,x2-x,y2-y,srcx,srcy,srcx2,srcy2); + GL.glEnd(); + + if (angle != 0) { + GL.glTranslatef(centerX, centerY, 0.0f); + GL.glRotatef(-angle, 0.0f, 0.0f, 1.0f); + GL.glTranslatef(-centerX, -centerY, 0.0f); + } + GL.glTranslatef(-x, -y, 0); + +// GL.glBegin(SGL.GL_QUADS); +// drawEmbedded(x,y,x2,y2,srcx,srcy,srcx2,srcy2); +// GL.glEnd(); + } + + /** + * Draw a section of this image at a particular location and scale on the screen, while this + * is image is "in use", i.e. between calls to startUse and endUse. + * + * @param x The x position to draw the image + * @param y The y position to draw the image + * @param x2 The x position of the bottom right corner of the drawn image + * @param y2 The y position of the bottom right corner of the drawn image + * @param srcx The x position of the rectangle to draw from this image (i.e. relative to this image) + * @param srcy The y position of the rectangle to draw from this image (i.e. relative to this image) + * @param srcx2 The x position of the bottom right cornder of rectangle to draw from this image (i.e. relative to this image) + * @param srcy2 The t position of the bottom right cornder of rectangle to draw from this image (i.e. relative to this image) + */ + public void drawEmbedded(float x, float y, float x2, float y2, float srcx, float srcy, float srcx2, float srcy2) { + drawEmbedded(x,y,x2,y2,srcx,srcy,srcx2,srcy2,null); + } + + /** + * Draw a section of this image at a particular location and scale on the screen, while this + * is image is "in use", i.e. between calls to startUse and endUse. + * + * @param x The x position to draw the image + * @param y The y position to draw the image + * @param x2 The x position of the bottom right corner of the drawn image + * @param y2 The y position of the bottom right corner of the drawn image + * @param srcx The x position of the rectangle to draw from this image (i.e. relative to this image) + * @param srcy The y position of the rectangle to draw from this image (i.e. relative to this image) + * @param srcx2 The x position of the bottom right cornder of rectangle to draw from this image (i.e. relative to this image) + * @param srcy2 The t position of the bottom right cornder of rectangle to draw from this image (i.e. relative to this image) + * @param filter The colour filter to apply when drawing + */ + public void drawEmbedded(float x, float y, float x2, float y2, float srcx, float srcy, float srcx2, float srcy2, Color filter) { + if (filter != null) { + filter.bind(); + } + + float mywidth = x2 - x; + float myheight = y2 - y; + float texwidth = srcx2 - srcx; + float texheight = srcy2 - srcy; + + float newTextureOffsetX = (((srcx) / (width)) * textureWidth) + + textureOffsetX; + float newTextureOffsetY = (((srcy) / (height)) * textureHeight) + + textureOffsetY; + float newTextureWidth = ((texwidth) / (width)) + * textureWidth; + float newTextureHeight = ((texheight) / (height)) + * textureHeight; + + GL.glTexCoord2f(newTextureOffsetX, newTextureOffsetY); + GL.glVertex3f(x,y, 0.0f); + GL.glTexCoord2f(newTextureOffsetX, newTextureOffsetY + + newTextureHeight); + GL.glVertex3f(x,(y + myheight), 0.0f); + GL.glTexCoord2f(newTextureOffsetX + newTextureWidth, + newTextureOffsetY + newTextureHeight); + GL.glVertex3f((x + mywidth),(y + myheight), 0.0f); + GL.glTexCoord2f(newTextureOffsetX + newTextureWidth, + newTextureOffsetY); + GL.glVertex3f((x + mywidth),y, 0.0f); + } + + /** + * Get the width of this image + * + * @return The width of this image + */ + public int getWidth() { + init(); + return width; + } + + /** + * Get the height of this image + * + * @return The height of this image + */ + public int getHeight() { + init(); + return height; + } + + /** + * Get a copy of this image. This is a shallow copy and does not + * duplicate image adata. + * + * @return The copy of this image + */ + public Image copy() { + init(); + return getSubImage(0,0,width,height); + } + + /** + * Get a scaled copy of this image with a uniform scale + * + * @param scale The scale to apply + * @return The new scaled image + */ + public Image getScaledCopy(float scale) { + init(); + return getScaledCopy((int) (width*scale),(int) (height*scale)); + } + + /** + * Get a scaled copy of this image + * + * @param width The width of the copy + * @param height The height of the copy + * @return The new scaled image + */ + public Image getScaledCopy(int width, int height) { + init(); + Image image = copy(); + image.width = width; + image.height = height; + image.centerX = width / 2; + image.centerY = height / 2; + return image; + } + + /** + * Make sure the texture cordinates are inverse on the y axis + */ + public void ensureInverted() { + if (textureHeight > 0) { + textureOffsetY = textureOffsetY + textureHeight; + textureHeight = -textureHeight; + } + } + + /** + * Get a copy image flipped on potentially two axis + * + * @param flipHorizontal True if we want to flip the image horizontally + * @param flipVertical True if we want to flip the image vertically + * @return The flipped image instance + */ + public Image getFlippedCopy(boolean flipHorizontal, boolean flipVertical) { + init(); + Image image = copy(); + + if (flipHorizontal) { + image.textureOffsetX = textureOffsetX + textureWidth; + image.textureWidth = -textureWidth; + } + if (flipVertical) { + image.textureOffsetY = textureOffsetY + textureHeight; + image.textureHeight = -textureHeight; + } + + return image; + } + + /** + * End the use of this sprite sheet and release the lock. + * + * @see #startUse + */ + public void endUse() { + if (inUse != this) { + throw new RuntimeException("The sprite sheet is not currently in use"); + } + inUse = null; + GL.glEnd(); + } + + /** + * Start using this sheet. This method can be used for optimal rendering of a collection + * of sprites from a single sprite sheet. First, startUse(). Then render each sprite by + * calling renderInUse(). Finally, endUse(). Between start and end there can be no rendering + * of other sprites since the rendering is locked for this sprite sheet. + */ + public void startUse() { + if (inUse != null) { + throw new RuntimeException("Attempt to start use of a sprite sheet before ending use with another - see endUse()"); + } + inUse = this; + init(); + + Color.white.bind(); + texture.bind(); + GL.glBegin(SGL.GL_QUADS); + } + + /** + * @see java.lang.Object#toString() + */ + public String toString() { + init(); + + return "[Image "+ref+" "+width+"x"+height+" "+textureOffsetX+","+textureOffsetY+","+textureWidth+","+textureHeight+"]"; + } + + /** + * Get the OpenGL texture holding this image + * + * @return The OpenGL texture holding this image + */ + public Texture getTexture() { + return texture; + } + + /** + * Set the texture used by this image + * + * @param texture The texture used by this image + */ + public void setTexture(Texture texture) { + this.texture = texture; + reinit(); + } + + /** + * Translate an unsigned int into a signed integer + * + * @param b The byte to convert + * @return The integer value represented by the byte + */ + private int translate(byte b) { + if (b < 0) { + return 256 + b; + } + + return b; + } + + /** + * Get the colour of a pixel at a specified location in this image + * + * @param x The x coordinate of the pixel + * @param y The y coordinate of the pixel + * @return The Color of the pixel at the specified location + */ + public Color getColor(int x, int y) { + if (pixelData == null) { + pixelData = texture.getTextureData(); + } + + int xo = (int) (textureOffsetX * texture.getTextureWidth()); + int yo = (int) (textureOffsetY * texture.getTextureHeight()); + + if (textureWidth < 0) { + x = xo - x; + } else { + x = xo + x; + } + + if (textureHeight < 0) { + y = yo - y; + } else { + y = yo + y; + } + + int offset = x + (y * texture.getTextureWidth()); + offset *= texture.hasAlpha() ? 4 : 3; + + if (texture.hasAlpha()) { + return new Color(translate(pixelData[offset]),translate(pixelData[offset+1]), + translate(pixelData[offset+2]),translate(pixelData[offset+3])); + } else { + return new Color(translate(pixelData[offset]),translate(pixelData[offset+1]), + translate(pixelData[offset+2])); + } + } + + /** + * Check if this image has been destroyed + * + * @return True if this image has been destroyed + */ + public boolean isDestroyed() { + return destroyed; + } + + /** + * Destroy the image and release any native resources. + * Calls on a destroyed image have undefined results + * + * @throws SlickException Indicates a failure to release resources on the graphics card + */ + public void destroy() throws SlickException { + if (isDestroyed()) { + return; + } + + destroyed = true; + texture.release(); + GraphicsFactory.releaseGraphicsForImage(this); + } + + /** + * Flush the current pixel data to force a re-read next update + */ + public void flushPixelData() { + pixelData = null; + } +} diff --git a/ext/slick/src/org/newdawn/slick/ImageBuffer.java b/ext/slick/src/org/newdawn/slick/ImageBuffer.java new file mode 100644 index 0000000..9aab61c --- /dev/null +++ b/ext/slick/src/org/newdawn/slick/ImageBuffer.java @@ -0,0 +1,167 @@ +package org.newdawn.slick; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import org.lwjgl.BufferUtils; +import org.newdawn.slick.opengl.ImageData; + +/** + * A utility for creating images from pixel operations + * + * Expected usage is: + * + * ImageBuffer buffer = new ImageBuffer(320,200); + * buffer.setRGBA(100,100,50,50,20,255); + * .. + * Image image = buffer.getImage(); + * + * + * @author kevin + */ +public class ImageBuffer implements ImageData { + /** The width of the image */ + private int width; + /** The height of the image */ + private int height; + /** The width of the texture */ + private int texWidth; + /** The height of the texture */ + private int texHeight; + /** The raw data generated for the image */ + private byte[] rawData; + + /** + * + * @param width + * @param height + */ + public ImageBuffer(int width, int height) { + this.width = width; + this.height = height; + + texWidth = get2Fold(width); + texHeight = get2Fold(height); + + rawData = new byte[texWidth * texHeight * 4]; + } + + /** + * Retrieve the raw data stored within the image buffer + * + * @return The raw data in RGBA packed format from within the image buffer + */ + public byte[] getRGBA() { + return rawData; + } + + /** + * @see org.newdawn.slick.opengl.ImageData#getDepth() + */ + public int getDepth() { + return 32; + } + + /** + * @see org.newdawn.slick.opengl.ImageData#getHeight() + */ + public int getHeight() { + return height; + } + + /** + * @see org.newdawn.slick.opengl.ImageData#getTexHeight() + */ + public int getTexHeight() { + return texHeight; + } + + /** + * @see org.newdawn.slick.opengl.ImageData#getTexWidth() + */ + public int getTexWidth() { + return texWidth; + } + + /** + * @see org.newdawn.slick.opengl.ImageData#getWidth() + */ + public int getWidth() { + return width; + } + + /** + * @see org.newdawn.slick.opengl.ImageData#getImageBufferData() + */ + public ByteBuffer getImageBufferData() { + ByteBuffer scratch = BufferUtils.createByteBuffer(rawData.length); + scratch.put(rawData); + scratch.flip(); + + return scratch; + } + + /** + * Set a pixel in the image buffer + * + * @param x The x position of the pixel to set + * @param y The y position of the pixel to set + * @param r The red component to set (0->255) + * @param g The green component to set (0->255) + * @param b The blue component to set (0->255) + * @param a The alpha component to set (0->255) + */ + public void setRGBA(int x, int y, int r, int g, int b, int a) { + if ((x < 0) || (x >= width) || (y < 0) || (y >= height)) { + throw new RuntimeException("Specified location: "+x+","+y+" outside of image"); + } + + int ofs = ((x + (y * texWidth)) * 4); + + if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN) { + rawData[ofs] = (byte) r; + rawData[ofs + 1] = (byte) g; + rawData[ofs + 2] = (byte) b; + rawData[ofs + 3] = (byte) a; + } else { + rawData[ofs] = (byte) r; + rawData[ofs + 1] = (byte) g; + rawData[ofs + 2] = (byte) b; + rawData[ofs + 3] = (byte) a; + } + } + + /** + * Get an image generated based on this buffer + * + * @return The image generated from this buffer + */ + public Image getImage() { + return new Image(this); + } + + /** + * Get an image generated based on this buffer + * + * @param filter The filtering method to use when scaling this image + * @return The image generated from this buffer + */ + public Image getImage(int filter) { + return new Image(this, filter); + } + + /** + * Get the closest greater power of 2 to the fold number + * + * @param fold The target number + * @return The power of 2 + */ + private int get2Fold(int fold) { + int ret = 2; + while (ret < fold) { + ret *= 2; + } + return ret; + } + +} diff --git a/ext/slick/src/org/newdawn/slick/Input.java b/ext/slick/src/org/newdawn/slick/Input.java new file mode 100644 index 0000000..e6d8f9d --- /dev/null +++ b/ext/slick/src/org/newdawn/slick/Input.java @@ -0,0 +1,1523 @@ +package org.newdawn.slick; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; + +import org.lwjgl.LWJGLException; +import org.lwjgl.input.Controller; +import org.lwjgl.input.Controllers; +import org.lwjgl.input.Keyboard; +import org.lwjgl.input.Mouse; +import org.lwjgl.opengl.Display; +import org.newdawn.slick.util.Log; + +/** + * A wrapped for all keyboard, mouse and controller input + * + * @author kevin + */ +public class Input { + /** The controller index to pass to check all controllers */ + public static final int ANY_CONTROLLER = -1; + + /** The maximum number of buttons on controllers */ + private static final int MAX_BUTTONS = 100; + + /** */ + public static final int KEY_ESCAPE = 0x01; + /** */ + public static final int KEY_1 = 0x02; + /** */ + public static final int KEY_2 = 0x03; + /** */ + public static final int KEY_3 = 0x04; + /** */ + public static final int KEY_4 = 0x05; + /** */ + public static final int KEY_5 = 0x06; + /** */ + public static final int KEY_6 = 0x07; + /** */ + public static final int KEY_7 = 0x08; + /** */ + public static final int KEY_8 = 0x09; + /** */ + public static final int KEY_9 = 0x0A; + /** */ + public static final int KEY_0 = 0x0B; + /** */ + public static final int KEY_MINUS = 0x0C; /* - on main keyboard */ + /** */ + public static final int KEY_EQUALS = 0x0D; + /** */ + public static final int KEY_BACK = 0x0E; /* backspace */ + /** */ + public static final int KEY_TAB = 0x0F; + /** */ + public static final int KEY_Q = 0x10; + /** */ + public static final int KEY_W = 0x11; + /** */ + public static final int KEY_E = 0x12; + /** */ + public static final int KEY_R = 0x13; + /** */ + public static final int KEY_T = 0x14; + /** */ + public static final int KEY_Y = 0x15; + /** */ + public static final int KEY_U = 0x16; + /** */ + public static final int KEY_I = 0x17; + /** */ + public static final int KEY_O = 0x18; + /** */ + public static final int KEY_P = 0x19; + /** */ + public static final int KEY_LBRACKET = 0x1A; + /** */ + public static final int KEY_RBRACKET = 0x1B; + /** */ + public static final int KEY_RETURN = 0x1C; /* Enter on main keyboard */ + /** */ + public static final int KEY_ENTER = 0x1C; /* Enter on main keyboard */ + /** */ + public static final int KEY_LCONTROL = 0x1D; + /** */ + public static final int KEY_A = 0x1E; + /** */ + public static final int KEY_S = 0x1F; + /** */ + public static final int KEY_D = 0x20; + /** */ + public static final int KEY_F = 0x21; + /** */ + public static final int KEY_G = 0x22; + /** */ + public static final int KEY_H = 0x23; + /** */ + public static final int KEY_J = 0x24; + /** */ + public static final int KEY_K = 0x25; + /** */ + public static final int KEY_L = 0x26; + /** */ + public static final int KEY_SEMICOLON = 0x27; + /** */ + public static final int KEY_APOSTROPHE = 0x28; + /** */ + public static final int KEY_GRAVE = 0x29; /* accent grave */ + /** */ + public static final int KEY_LSHIFT = 0x2A; + /** */ + public static final int KEY_BACKSLASH = 0x2B; + /** */ + public static final int KEY_Z = 0x2C; + /** */ + public static final int KEY_X = 0x2D; + /** */ + public static final int KEY_C = 0x2E; + /** */ + public static final int KEY_V = 0x2F; + /** */ + public static final int KEY_B = 0x30; + /** */ + public static final int KEY_N = 0x31; + /** */ + public static final int KEY_M = 0x32; + /** */ + public static final int KEY_COMMA = 0x33; + /** */ + public static final int KEY_PERIOD = 0x34; /* . on main keyboard */ + /** */ + public static final int KEY_SLASH = 0x35; /* / on main keyboard */ + /** */ + public static final int KEY_RSHIFT = 0x36; + /** */ + public static final int KEY_MULTIPLY = 0x37; /* * on numeric keypad */ + /** */ + public static final int KEY_LMENU = 0x38; /* left Alt */ + /** */ + public static final int KEY_SPACE = 0x39; + /** */ + public static final int KEY_CAPITAL = 0x3A; + /** */ + public static final int KEY_F1 = 0x3B; + /** */ + public static final int KEY_F2 = 0x3C; + /** */ + public static final int KEY_F3 = 0x3D; + /** */ + public static final int KEY_F4 = 0x3E; + /** */ + public static final int KEY_F5 = 0x3F; + /** */ + public static final int KEY_F6 = 0x40; + /** */ + public static final int KEY_F7 = 0x41; + /** */ + public static final int KEY_F8 = 0x42; + /** */ + public static final int KEY_F9 = 0x43; + /** */ + public static final int KEY_F10 = 0x44; + /** */ + public static final int KEY_NUMLOCK = 0x45; + /** */ + public static final int KEY_SCROLL = 0x46; /* Scroll Lock */ + /** */ + public static final int KEY_NUMPAD7 = 0x47; + /** */ + public static final int KEY_NUMPAD8 = 0x48; + /** */ + public static final int KEY_NUMPAD9 = 0x49; + /** */ + public static final int KEY_SUBTRACT = 0x4A; /* - on numeric keypad */ + /** */ + public static final int KEY_NUMPAD4 = 0x4B; + /** */ + public static final int KEY_NUMPAD5 = 0x4C; + /** */ + public static final int KEY_NUMPAD6 = 0x4D; + /** */ + public static final int KEY_ADD = 0x4E; /* + on numeric keypad */ + /** */ + public static final int KEY_NUMPAD1 = 0x4F; + /** */ + public static final int KEY_NUMPAD2 = 0x50; + /** */ + public static final int KEY_NUMPAD3 = 0x51; + /** */ + public static final int KEY_NUMPAD0 = 0x52; + /** */ + public static final int KEY_DECIMAL = 0x53; /* . on numeric keypad */ + /** */ + public static final int KEY_F11 = 0x57; + /** */ + public static final int KEY_F12 = 0x58; + /** */ + public static final int KEY_F13 = 0x64; /* (NEC PC98) */ + /** */ + public static final int KEY_F14 = 0x65; /* (NEC PC98) */ + /** */ + public static final int KEY_F15 = 0x66; /* (NEC PC98) */ + /** */ + public static final int KEY_KANA = 0x70; /* (Japanese keyboard) */ + /** */ + public static final int KEY_CONVERT = 0x79; /* (Japanese keyboard) */ + /** */ + public static final int KEY_NOCONVERT = 0x7B; /* (Japanese keyboard) */ + /** */ + public static final int KEY_YEN = 0x7D; /* (Japanese keyboard) */ + /** */ + public static final int KEY_NUMPADEQUALS = 0x8D; /* = on numeric keypad (NEC PC98) */ + /** */ + public static final int KEY_CIRCUMFLEX = 0x90; /* (Japanese keyboard) */ + /** */ + public static final int KEY_AT = 0x91; /* (NEC PC98) */ + /** */ + public static final int KEY_COLON = 0x92; /* (NEC PC98) */ + /** */ + public static final int KEY_UNDERLINE = 0x93; /* (NEC PC98) */ + /** */ + public static final int KEY_KANJI = 0x94; /* (Japanese keyboard) */ + /** */ + public static final int KEY_STOP = 0x95; /* (NEC PC98) */ + /** */ + public static final int KEY_AX = 0x96; /* (Japan AX) */ + /** */ + public static final int KEY_UNLABELED = 0x97; /* (J3100) */ + /** */ + public static final int KEY_NUMPADENTER = 0x9C; /* Enter on numeric keypad */ + /** */ + public static final int KEY_RCONTROL = 0x9D; + /** */ + public static final int KEY_NUMPADCOMMA = 0xB3; /* , on numeric keypad (NEC PC98) */ + /** */ + public static final int KEY_DIVIDE = 0xB5; /* / on numeric keypad */ + /** */ + public static final int KEY_SYSRQ = 0xB7; + /** */ + public static final int KEY_RMENU = 0xB8; /* right Alt */ + /** */ + public static final int KEY_PAUSE = 0xC5; /* Pause */ + /** */ + public static final int KEY_HOME = 0xC7; /* Home on arrow keypad */ + /** */ + public static final int KEY_UP = 0xC8; /* UpArrow on arrow keypad */ + /** */ + public static final int KEY_PRIOR = 0xC9; /* PgUp on arrow keypad */ + /** */ + public static final int KEY_LEFT = 0xCB; /* LeftArrow on arrow keypad */ + /** */ + public static final int KEY_RIGHT = 0xCD; /* RightArrow on arrow keypad */ + /** */ + public static final int KEY_END = 0xCF; /* End on arrow keypad */ + /** */ + public static final int KEY_DOWN = 0xD0; /* DownArrow on arrow keypad */ + /** */ + public static final int KEY_NEXT = 0xD1; /* PgDn on arrow keypad */ + /** */ + public static final int KEY_INSERT = 0xD2; /* Insert on arrow keypad */ + /** */ + public static final int KEY_DELETE = 0xD3; /* Delete on arrow keypad */ + /** */ + public static final int KEY_LWIN = 0xDB; /* Left Windows key */ + /** */ + public static final int KEY_RWIN = 0xDC; /* Right Windows key */ + /** */ + public static final int KEY_APPS = 0xDD; /* AppMenu key */ + /** */ + public static final int KEY_POWER = 0xDE; + /** */ + public static final int KEY_SLEEP = 0xDF; + + /** A helper for left ALT */ + public static final int KEY_LALT = KEY_LMENU; + /** A helper for right ALT */ + public static final int KEY_RALT = KEY_RMENU; + + /** Control index */ + private static final int LEFT = 0; + /** Control index */ + private static final int RIGHT = 1; + /** Control index */ + private static final int UP = 2; + /** Control index */ + private static final int DOWN = 3; + /** Control index */ + private static final int BUTTON1 = 4; + /** Control index */ + private static final int BUTTON2 = 5; + /** Control index */ + private static final int BUTTON3 = 6; + /** Control index */ + private static final int BUTTON4 = 7; + /** Control index */ + private static final int BUTTON5 = 8; + /** Control index */ + private static final int BUTTON6 = 9; + /** Control index */ + private static final int BUTTON7 = 10; + /** Control index */ + private static final int BUTTON8 = 11; + /** Control index */ + private static final int BUTTON9 = 12; + /** Control index */ + private static final int BUTTON10 = 13; + + /** The left mouse button indicator */ + public static final int MOUSE_LEFT_BUTTON = 0; + /** The right mouse button indicator */ + public static final int MOUSE_RIGHT_BUTTON = 1; + /** The middle mouse button indicator */ + public static final int MOUSE_MIDDLE_BUTTON = 2; + + /** True if the controllers system has been initialised */ + private static boolean controllersInited = false; + /** The list of controllers */ + private static ArrayList controllers = new ArrayList(); + + /** The last recorded mouse x position */ + private int lastMouseX; + /** The last recorded mouse y position */ + private int lastMouseY; + /** THe state of the mouse buttons */ + protected boolean[] mousePressed = new boolean[10]; + /** THe state of the controller buttons */ + private boolean[][] controllerPressed = new boolean[100][MAX_BUTTONS]; + + /** The character values representing the pressed keys */ + protected char[] keys = new char[1024]; + /** True if the key has been pressed since last queries */ + protected boolean[] pressed = new boolean[1024]; + /** The time since the next key repeat to be fired for the key */ + protected long[] nextRepeat = new long[1024]; + + /** The control states from the controllers */ + private boolean[][] controls = new boolean[10][MAX_BUTTONS+10]; + /** True if the event has been consumed */ + protected boolean consumed = false; + /** A list of listeners to be notified of input events */ + protected HashSet allListeners = new HashSet(); + /** The listeners to notify of key events */ + protected ArrayList keyListeners = new ArrayList(); + /** The listener to add */ + protected ArrayList keyListenersToAdd = new ArrayList(); + /** The listeners to notify of mouse events */ + protected ArrayList mouseListeners = new ArrayList(); + /** The listener to add */ + protected ArrayList mouseListenersToAdd = new ArrayList(); + /** The listener to nofiy of controller events */ + protected ArrayList controllerListeners = new ArrayList(); + /** The current value of the wheel */ + private int wheel; + /** The height of the display */ + private int height; + + /** True if the display is active */ + private boolean displayActive = true; + + /** True if key repeat is enabled */ + private boolean keyRepeat; + /** The initial delay for key repeat starts */ + private int keyRepeatInitial; + /** The interval of key repeat */ + private int keyRepeatInterval; + + /** True if the input is currently paused */ + private boolean paused; + /** The scale to apply to screen coordinates */ + private float scaleX = 1; + /** The scale to apply to screen coordinates */ + private float scaleY = 1; + /** The offset to apply to screen coordinates */ + private float xoffset = 0; + /** The offset to apply to screen coordinates */ + private float yoffset = 0; + + /** The delay before determining a single or double click */ + private int doubleClickDelay = 250; + /** The timer running out for a single click */ + private long doubleClickTimeout = 0; + + /** The clicked x position */ + private int clickX; + /** The clicked y position */ + private int clickY; + /** The clicked button */ + private int clickButton; + + /** The x position location the mouse was pressed */ + private int pressedX = -1; + + /** The x position location the mouse was pressed */ + private int pressedY = -1; + + /** The pixel distance the mouse can move to accept a mouse click */ + private int mouseClickTolerance = 5; + + /** + * Disables support for controllers. This means the jinput JAR and native libs + * are not required. + */ + public static void disableControllers() { + controllersInited = true; + } + + /** + * Create a new input with the height of the screen + * + * @param height The height of the screen + */ + public Input(int height) { + init(height); + } + + /** + * Set the double click interval, the time between the first + * and second clicks that should be interpreted as a + * double click. + * + * @param delay The delay between clicks + */ + public void setDoubleClickInterval(int delay) { + doubleClickDelay = delay; + } + + /** + * Set the pixel distance the mouse can move to accept a mouse click. + * Default is 5. + * + * @param mouseClickTolerance The number of pixels. + */ + public void setMouseClickTolerance (int mouseClickTolerance) { + this.mouseClickTolerance = mouseClickTolerance; + } + + /** + * Set the scaling to apply to screen coordinates + * + * @param scaleX The scaling to apply to the horizontal axis + * @param scaleY The scaling to apply to the vertical axis + */ + public void setScale(float scaleX, float scaleY) { + this.scaleX = scaleX; + this.scaleY = scaleY; + } + + /** + * Set the offset to apply to the screen coodinates + * + * @param xoffset The offset on the x-axis + * @param yoffset The offset on the y-axis + */ + public void setOffset(float xoffset, float yoffset) { + this.xoffset = xoffset; + this.yoffset = yoffset; + } + + /** + * Reset the transformation being applied to the input to the default + */ + public void resetInputTransform() { + setOffset(0, 0); + setScale(1, 1); + } + + /** + * Add a listener to be notified of input events + * + * @param listener The listener to be notified + */ + public void addListener(InputListener listener) { + addKeyListener(listener); + addMouseListener(listener); + addControllerListener(listener); + } + + /** + * Add a key listener to be notified of key input events + * + * @param listener The listener to be notified + */ + public void addKeyListener(KeyListener listener) { + keyListenersToAdd.add(listener); + } + + /** + * Add a key listener to be notified of key input events + * + * @param listener The listener to be notified + */ + private void addKeyListenerImpl(KeyListener listener) { + if (keyListeners.contains(listener)) { + return; + } + keyListeners.add(listener); + allListeners.add(listener); + } + + /** + * Add a mouse listener to be notified of mouse input events + * + * @param listener The listener to be notified + */ + public void addMouseListener(MouseListener listener) { + mouseListenersToAdd.add(listener); + } + + /** + * Add a mouse listener to be notified of mouse input events + * + * @param listener The listener to be notified + */ + private void addMouseListenerImpl(MouseListener listener) { + if (mouseListeners.contains(listener)) { + return; + } + mouseListeners.add(listener); + allListeners.add(listener); + } + + /** + * Add a controller listener to be notified of controller input events + * + * @param listener The listener to be notified + */ + public void addControllerListener(ControllerListener listener) { + if (controllerListeners.contains(listener)) { + return; + } + controllerListeners.add(listener); + allListeners.add(listener); + } + + /** + * Remove all the listeners from this input + */ + public void removeAllListeners() { + removeAllKeyListeners(); + removeAllMouseListeners(); + removeAllControllerListeners(); + } + + /** + * Remove all the key listeners from this input + */ + public void removeAllKeyListeners() { + allListeners.removeAll(keyListeners); + keyListeners.clear(); + } + + /** + * Remove all the mouse listeners from this input + */ + public void removeAllMouseListeners() { + allListeners.removeAll(mouseListeners); + mouseListeners.clear(); + } + + /** + * Remove all the controller listeners from this input + */ + public void removeAllControllerListeners() { + allListeners.removeAll(controllerListeners); + controllerListeners.clear(); + } + + /** + * Add a listener to be notified of input events. This listener + * will get events before others that are currently registered + * + * @param listener The listener to be notified + */ + public void addPrimaryListener(InputListener listener) { + removeListener(listener); + + keyListeners.add(0, listener); + mouseListeners.add(0, listener); + controllerListeners.add(0, listener); + + allListeners.add(listener); + } + + /** + * Remove a listener that will no longer be notified + * + * @param listener The listen to be removed + */ + public void removeListener(InputListener listener) { + removeKeyListener(listener); + removeMouseListener(listener); + removeControllerListener(listener); + } + + /** + * Remove a key listener that will no longer be notified + * + * @param listener The listen to be removed + */ + public void removeKeyListener(KeyListener listener) { + keyListeners.remove(listener); + + if (!mouseListeners.contains(listener) && !controllerListeners.contains(listener)) { + allListeners.remove(listener); + } + } + + /** + * Remove a controller listener that will no longer be notified + * + * @param listener The listen to be removed + */ + public void removeControllerListener(ControllerListener listener) { + controllerListeners.remove(listener); + + if (!mouseListeners.contains(listener) && !keyListeners.contains(listener)) { + allListeners.remove(listener); + } + } + + /** + * Remove a mouse listener that will no longer be notified + * + * @param listener The listen to be removed + */ + public void removeMouseListener(MouseListener listener) { + mouseListeners.remove(listener); + + if (!controllerListeners.contains(listener) && !keyListeners.contains(listener)) { + allListeners.remove(listener); + } + } + + /** + * Initialise the input system + * + * @param height The height of the window + */ + void init(int height) { + this.height = height; + lastMouseX = getMouseX(); + lastMouseY = getMouseY(); + } + + /** + * Get the character representation of the key identified by the specified code + * + * @param code The key code of the key to retrieve the name of + * @return The name or character representation of the key requested + */ + public static String getKeyName(int code) { + return Keyboard.getKeyName(code); + } + + /** + * Check if a particular key has been pressed since this method + * was last called for the specified key + * + * @param code The key code of the key to check + * @return True if the key has been pressed + */ + public boolean isKeyPressed(int code) { + if (pressed[code]) { + pressed[code] = false; + return true; + } + + return false; + } + + /** + * Check if a mouse button has been pressed since last call + * + * @param button The button to check + * @return True if the button has been pressed since last call + */ + public boolean isMousePressed(int button) { + if (mousePressed[button]) { + mousePressed[button] = false; + return true; + } + + return false; + } + + /** + * Check if a controller button has been pressed since last + * time + * + * @param button The button to check for (note that this includes directional controls first) + * @return True if the button has been pressed since last time + */ + public boolean isControlPressed(int button) { + return isControlPressed(button, 0); + } + + /** + * Check if a controller button has been pressed since last + * time + * + * @param controller The index of the controller to check + * @param button The button to check for (note that this includes directional controls first) + * @return True if the button has been pressed since last time + */ + public boolean isControlPressed(int button, int controller) { + if (controllerPressed[controller][button]) { + controllerPressed[controller][button] = false; + return true; + } + + return false; + } + + /** + * Clear the state for isControlPressed method. This will reset all + * controls to not pressed + */ + public void clearControlPressedRecord() { + for (int i=0;iisKeyPressed method. This will + * resort in all keys returning that they haven't been pressed, until + * they are pressed again + */ + public void clearKeyPressedRecord() { + Arrays.fill(pressed, false); + } + + /** + * Clear the state for the isMousePressed method. This will + * resort in all mouse buttons returning that they haven't been pressed, until + * they are pressed again + */ + public void clearMousePressedRecord() { + Arrays.fill(mousePressed, false); + } + + /** + * Check if a particular key is down + * + * @param code The key code of the key to check + * @return True if the key is down + */ + public boolean isKeyDown(int code) { + return Keyboard.isKeyDown(code); + } + + /** + * Get the absolute x position of the mouse cursor within the container + * + * @return The absolute x position of the mouse cursor + */ + public int getAbsoluteMouseX() { + return Mouse.getX(); + } + + /** + * Get the absolute y position of the mouse cursor within the container + * + * @return The absolute y position of the mouse cursor + */ + public int getAbsoluteMouseY() { + return height - Mouse.getY(); + } + + /** + * Get the x position of the mouse cursor + * + * @return The x position of the mouse cursor + */ + public int getMouseX() { + return (int) ((Mouse.getX() * scaleX)+xoffset); + } + + /** + * Get the y position of the mouse cursor + * + * @return The y position of the mouse cursor + */ + public int getMouseY() { + return (int) (((height-Mouse.getY()) * scaleY)+yoffset); + } + + /** + * Check if a given mouse button is down + * + * @param button The index of the button to check (starting at 0) + * @return True if the mouse button is down + */ + public boolean isMouseButtonDown(int button) { + return Mouse.isButtonDown(button); + } + + /** + * Check if any mouse button is down + * + * @return True if any mouse button is down + */ + private boolean anyMouseDown() { + for (int i=0;i<3;i++) { + if (Mouse.isButtonDown(i)) { + return true; + } + } + + return false; + } + + /** + * Get a count of the number of controlles available + * + * @return The number of controllers available + */ + public int getControllerCount() { + try { + initControllers(); + } catch (SlickException e) { + throw new RuntimeException("Failed to initialise controllers"); + } + + return controllers.size(); + } + + /** + * Get the number of axis that are avaiable on a given controller + * + * @param controller The index of the controller to check + * @return The number of axis available on the controller + */ + public int getAxisCount(int controller) { + return ((Controller) controllers.get(controller)).getAxisCount(); + } + + /** + * Get the value of the axis with the given index + * + * @param controller The index of the controller to check + * @param axis The index of the axis to read + * @return The axis value at time of reading + */ + public float getAxisValue(int controller, int axis) { + return ((Controller) controllers.get(controller)).getAxisValue(axis); + } + + /** + * Get the name of the axis with the given index + * + * @param controller The index of the controller to check + * @param axis The index of the axis to read + * @return The name of the specified axis + */ + public String getAxisName(int controller, int axis) { + return ((Controller) controllers.get(controller)).getAxisName(axis); + } + + /** + * Check if the controller has the left direction pressed + * + * @param controller The index of the controller to check + * @return True if the controller is pressed to the left + */ + public boolean isControllerLeft(int controller) { + if (controller >= getControllerCount()) { + return false; + } + + if (controller == ANY_CONTROLLER) { + for (int i=0;i= getControllerCount()) { + return false; + } + + if (controller == ANY_CONTROLLER) { + for (int i=0;i 0.5f + || ((Controller) controllers.get(controller)).getPovX() > 0.5f; + } + + /** + * Check if the controller has the up direction pressed + * + * @param controller The index of the controller to check + * @return True if the controller is pressed to the up + */ + public boolean isControllerUp(int controller) { + if (controller >= getControllerCount()) { + return false; + } + + if (controller == ANY_CONTROLLER) { + for (int i=0;i= getControllerCount()) { + return false; + } + + if (controller == ANY_CONTROLLER) { + for (int i=0;i 0.5f + || ((Controller) controllers.get(controller)).getPovY() > 0.5f; + + } + + /** + * Check if controller button is pressed + * + * @param controller The index of the controller to check + * @param index The index of the button to check + * @return True if the button is pressed + */ + public boolean isButtonPressed(int index, int controller) { + if (controller >= getControllerCount()) { + return false; + } + + if (controller == ANY_CONTROLLER) { + for (int i=0;i= 3) && (controller.getButtonCount() < MAX_BUTTONS)) { + controllers.add(controller); + } + } + + Log.info("Found "+controllers.size()+" controllers"); + for (int i=0;i doubleClickTimeout) { + doubleClickTimeout = 0; + } + } + + this.height = height; + + Iterator allStarts = allListeners.iterator(); + while (allStarts.hasNext()) { + ControlledInputReciever listener = (ControlledInputReciever) allStarts.next(); + listener.inputStarted(); + } + + while (Keyboard.next()) { + if (Keyboard.getEventKeyState()) { + int eventKey = resolveEventKey(Keyboard.getEventKey(), Keyboard.getEventCharacter()); + + keys[eventKey] = Keyboard.getEventCharacter(); + pressed[eventKey] = true; + nextRepeat[eventKey] = System.currentTimeMillis() + keyRepeatInitial; + + consumed = false; + for (int i=0;i= 0) { + if (Mouse.getEventButtonState()) { + consumed = false; + mousePressed[Mouse.getEventButton()] = true; + + pressedX = (int) (xoffset + (Mouse.getEventX() * scaleX)); + pressedY = (int) (yoffset + ((height-Mouse.getEventY()) * scaleY)); + + for (int i=0;i nextRepeat[i]) { + nextRepeat[i] = System.currentTimeMillis() + keyRepeatInterval; + consumed = false; + for (int j=0;j= BUTTON1) { + return isButtonPressed((index-BUTTON1), controllerIndex); + } + + throw new RuntimeException("Unknown control index"); + } + + + /** + * Pauses the polling and sending of input events. + */ + public void pause() { + paused = true; + + // Reset all polling arrays + clearKeyPressedRecord(); + clearMousePressedRecord(); + clearControlPressedRecord(); + } + + /** + * Resumes the polling and sending of input events. + */ + public void resume() { + paused = false; + } + + /** + * Notify listeners that the mouse button has been clicked + * + * @param button The button that has been clicked + * @param x The location at which the button was clicked + * @param y The location at which the button was clicked + * @param clickCount The number of times the button was clicked (single or double click) + */ + private void fireMouseClicked(int button, int x, int y, int clickCount) { + consumed = false; + for (int i=0;i + */ +public class Music { + /** The music currently being played or null if none */ + private static Music currentMusic; + + /** + * Poll the state of the current music. This causes streaming music + * to stream and checks listeners. Note that if you're using a game container + * this will be auto-magically called for you. + * + * @param delta The amount of time since last poll + */ + public static void poll(int delta) { + if (currentMusic != null) { + SoundStore.get().poll(delta); + if (!SoundStore.get().isMusicPlaying()) { + if (!currentMusic.positioning) { + Music oldMusic = currentMusic; + currentMusic = null; + oldMusic.fireMusicEnded(); + } + } else { + currentMusic.update(delta); + } + } + } + + /** The sound from FECK representing this music */ + private Audio sound; + /** True if the music is playing */ + private boolean playing; + /** The list of listeners waiting for notification that the music ended */ + private ArrayList listeners = new ArrayList(); + /** The volume of this music */ + private float volume = 1.0f; + /** Start gain for fading in/out */ + private float fadeStartGain; + /** End gain for fading in/out */ + private float fadeEndGain; + /** Countdown for fading in/out */ + private int fadeTime; + /** Duration for fading in/out */ + private int fadeDuration; + /** True if music should be stopped after fading in/out */ + private boolean stopAfterFade; + /** True if the music is being repositioned and it is therefore normal that it's not playing */ + private boolean positioning; + /** The position that was requested */ + private float requiredPosition = -1; + + /** + * Create and load a piece of music (either OGG or MOD/XM) + * + * @param ref The location of the music + * @throws SlickException + */ + public Music(String ref) throws SlickException { + this(ref, false); + } + + /** + * Create and load a piece of music (either OGG or MOD/XM) + * + * @param ref The location of the music + * @throws SlickException + */ + public Music(URL ref) throws SlickException { + this(ref, false); + } + + /** + * Create and load a piece of music (either OGG or MOD/XM) + * + * @param url The location of the music + * @param streamingHint A hint to indicate whether streaming should be used if possible + * @throws SlickException + */ + public Music(URL url, boolean streamingHint) throws SlickException { + SoundStore.get().init(); + String ref = url.getFile(); + + try { + if (ref.toLowerCase().endsWith(".ogg")) { + if (streamingHint) { + sound = SoundStore.get().getOggStream(url); + } else { + sound = SoundStore.get().getOgg(url.openStream()); + } + } else if (ref.toLowerCase().endsWith(".wav")) { + sound = SoundStore.get().getWAV(url.openStream()); + } else if (ref.toLowerCase().endsWith(".xm") || ref.toLowerCase().endsWith(".mod")) { + sound = SoundStore.get().getMOD(url.openStream()); + } else if (ref.toLowerCase().endsWith(".aif") || ref.toLowerCase().endsWith(".aiff")) { + sound = SoundStore.get().getAIF(url.openStream()); + } else { + throw new SlickException("Only .xm, .mod, .ogg, and .aif/f are currently supported."); + } + } catch (Exception e) { + Log.error(e); + throw new SlickException("Failed to load sound: "+url); + } + } + + /** + * Create and load a piece of music (either OGG or MOD/XM) + * + * @param ref The location of the music + * @param streamingHint A hint to indicate whether streaming should be used if possible + * @throws SlickException + */ + public Music(String ref, boolean streamingHint) throws SlickException { + SoundStore.get().init(); + + try { + if (ref.toLowerCase().endsWith(".ogg")) { + if (streamingHint) { + sound = SoundStore.get().getOggStream(ref); + } else { + sound = SoundStore.get().getOgg(ref); + } + } else if (ref.toLowerCase().endsWith(".wav")) { + sound = SoundStore.get().getWAV(ref); + } else if (ref.toLowerCase().endsWith(".xm") || ref.toLowerCase().endsWith(".mod")) { + sound = SoundStore.get().getMOD(ref); + } else if (ref.toLowerCase().endsWith(".aif") || ref.toLowerCase().endsWith(".aiff")) { + sound = SoundStore.get().getAIF(ref); + } else { + throw new SlickException("Only .xm, .mod, .ogg, and .aif/f are currently supported."); + } + } catch (Exception e) { + Log.error(e); + throw new SlickException("Failed to load sound: "+ref); + } + } + + /** + * Add a listener to this music + * + * @param listener The listener to add + */ + public void addListener(MusicListener listener) { + listeners.add(listener); + } + + /** + * Remove a listener from this music + * + * @param listener The listener to remove + */ + public void removeListener(MusicListener listener) { + listeners.remove(listener); + } + + /** + * Fire notifications that this music ended + */ + private void fireMusicEnded() { + playing = false; + for (int i=0;i 1.0f) + volume = 1.0f; + + sound.playAsMusic(pitch, volume, loop); + playing = true; + setVolume(volume); + if (requiredPosition != -1) { + setPosition(requiredPosition); + } + } + + /** + * Pause the music playback + */ + public void pause() { + playing = false; + AudioImpl.pauseMusic(); + } + + /** + * Stop the music playing + */ + public void stop() { + sound.stop(); + } + + /** + * Resume the music playback + */ + public void resume() { + playing = true; + AudioImpl.restartMusic(); + } + + /** + * Check if the music is being played + * + * @return True if the music is being played + */ + public boolean playing() { + return (currentMusic == this) && (playing); + } + + /** + * Set the volume of the music as a factor of the global volume setting + * + * @param volume The volume to play music at. 0 - 1, 1 is Max + */ + public void setVolume(float volume) { + // Bounds check + if(volume > 1) { + volume = 1; + } else if(volume < 0) { + volume = 0; + } + + this.volume = volume; + // This sound is being played as music + if (currentMusic == this) { + SoundStore.get().setCurrentMusicVolume(volume); + } + } + + /** + * Get the individual volume of the music + * @return The volume of this music, still effected by global SoundStore volume. 0 - 1, 1 is Max + */ + public float getVolume() { + return volume; + } + + /** + * Fade this music to the volume specified + * + * @param duration Fade time in milliseconds. + * @param endVolume The target volume + * @param stopAfterFade True if music should be stopped after fading in/out + */ + public void fade (int duration, float endVolume, boolean stopAfterFade) { + this.stopAfterFade = stopAfterFade; + fadeStartGain = volume; + fadeEndGain = endVolume; + fadeDuration = duration; + fadeTime = duration; + } + + /** + * Update the current music applying any effects that need to updated per + * tick. + * + * @param delta The amount of time in milliseconds thats passed since last update + */ + void update(int delta) { + if (!playing) { + return; + } + + if (fadeTime > 0) { + fadeTime -= delta; + if (fadeTime < 0) { + fadeTime = 0; + if (stopAfterFade) { + stop(); + return; + } + } + + float offset = (fadeEndGain - fadeStartGain) * (1 - (fadeTime / (float)fadeDuration)); + setVolume(fadeStartGain + offset); + } + } + + /** + * Seeks to a position in the music. For streaming music, seeking before the current position causes + * the stream to be reloaded. + * + * @param position Position in seconds. + * @return True if the seek was successful + */ + public boolean setPosition(float position) { + if (playing) { + requiredPosition = -1; + + positioning = true; + playing = false; + boolean result = sound.setPosition(position); + playing = true; + positioning = false; + + return result; + } else { + requiredPosition = position; + return false; + } + } + + /** + * The position into the sound thats being played + * + * @return The current position in seconds. + */ + public float getPosition () { + return sound.getPosition(); + } +} diff --git a/ext/slick/src/org/newdawn/slick/MusicListener.java b/ext/slick/src/org/newdawn/slick/MusicListener.java new file mode 100644 index 0000000..fc0843b --- /dev/null +++ b/ext/slick/src/org/newdawn/slick/MusicListener.java @@ -0,0 +1,26 @@ +package org.newdawn.slick; + +/** + * The description of any class needing to recieve notification of changes + * to music state. + * + * @author kevin + */ +public interface MusicListener { + + /** + * Notification that a piece of music finished playing + * + * @param music The music that finished playing + */ + public void musicEnded(Music music); + + /** + * Notification that a piece of music has been swapped + * for another. + * + * @param music The music that has been swapped out + * @param newMusic The new music we're playing + */ + public void musicSwapped(Music music, Music newMusic); +} diff --git a/ext/slick/src/org/newdawn/slick/PackedSpriteSheet.java b/ext/slick/src/org/newdawn/slick/PackedSpriteSheet.java new file mode 100644 index 0000000..efa543c --- /dev/null +++ b/ext/slick/src/org/newdawn/slick/PackedSpriteSheet.java @@ -0,0 +1,193 @@ +package org.newdawn.slick; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.HashMap; + +import org.newdawn.slick.util.Log; +import org.newdawn.slick.util.ResourceLoader; + +/** + * A sprite sheet packed and defined by the Pacific Software Image Packer available + * from: + * + * http://homepage.ntlworld.com/config/imagepacker/ + * + * @author kevin + */ +public class PackedSpriteSheet { + /** The image loaded for the sheet */ + private Image image; + /** The base path where the image is expected to be found based on the original definition file */ + private String basePath; + /** The section definitions */ + private HashMap sections = new HashMap(); + /** The filter used when loading the image */ + private int filter = Image.FILTER_NEAREST; + + /** + * Create a new packed sprite sheet based on a ImagePacker definition file + * + * @param def The location of the definition file to read + * @throws SlickException Indicates a failure to read the definition file + */ + public PackedSpriteSheet(String def) throws SlickException { + this(def, null); + } + + /** + * Create a new packed sprite sheet based on a ImagePacker definition file + * + * @param def The location of the definition file to read + * @param trans The color to be treated as transparent + * @throws SlickException Indicates a failure to read the definition file + */ + public PackedSpriteSheet(String def, Color trans) throws SlickException { + def = def.replace('\\', '/'); + basePath = def.substring(0,def.lastIndexOf("/")+1); + + loadDefinition(def, trans); + } + + /** + * Create a new packed sprite sheet based on a ImagePacker definition file + * + * @param def The location of the definition file to read + * @param filter The image filter to use when loading the packed sprite image + * @throws SlickException Indicates a failure to read the definition file + */ + public PackedSpriteSheet(String def, int filter) throws SlickException { + this(def, filter, null); + } + + /** + * Create a new packed sprite sheet based on a ImagePacker definition file + * + * @param def The location of the definition file to read + * @param filter The image filter to use when loading the packed sprite image + * @param trans The color to be treated as transparent + * @throws SlickException Indicates a failure to read the definition file + */ + public PackedSpriteSheet(String def, int filter, Color trans) throws SlickException { + this.filter = filter; + + def = def.replace('\\', '/'); + basePath = def.substring(0,def.lastIndexOf("/")+1); + + loadDefinition(def, trans); + } + + /** + * Get the full image contaning all the sprites/sections + * + * @return The full image containing all the sprites/sections + */ + public Image getFullImage() { + return image; + } + + /** + * Get a single named sprite from the sheet + * + * @param name The name of the sprite to retrieve + * @return The sprite requested (image of) + */ + public Image getSprite(String name) { + Section section = (Section) sections.get(name); + + if (section == null) { + throw new RuntimeException("Unknown sprite from packed sheet: "+name); + } + + return image.getSubImage(section.x, section.y, section.width, section.height); + } + + /** + * Get a sprite sheet that has been packed into the greater image + * + * @param name The name of the sprite sheet to retrieve + * @return The sprite sheet from the packed sheet + */ + public SpriteSheet getSpriteSheet(String name) { + Image image = getSprite(name); + Section section = (Section) sections.get(name); + + return new SpriteSheet(image, section.width / section.tilesx, section.height / section.tilesy); + } + + /** + * Load the definition file and parse each of the sections + * + * @param def The location of the definitions file + * @param trans The color to be treated as transparent + * @throws SlickException Indicates a failure to read or parse the definitions file + * or referenced image. + */ + private void loadDefinition(String def, Color trans) throws SlickException { + BufferedReader reader = new BufferedReader(new InputStreamReader(ResourceLoader.getResourceAsStream(def))); + + try { + image = new Image(basePath+reader.readLine(), false, filter, trans); + while (reader.ready()) { + if (reader.readLine() == null) { + break; + } + + Section sect = new Section(reader); + sections.put(sect.name, sect); + + if (reader.readLine() == null) { + break; + } + } + } catch (Exception e) { + Log.error(e); + throw new SlickException("Failed to process definitions file - invalid format?", e); + } + } + + /** + * A single section defined within the packed sheet + * + * @author kevin + */ + private class Section { + /** The x position of the section */ + public int x; + /** The y position of the section */ + public int y; + /** The width of the section */ + public int width; + /** The height of the section */ + public int height; + /** The number of sprites across this section */ + public int tilesx; + /** The number of sprites down this section */ + public int tilesy; + /** The name of this section */ + public String name; + + /** + * Create a new section by reading the stream provided + * + * @param reader The reader from which the definition can be read + * @throws IOException Indicates a failure toread the provided stream + */ + public Section(BufferedReader reader) throws IOException { + name = reader.readLine().trim(); + + x = Integer.parseInt(reader.readLine().trim()); + y = Integer.parseInt(reader.readLine().trim()); + width = Integer.parseInt(reader.readLine().trim()); + height = Integer.parseInt(reader.readLine().trim()); + tilesx = Integer.parseInt(reader.readLine().trim()); + tilesy = Integer.parseInt(reader.readLine().trim()); + reader.readLine().trim(); + reader.readLine().trim(); + + tilesx = Math.max(1,tilesx); + tilesy = Math.max(1,tilesy); + } + } +} diff --git a/ext/slick/src/org/newdawn/slick/Renderable.java b/ext/slick/src/org/newdawn/slick/Renderable.java new file mode 100644 index 0000000..28c546c --- /dev/null +++ b/ext/slick/src/org/newdawn/slick/Renderable.java @@ -0,0 +1,18 @@ +package org.newdawn.slick; + +/** + * Description of anything that can be drawn + * + * @author kevin + */ +public interface Renderable { + + /** + * Draw this artefact at the given location + * + * @param x The x coordinate to draw the artefact at + * @param y The y coordinate to draw the artefact at + */ + public void draw(float x, float y); + +} diff --git a/ext/slick/src/org/newdawn/slick/SavedState.java b/ext/slick/src/org/newdawn/slick/SavedState.java new file mode 100644 index 0000000..6334595 --- /dev/null +++ b/ext/slick/src/org/newdawn/slick/SavedState.java @@ -0,0 +1,176 @@ +package org.newdawn.slick; + +import java.io.IOException; +import java.util.HashMap; + +import javax.jnlp.ServiceManager; + +import org.newdawn.slick.muffin.FileMuffin; +import org.newdawn.slick.muffin.Muffin; +import org.newdawn.slick.muffin.WebstartMuffin; +import org.newdawn.slick.util.Log; + +/** + * A utility to allow game setup/state to be stored locally. This utility will adapt to the + * current enviornment (webstart or file based). Note that this will not currently + * work in an applet. + * + * @author kappaOne + */ +public class SavedState { + /** file name of where the scores will be saved */ + private String fileName; + /** Type of Muffin to use */ + private Muffin muffin; + /** hash map where int data will be stored */ + private HashMap numericData = new HashMap(); + /** hash map where string data will be stored */ + private HashMap stringData = new HashMap(); + + /** + * Create and Test to see if the app is running + * as webstart or local app and select the appropriate + * muffin type + * + * @param fileName name of muffin where data will be saved + * @throws SlickException Indicates a failure to load the stored state + */ + public SavedState(String fileName) throws SlickException { + this.fileName = fileName; + + if (isWebstartAvailable()) { + muffin = new WebstartMuffin(); + } + else { + muffin = new FileMuffin(); + } + + try { + load(); + } catch (IOException e) { + throw new SlickException("Failed to load state on startup",e); + } + } + + /** + * Get number stored at given location + * + * @param nameOfField The name of the number to retrieve + * @return The number saved at this location + */ + public double getNumber(String nameOfField) { + return getNumber(nameOfField, 0); + } + + /** + * Get number stored at given location + * + * @param nameOfField The name of the number to retrieve + * @param defaultValue The value to return if the specified value hasn't been set + * @return The number saved at this location + */ + public double getNumber(String nameOfField, double defaultValue) { + Double value = ((Double)numericData.get(nameOfField)); + + if (value == null) { + return defaultValue; + } + + return value.doubleValue(); + } + + /** + * Save the given value at the given location + * will overwrite any previous value at this location + * + * @param nameOfField The name to store the value against + * @param value The value to store + */ + public void setNumber(String nameOfField, double value){ + numericData.put(nameOfField, new Double(value)); + } + + /** + * Get the String at the given location + * + * @param nameOfField location of string + * @return String stored at the location given + */ + public String getString(String nameOfField) { + return getString(nameOfField, null); + } + + /** + * Get the String at the given location + * + * @param nameOfField location of string + * @param defaultValue The value to return if the specified value hasn't been set + * @return String stored at the location given + */ + public String getString(String nameOfField, String defaultValue) { + String value = (String) stringData.get(nameOfField); + + if (value == null) { + return defaultValue; + } + + return value; + } + + /** + * Save the given value at the given location + * will overwrite any previous value at this location + * + * @param nameOfField location to store int + * @param value The value to store + */ + public void setString(String nameOfField, String value){ + stringData.put(nameOfField, value); + } + + /** + * Save the stored data to file/muffin + * + * @throws IOException Indicates it wasn't possible to store the state + */ + public void save() throws IOException { + muffin.saveFile(numericData, fileName + "_Number"); + muffin.saveFile(stringData, fileName + "_String"); + } + + /** + * Load the data from file/muffin + * + * @throws IOException Indicates it wasn't possible to load the state + */ + public void load() throws IOException { + numericData = muffin.loadFile(fileName + "_Number"); + stringData = muffin.loadFile(fileName + "_String"); + } + + /** + * Will delete all current data held in Score + */ + public void clear() { + numericData.clear(); + stringData.clear(); + } + + /** + * Quick test to see if running through Java webstart + * + * @return True if jws running + */ + private boolean isWebstartAvailable() { + try { + Class.forName("javax.jnlp.ServiceManager"); + // this causes to go and see if the service is available + ServiceManager.lookup("javax.jnlp.PersistenceService"); + Log.info("Webstart detected using Muffins"); + } catch (Exception e) { + Log.info("Using Local File System"); + return false; + } + return true; + } +} \ No newline at end of file diff --git a/ext/slick/src/org/newdawn/slick/ScalableGame.java b/ext/slick/src/org/newdawn/slick/ScalableGame.java new file mode 100644 index 0000000..566ec02 --- /dev/null +++ b/ext/slick/src/org/newdawn/slick/ScalableGame.java @@ -0,0 +1,188 @@ +package org.newdawn.slick; + +import org.newdawn.slick.opengl.SlickCallable; +import org.newdawn.slick.opengl.renderer.Renderer; +import org.newdawn.slick.opengl.renderer.SGL; + +/** + * A wrapper to allow any game to be scalable. This relies on knowing the + * normal width/height of the game - i.e. the dimensions that the game is + * expecting to be run at. The wrapper then takes the size of the container + * and scales rendering and input based on the ratio. + * + * Note: Using OpenGL directly within a ScalableGame can break it + * + * @author kevin + */ +public class ScalableGame implements Game { + /** The renderer to use for all GL operations */ + private static SGL GL = Renderer.get(); + + /** The normal or native width of the game */ + private float normalWidth; + /** The normal or native height of the game */ + private float normalHeight; + /** The game that is being wrapped */ + private Game held; + /** True if we should maintain the aspect ratio */ + private boolean maintainAspect; + /** The target width */ + private int targetWidth; + /** The target height */ + private int targetHeight; + /** The game container wrapped */ + private GameContainer container; + + /** + * Create a new scalable game wrapper + * + * @param held The game to be wrapper and displayed at a different resolution + * @param normalWidth The normal width of the game + * @param normalHeight The noral height of the game + */ + public ScalableGame(Game held, int normalWidth, int normalHeight) { + this(held, normalWidth, normalHeight, false); + } + + /** + * Create a new scalable game wrapper + * + * @param held The game to be wrapper and displayed at a different resolution + * @param normalWidth The normal width of the game + * @param normalHeight The noral height of the game + * @param maintainAspect True if we should maintain the aspect ratio + */ + public ScalableGame(Game held, int normalWidth, int normalHeight, boolean maintainAspect) { + this.held = held; + this.normalWidth = normalWidth; + this.normalHeight = normalHeight; + this.maintainAspect = maintainAspect; + } + + /** + * @see org.newdawn.slick.BasicGame#init(org.newdawn.slick.GameContainer) + */ + public void init(GameContainer container) throws SlickException { + this.container = container; + + recalculateScale(); + held.init(container); + } + + /** + * Recalculate the scale of the game + * + * @throws SlickException Indicates a failure to reinit the game + */ + public void recalculateScale() throws SlickException { + targetWidth = container.getWidth(); + targetHeight = container.getHeight(); + + if (maintainAspect) { + boolean normalIsWide = (normalWidth / normalHeight > 1.6 ? true : false); + boolean containerIsWide = ((float) targetWidth / (float) targetHeight > 1.6 ? true : false); + float wScale = targetWidth / normalWidth; + float hScale = targetHeight / normalHeight; + + if (normalIsWide & containerIsWide) { + float scale = (wScale < hScale ? wScale : hScale); + targetWidth = (int) (normalWidth * scale); + targetHeight = (int) (normalHeight * scale); + } else if (normalIsWide & !containerIsWide) { + targetWidth = (int) (normalWidth * wScale); + targetHeight = (int) (normalHeight * wScale); + } else if (!normalIsWide & containerIsWide) { + targetWidth = (int) (normalWidth * hScale); + targetHeight = (int) (normalHeight * hScale); + } else { + float scale = (wScale < hScale ? wScale : hScale); + targetWidth = (int) (normalWidth * scale); + targetHeight = (int) (normalHeight * scale); + } + + } + + if (held instanceof InputListener) { + container.getInput().addListener((InputListener) held); + } + container.getInput().setScale(normalWidth / targetWidth, + normalHeight / targetHeight); + + + int yoffset = 0; + int xoffset = 0; + + if (targetHeight < container.getHeight()) { + yoffset = (container.getHeight() - targetHeight) / 2; + } + if (targetWidth < container.getWidth()) { + xoffset = (container.getWidth() - targetWidth) / 2; + } + container.getInput().setOffset(-xoffset / (targetWidth / normalWidth), + -yoffset / (targetHeight / normalHeight)); + + } + + /** + * @see org.newdawn.slick.BasicGame#update(org.newdawn.slick.GameContainer, int) + */ + public void update(GameContainer container, int delta) throws SlickException { + if ((targetHeight != container.getHeight()) || + (targetWidth != container.getWidth())) { + recalculateScale(); + } + + held.update(container, delta); + } + + /** + * @see org.newdawn.slick.Game#render(org.newdawn.slick.GameContainer, org.newdawn.slick.Graphics) + */ + public final void render(GameContainer container, Graphics g) + throws SlickException { + int yoffset = 0; + int xoffset = 0; + + if (targetHeight < container.getHeight()) { + yoffset = (container.getHeight() - targetHeight) / 2; + } + if (targetWidth < container.getWidth()) { + xoffset = (container.getWidth() - targetWidth) / 2; + } + + SlickCallable.enterSafeBlock(); + g.setClip(xoffset, yoffset, targetWidth, targetHeight); + GL.glTranslatef(xoffset, yoffset, 0); + GL.glScalef(targetWidth / normalWidth, targetHeight / normalHeight,0); + GL.glPushMatrix(); + held.render(container, g); + GL.glPopMatrix(); + g.clearClip(); + SlickCallable.leaveSafeBlock(); + + renderOverlay(container, g); + } + + /** + * Render the overlay that will sit over the scaled screen + * + * @param container The container holding the game being render + * @param g Graphics context on which to render + */ + protected void renderOverlay(GameContainer container, Graphics g) { + } + + /** + * @see org.newdawn.slick.Game#closeRequested() + */ + public boolean closeRequested() { + return held.closeRequested(); + } + + /** + * @see org.newdawn.slick.Game#getTitle() + */ + public String getTitle() { + return held.getTitle(); + } +} diff --git a/ext/slick/src/org/newdawn/slick/ShapeFill.java b/ext/slick/src/org/newdawn/slick/ShapeFill.java new file mode 100644 index 0000000..18e1dbd --- /dev/null +++ b/ext/slick/src/org/newdawn/slick/ShapeFill.java @@ -0,0 +1,33 @@ +package org.newdawn.slick; + +import org.newdawn.slick.geom.Shape; +import org.newdawn.slick.geom.Vector2f; + +/** + * A filling method for a shape. This allows changing colours at shape verticies and + * modify they're positions as required + * + * @author kevin + */ +public interface ShapeFill { + + /** + * Get the colour that should be applied at the specified location + * + * @param shape The shape being filled + * @param x The x coordinate of the point being coloured + * @param y The y coordinate of the point being coloured + * @return The colour that should be applied based on the control points of this gradient + */ + public Color colorAt(Shape shape, float x, float y); + + /** + * Get the offset for a vertex at a given location based on it's shape + * + * @param shape The shape being filled + * @param x The x coordinate of the point being drawn + * @param y The y coordinate of the point being drawn + * @return The offset to apply to this vertex + */ + public Vector2f getOffsetAt(Shape shape, float x, float y); +} diff --git a/ext/slick/src/org/newdawn/slick/SlickException.java b/ext/slick/src/org/newdawn/slick/SlickException.java new file mode 100644 index 0000000..3c901f5 --- /dev/null +++ b/ext/slick/src/org/newdawn/slick/SlickException.java @@ -0,0 +1,27 @@ +package org.newdawn.slick; + +/** + * A generic exception thrown by everything in the library + * + * @author kevin + */ +public class SlickException extends Exception { + /** + * Create a new exception with a detail message + * + * @param message The message describing the cause of this exception + */ + public SlickException(String message) { + super(message); + } + + /** + * Create a new exception with a detail message + * + * @param message The message describing the cause of this exception + * @param e The exception causing this exception to be thrown + */ + public SlickException(String message, Throwable e) { + super(message, e); + } +} diff --git a/ext/slick/src/org/newdawn/slick/Sound.java b/ext/slick/src/org/newdawn/slick/Sound.java new file mode 100644 index 0000000..4e6df95 --- /dev/null +++ b/ext/slick/src/org/newdawn/slick/Sound.java @@ -0,0 +1,146 @@ +package org.newdawn.slick; + +import java.net.URL; + +import org.newdawn.slick.openal.Audio; +import org.newdawn.slick.openal.SoundStore; +import org.newdawn.slick.util.Log; + +/** + * A single sound effect loaded from either OGG or XM/MOD file. Sounds are allocated to + * channels dynamically - if not channel is available the sound will not play. + * + * @author kevin + */ +public class Sound { + /** The internal sound effect represent this sound */ + private Audio sound; + + /** + * Create a new Sound + * + * @param url The location of the OGG or MOD/XM to load + * @throws SlickException Indicates a failure to load the sound effect + */ + public Sound(URL url) throws SlickException { + SoundStore.get().init(); + String ref = url.getFile(); + + try { + if (ref.toLowerCase().endsWith(".ogg")) { + sound = SoundStore.get().getOgg(url.openStream()); + } else if (ref.toLowerCase().endsWith(".wav")) { + sound = SoundStore.get().getWAV(url.openStream()); + } else if (ref.toLowerCase().endsWith(".aif")) { + sound = SoundStore.get().getAIF(url.openStream()); + } else if (ref.toLowerCase().endsWith(".xm") || ref.toLowerCase().endsWith(".mod")) { + sound = SoundStore.get().getMOD(url.openStream()); + } else { + throw new SlickException("Only .xm, .mod, .aif, .wav and .ogg are currently supported."); + } + } catch (Exception e) { + Log.error(e); + throw new SlickException("Failed to load sound: "+ref); + } + } + + /** + * Create a new Sound + * + * @param ref The location of the OGG or MOD/XM to load + * @throws SlickException Indicates a failure to load the sound effect + */ + public Sound(String ref) throws SlickException { + SoundStore.get().init(); + + try { + if (ref.toLowerCase().endsWith(".ogg")) { + sound = SoundStore.get().getOgg(ref); + } else if (ref.toLowerCase().endsWith(".wav")) { + sound = SoundStore.get().getWAV(ref); + } else if (ref.toLowerCase().endsWith(".aif")) { + sound = SoundStore.get().getAIF(ref); + } else if (ref.toLowerCase().endsWith(".xm") || ref.toLowerCase().endsWith(".mod")) { + sound = SoundStore.get().getMOD(ref); + } else { + throw new SlickException("Only .xm, .mod, .aif, .wav and .ogg are currently supported."); + } + } catch (Exception e) { + Log.error(e); + throw new SlickException("Failed to load sound: "+ref); + } + } + + /** + * Play this sound effect at default volume and pitch + */ + public void play() { + play(1.0f, 1.0f); + } + + /** + * Play this sound effect at a given volume and pitch + * + * @param pitch The pitch to play the sound effect at + * @param volume The volumen to play the sound effect at + */ + public void play(float pitch, float volume) { + sound.playAsSoundEffect(pitch, volume * SoundStore.get().getSoundVolume(), false); + } + + /** + * Play a sound effect from a particular location + * + * @param x The x position of the source of the effect + * @param y The y position of the source of the effect + * @param z The z position of the source of the effect + */ + public void playAt(float x, float y, float z) { + playAt(1.0f, 1.0f, x,y,z); + } + + /** + * Play a sound effect from a particular location + * + * @param pitch The pitch to play the sound effect at + * @param volume The volumen to play the sound effect at + * @param x The x position of the source of the effect + * @param y The y position of the source of the effect + * @param z The z position of the source of the effect + */ + public void playAt(float pitch, float volume, float x, float y, float z) { + sound.playAsSoundEffect(pitch, volume * SoundStore.get().getSoundVolume(), false, x,y,z); + } + /** + * Loop this sound effect at default volume and pitch + */ + public void loop() { + loop(1.0f, 1.0f); + } + + /** + * Loop this sound effect at a given volume and pitch + * + * @param pitch The pitch to play the sound effect at + * @param volume The volumen to play the sound effect at + */ + public void loop(float pitch, float volume) { + sound.playAsSoundEffect(pitch, volume * SoundStore.get().getSoundVolume(), true); + } + + /** + * Check if the sound is currently playing + * + * @return True if the sound is playing + */ + public boolean playing() { + return sound.isPlaying(); + } + + /** + * Stop the sound being played + */ + public void stop() { + sound.stop(); + } +} diff --git a/ext/slick/src/org/newdawn/slick/SpriteSheet.java b/ext/slick/src/org/newdawn/slick/SpriteSheet.java new file mode 100644 index 0000000..a3f8809 --- /dev/null +++ b/ext/slick/src/org/newdawn/slick/SpriteSheet.java @@ -0,0 +1,288 @@ +package org.newdawn.slick; + +import java.io.InputStream; + +import org.newdawn.slick.opengl.Texture; + +/** + * A sheet of sprites that can be drawn individually + * + * @author Kevin Glass + */ +public class SpriteSheet extends Image { + /** The width of a single element in pixels */ + private int tw; + /** The height of a single element in pixels */ + private int th; + /** The margin of the image */ + private int margin = 0; + /** Subimages */ + private Image[][] subImages; + /** The spacing between tiles */ + private int spacing; + /** The target image for this sheet */ + private Image target; + + /** + * Create a new sprite sheet based on a image location + * + * @param image The image to based the sheet of + * @param tw The width of the tiles on the sheet + * @param th The height of the tiles on the sheet + */ + public SpriteSheet(Image image,int tw,int th) { + super(image); + + this.target = image; + this.tw = tw; + this.th = th; + + // call init manually since constructing from an image will have previously initialised + // from incorrect values + initImpl(); + } + + /** + * Create a new sprite sheet based on a image location + * + * @param image The image to based the sheet of + * @param tw The width of the tiles on the sheet + * @param th The height of the tiles on the sheet + * @param spacing The spacing between tiles + * @param margin The magrin around the tiles + */ + public SpriteSheet(Image image,int tw,int th,int spacing,int margin) { + super(image); + + this.target = image; + this.tw = tw; + this.th = th; + this.spacing = spacing; + this.margin = margin; + + // call init manually since constructing from an image will have previously initialised + // from incorrect values + initImpl(); + } + + /** + * Create a new sprite sheet based on a image location + * + * @param image The image to based the sheet of + * @param tw The width of the tiles on the sheet + * @param th The height of the tiles on the sheet + * @param spacing The spacing between tiles + */ + public SpriteSheet(Image image,int tw,int th,int spacing) { + this(image,tw,th,spacing,0); + } + + /** + * Create a new sprite sheet based on a image location + * + * @param ref The location of the sprite sheet to load + * @param tw The width of the tiles on the sheet + * @param th The height of the tiles on the sheet + * @param spacing The spacing between tiles + * @throws SlickException Indicates a failure to load the image + */ + public SpriteSheet(String ref,int tw,int th, int spacing) throws SlickException { + this(ref,tw,th,null,spacing); + } + + /** + * Create a new sprite sheet based on a image location + * + * @param ref The location of the sprite sheet to load + * @param tw The width of the tiles on the sheet + * @param th The height of the tiles on the sheet + * @throws SlickException Indicates a failure to load the image + */ + public SpriteSheet(String ref,int tw,int th) throws SlickException { + this(ref,tw,th,null); + } + + /** + * Create a new sprite sheet based on a image location + * + * @param ref The location of the sprite sheet to load + * @param tw The width of the tiles on the sheet + * @param th The height of the tiles on the sheet + * @param col The colour to treat as transparent + * @throws SlickException Indicates a failure to load the image + */ + public SpriteSheet(String ref,int tw,int th, Color col) throws SlickException { + this(ref, tw, th, col, 0); + } + + /** + * Create a new sprite sheet based on a image location + * + * @param ref The location of the sprite sheet to load + * @param tw The width of the tiles on the sheet + * @param th The height of the tiles on the sheet + * @param col The colour to treat as transparent + * @param spacing The spacing between tiles + * @throws SlickException Indicates a failure to load the image + */ + public SpriteSheet(String ref,int tw,int th, Color col, int spacing) throws SlickException { + super(ref, false, FILTER_NEAREST, col); + + this.target = this; + this.tw = tw; + this.th = th; + this.spacing = spacing; + } + + /** + * Create a new sprite sheet based on a image location + * + * @param name The name to give to the image in the image cache + * @param ref The stream from which we can load the image + * @param tw The width of the tiles on the sheet + * @param th The height of the tiles on the sheet + * @throws SlickException Indicates a failure to load the image + */ + public SpriteSheet(String name, InputStream ref,int tw,int th) throws SlickException { + super(ref,name,false); + + this.target = this; + this.tw = tw; + this.th = th; + } + + /** + * @see org.newdawn.slick.Image#initImpl() + */ + protected void initImpl() { + if (subImages != null) { + return; + } + + int tilesAcross = ((getWidth()-(margin*2) - tw) / (tw + spacing)) + 1; + int tilesDown = ((getHeight()-(margin*2) - th) / (th + spacing)) + 1; + if ((getHeight() - th) % (th+spacing) != 0) { + tilesDown++; + } + + subImages = new Image[tilesAcross][tilesDown]; + for (int x=0;x= subImages.length)) { + throw new RuntimeException("SubImage out of sheet bounds: "+x+","+y); + } + if ((y < 0) || (y >= subImages[0].length)) { + throw new RuntimeException("SubImage out of sheet bounds: "+x+","+y); + } + + return subImages[x][y]; + } + + /** + * Get a sprite at a particular cell on the sprite sheet + * + * @param x The x position of the cell on the sprite sheet + * @param y The y position of the cell on the sprite sheet + * @return The single image from the sprite sheet + */ + public Image getSprite(int x, int y) { + target.init(); + initImpl(); + + if ((x < 0) || (x >= subImages.length)) { + throw new RuntimeException("SubImage out of sheet bounds: "+x+","+y); + } + if ((y < 0) || (y >= subImages[0].length)) { + throw new RuntimeException("SubImage out of sheet bounds: "+x+","+y); + } + + return target.getSubImage(x*(tw+spacing) + margin, y*(th+spacing) + margin,tw,th); + } + + /** + * Get the number of sprites across the sheet + * + * @return The number of sprites across the sheet + */ + public int getHorizontalCount() { + target.init(); + initImpl(); + + return subImages.length; + } + + /** + * Get the number of sprites down the sheet + * + * @return The number of sprite down the sheet + */ + public int getVerticalCount() { + target.init(); + initImpl(); + + return subImages[0].length; + } + + /** + * Render a sprite when this sprite sheet is in use. + * + * @see #startUse() + * @see #endUse() + * + * @param x The x position to render the sprite at + * @param y The y position to render the sprite at + * @param sx The x location of the cell to render + * @param sy The y location of the cell to render + */ + public void renderInUse(int x,int y,int sx,int sy) { + subImages[sx][sy].drawEmbedded(x, y, tw, th); + } + + /** + * @see org.newdawn.slick.Image#endUse() + */ + public void endUse() { + if (target == this) { + super.endUse(); + return; + } + target.endUse(); + } + + /** + * @see org.newdawn.slick.Image#startUse() + */ + public void startUse() { + if (target == this) { + super.startUse(); + return; + } + target.startUse(); + } + + /** + * @see org.newdawn.slick.Image#setTexture(org.newdawn.slick.opengl.Texture) + */ + public void setTexture(Texture texture) { + if (target == this) { + super.setTexture(texture); + return; + } + target.setTexture(texture); + } +} diff --git a/ext/slick/src/org/newdawn/slick/SpriteSheetFont.java b/ext/slick/src/org/newdawn/slick/SpriteSheetFont.java new file mode 100644 index 0000000..16e392a --- /dev/null +++ b/ext/slick/src/org/newdawn/slick/SpriteSheetFont.java @@ -0,0 +1,111 @@ +package org.newdawn.slick; + +import java.io.UnsupportedEncodingException; + +import org.newdawn.slick.util.Log; + +/** + * A font implementation that will use the graphics inside a SpriteSheet for its data. + * This is useful when your font has a fixed width and height for each character as + * opposed to the more complex AngelCodeFont that allows different sizes and kerning + * for each character. + * + * @author Onno Scheffers + */ +public class SpriteSheetFont implements Font { + /** The SpriteSheet containing the bitmap font */ + private SpriteSheet font; + /** First character in the SpriteSheet */ + private char startingCharacter; + /** Width of each character in pixels */ + private int charWidth; + /** Height of each character in pixels */ + private int charHeight; + /** Number of characters in SpriteSheet horizontally */ + private int horizontalCount; + /** Total number of characters in SpriteSheet */ + private int numChars; + + /** + * Create a new font based on a SpriteSheet. The SpriteSheet should hold your + * fixed-width character set in ASCII order. To only get upper-case characters + * working you would usually set up a SpriteSheet with characters for these values: + *
+	 *   !"#$%&'()*+,-./
+	 *  0123456789:;<=>?
+	 *  @ABCDEFGHIJKLMNO
+	 *  PQRSTUVWXYZ[\]^_
+	 * In this set, ' ' (SPACE) would be the startingCharacter of your characterSet.
+	 *
+	 * @param font              The SpriteSheet holding the font data.
+	 * @param startingCharacter The first character that is defined in the SpriteSheet.
+	 */
+	public SpriteSheetFont(SpriteSheet font, char startingCharacter) {
+		this.font = font;
+		this.startingCharacter = startingCharacter;
+		horizontalCount = font.getHorizontalCount();
+		int verticalCount = font.getVerticalCount();
+		charWidth = font.getWidth() / horizontalCount;
+		charHeight = font.getHeight() / verticalCount;
+		numChars = horizontalCount * verticalCount;
+	}
+
+	/**
+	 * @see org.newdawn.slick.Font#drawString(float, float, java.lang.String)
+	 */
+	public void drawString(float x, float y, String text) {
+		drawString(x, y, text, Color.white);
+	}
+
+	/**
+	 * @see org.newdawn.slick.Font#drawString(float, float, java.lang.String, org.newdawn.slick.Color)
+	 */
+	public void drawString(float x, float y, String text, Color col) {
+		drawString(x,y,text,col,0,text.length()-1);
+	}
+	
+	/**
+	 * @see Font#drawString(float, float, String, Color, int, int)
+	 */
+	public void drawString(float x, float y, String text, Color col, int startIndex, int endIndex) {
+		try {
+			byte[] data = text.getBytes("US-ASCII");
+			for (int i = 0; i < data.length; i++) {
+				int index = data[i] - startingCharacter;
+				if (index < numChars) {
+					int xPos = (index % horizontalCount);
+					int yPos = (index / horizontalCount);
+					
+					if ((i >= startIndex) || (i <= endIndex)) {
+						font.getSprite(xPos, yPos)
+								.draw(x + (i * charWidth), y, col);
+					}
+				}
+			}
+		} catch (UnsupportedEncodingException e) {
+			// Should never happen, ASCII is supported pretty much anywhere
+			Log.error(e);
+		}
+	}
+
+	/**
+	 * @see org.newdawn.slick.Font#getHeight(java.lang.String)
+	 */
+	public int getHeight(String text) {
+		return charHeight;
+	}
+
+	/**
+	 * @see org.newdawn.slick.Font#getWidth(java.lang.String)
+	 */
+	public int getWidth(String text) {
+		return charWidth * text.length();
+	}
+
+	/**
+	 * @see org.newdawn.slick.Font#getLineHeight()
+	 */
+	public int getLineHeight() {
+		return charHeight;
+	}
+}
diff --git a/ext/slick/src/org/newdawn/slick/TrueTypeFont.java b/ext/slick/src/org/newdawn/slick/TrueTypeFont.java
new file mode 100644
index 0000000..5bf65c9
--- /dev/null
+++ b/ext/slick/src/org/newdawn/slick/TrueTypeFont.java
@@ -0,0 +1,413 @@
+package org.newdawn.slick;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.FontMetrics;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.newdawn.slick.opengl.GLUtils;
+import org.newdawn.slick.opengl.Texture;
+import org.newdawn.slick.opengl.renderer.Renderer;
+import org.newdawn.slick.opengl.renderer.SGL;
+import org.newdawn.slick.util.BufferedImageUtil;
+
+/**
+ * A TrueType font implementation for Slick
+ * 
+ * @author James Chambers (Jimmy)
+ * @author Jeremy Adams (elias4444)
+ * @author Kevin Glass (kevglass)
+ * @author Peter Korzuszek (genail)
+ * 
+ * @deprecated Use {@link UnicodeFont} instead
+ */
+public class TrueTypeFont implements org.newdawn.slick.Font {
+	/** The renderer to use for all GL operations */
+	private static final SGL GL = Renderer.get();
+
+	/** Array that holds necessary information about the font characters */
+	private IntObject[] charArray = new IntObject[256];
+	
+	/** Map of user defined font characters (Character <-> IntObject) */
+	private Map customChars = new HashMap();
+
+	/** Boolean flag on whether AntiAliasing is enabled or not */
+	private boolean antiAlias;
+
+	/** Font's size */
+	private int fontSize = 0;
+
+	/** Font's height */
+	private int fontHeight = 0;
+
+	/** Texture used to cache the font 0-255 characters */
+	private Texture fontTexture;
+	
+	/** Default font texture width */
+	private int textureWidth = 512;
+
+	/** Default font texture height */
+	private int textureHeight = 512;
+
+	/** A reference to Java's AWT Font that we create our font texture from */
+	private java.awt.Font font;
+
+	/** The font metrics for our Java AWT font */
+	private FontMetrics fontMetrics;
+
+	/**
+	 * This is a special internal class that holds our necessary information for
+	 * the font characters. This includes width, height, and where the character
+	 * is stored on the font texture.
+	 */
+	private class IntObject {
+		/** Character's width */
+		public int width;
+
+		/** Character's height */
+		public int height;
+
+		/** Character's stored x position */
+		public int storedX;
+
+		/** Character's stored y position */
+		public int storedY;
+	}
+
+	/**
+	 * Constructor for the TrueTypeFont class Pass in the preloaded standard
+	 * Java TrueType font, and whether you want it to be cached with
+	 * AntiAliasing applied.
+	 * 
+	 * @param font
+	 *            Standard Java AWT font
+	 * @param antiAlias
+	 *            Whether or not to apply AntiAliasing to the cached font
+	 * @param additionalChars
+	 *            Characters of font that will be used in addition of first 256 (by unicode).
+	 */
+	public TrueTypeFont(java.awt.Font font, boolean antiAlias, char[] additionalChars) {
+		GLUtils.checkGLContext();
+		
+		this.font = font;
+		this.fontSize = font.getSize();
+		this.antiAlias = antiAlias;
+
+		createSet( additionalChars );
+	}
+	
+	/**
+	 * Constructor for the TrueTypeFont class Pass in the preloaded standard
+	 * Java TrueType font, and whether you want it to be cached with
+	 * AntiAliasing applied.
+	 * 
+	 * @param font
+	 *            Standard Java AWT font
+	 * @param antiAlias
+	 *            Whether or not to apply AntiAliasing to the cached font
+	 */
+	public TrueTypeFont(java.awt.Font font, boolean antiAlias) {
+		this( font, antiAlias, null );
+	}
+
+	/**
+	 * Create a standard Java2D BufferedImage of the given character
+	 * 
+	 * @param ch
+	 *            The character to create a BufferedImage for
+	 * 
+	 * @return A BufferedImage containing the character
+	 */
+	private BufferedImage getFontImage(char ch) {
+		// Create a temporary image to extract the character's size
+		BufferedImage tempfontImage = new BufferedImage(1, 1,
+				BufferedImage.TYPE_INT_ARGB);
+		Graphics2D g = (Graphics2D) tempfontImage.getGraphics();
+		if (antiAlias == true) {
+			g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+					RenderingHints.VALUE_ANTIALIAS_ON);
+		}
+		g.setFont(font);
+		fontMetrics = g.getFontMetrics();
+		int charwidth = fontMetrics.charWidth(ch);
+
+		if (charwidth <= 0) {
+			charwidth = 1;
+		}
+		int charheight = fontMetrics.getHeight();
+		if (charheight <= 0) {
+			charheight = fontSize;
+		}
+
+		// Create another image holding the character we are creating
+		BufferedImage fontImage;
+		fontImage = new BufferedImage(charwidth, charheight,
+				BufferedImage.TYPE_INT_ARGB);
+		Graphics2D gt = (Graphics2D) fontImage.getGraphics();
+		if (antiAlias == true) {
+			gt.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+					RenderingHints.VALUE_ANTIALIAS_ON);
+		}
+		gt.setFont(font);
+
+		gt.setColor(Color.WHITE);
+		int charx = 0;
+		int chary = 0;
+		gt.drawString(String.valueOf(ch), (charx), (chary)
+				+ fontMetrics.getAscent());
+
+		return fontImage;
+
+	}
+
+	/**
+	 * Create and store the font
+	 * 
+	 * @param customCharsArray Characters that should be also added to the cache.
+	 */
+	private void createSet( char[] customCharsArray ) {
+		// If there are custom chars then I expand the font texture twice		
+		if	(customCharsArray != null && customCharsArray.length > 0) {
+			textureWidth *= 2;
+		}
+		
+		// In any case this should be done in other way. Texture with size 512x512
+		// can maintain only 256 characters with resolution of 32x32. The texture
+		// size should be calculated dynamicaly by looking at character sizes. 
+		
+		try {
+			
+			BufferedImage imgTemp = new BufferedImage(textureWidth, textureHeight, BufferedImage.TYPE_INT_ARGB);
+			Graphics2D g = (Graphics2D) imgTemp.getGraphics();
+
+			g.setColor(new Color(255,255,255,1));
+			g.fillRect(0,0,textureWidth,textureHeight);
+			
+			int rowHeight = 0;
+			int positionX = 0;
+			int positionY = 0;
+			
+			int customCharsLength = ( customCharsArray != null ) ? customCharsArray.length : 0; 
+
+			for (int i = 0; i < 256 + customCharsLength; i++) {
+				
+				// get 0-255 characters and then custom characters
+				char ch = ( i < 256 ) ? (char) i : customCharsArray[i-256];
+				
+				BufferedImage fontImage = getFontImage(ch);
+
+				IntObject newIntObject = new IntObject();
+
+				newIntObject.width = fontImage.getWidth();
+				newIntObject.height = fontImage.getHeight();
+
+				if (positionX + newIntObject.width >= textureWidth) {
+					positionX = 0;
+					positionY += rowHeight;
+					rowHeight = 0;
+				}
+
+				newIntObject.storedX = positionX;
+				newIntObject.storedY = positionY;
+
+				if (newIntObject.height > fontHeight) {
+					fontHeight = newIntObject.height;
+				}
+
+				if (newIntObject.height > rowHeight) {
+					rowHeight = newIntObject.height;
+				}
+
+				// Draw it here
+				g.drawImage(fontImage, positionX, positionY, null);
+
+				positionX += newIntObject.width;
+
+				if( i < 256 ) { // standard characters
+					charArray[i] = newIntObject;
+				} else { // custom characters
+					customChars.put( new Character( ch ), newIntObject );
+				}
+
+				fontImage = null;
+			}
+
+			fontTexture = BufferedImageUtil
+					.getTexture(font.toString(), imgTemp);
+
+		} catch (IOException e) {
+			System.err.println("Failed to create font.");
+			e.printStackTrace();
+		}
+	}
+	
+	
+	/**
+	 * Draw a textured quad
+	 * 
+	 * @param drawX
+	 *            The left x position to draw to
+	 * @param drawY
+	 *            The top y position to draw to
+	 * @param drawX2
+	 *            The right x position to draw to
+	 * @param drawY2
+	 *            The bottom y position to draw to
+	 * @param srcX
+	 *            The left source x position to draw from
+	 * @param srcY
+	 *            The top source y position to draw from
+	 * @param srcX2
+	 *            The right source x position to draw from
+	 * @param srcY2
+	 *            The bottom source y position to draw from
+	 */
+	private void drawQuad(float drawX, float drawY, float drawX2, float drawY2,
+			float srcX, float srcY, float srcX2, float srcY2) {
+		float DrawWidth = drawX2 - drawX;
+		float DrawHeight = drawY2 - drawY;
+		float TextureSrcX = srcX / textureWidth;
+		float TextureSrcY = srcY / textureHeight;
+		float SrcWidth = srcX2 - srcX;
+		float SrcHeight = srcY2 - srcY;
+		float RenderWidth = (SrcWidth / textureWidth);
+		float RenderHeight = (SrcHeight / textureHeight);
+
+		GL.glTexCoord2f(TextureSrcX, TextureSrcY);
+		GL.glVertex2f(drawX, drawY);
+		GL.glTexCoord2f(TextureSrcX, TextureSrcY + RenderHeight);
+		GL.glVertex2f(drawX, drawY + DrawHeight);
+		GL.glTexCoord2f(TextureSrcX + RenderWidth, TextureSrcY + RenderHeight);
+		GL.glVertex2f(drawX + DrawWidth, drawY + DrawHeight);
+		GL.glTexCoord2f(TextureSrcX + RenderWidth, TextureSrcY);
+		GL.glVertex2f(drawX + DrawWidth, drawY);
+	}
+
+	/**
+	 * Get the width of a given String
+	 * 
+	 * @param whatchars
+	 *            The characters to get the width of
+	 * 
+	 * @return The width of the characters
+	 */
+	public int getWidth(String whatchars) {
+		int totalwidth = 0;
+		IntObject intObject = null;
+		int currentChar = 0;
+		for (int i = 0; i < whatchars.length(); i++) {
+			currentChar = whatchars.charAt(i);
+			if (currentChar < 256) {
+				intObject = charArray[currentChar];
+			} else {
+				intObject = (IntObject)customChars.get( new Character( (char) currentChar ) );
+			}
+			
+			if( intObject != null )
+				totalwidth += intObject.width;
+		}
+		return totalwidth;
+	}
+
+	/**
+	 * Get the font's height
+	 * 
+	 * @return The height of the font
+	 */
+	public int getHeight() {
+		return fontHeight;
+	}
+
+	/**
+	 * Get the height of a String
+	 * 
+	 * @return The height of a given string
+	 */
+	public int getHeight(String HeightString) {
+		return fontHeight;
+	}
+
+	/**
+	 * Get the font's line height
+	 * 
+	 * @return The line height of the font
+	 */
+	public int getLineHeight() {
+		return fontHeight;
+	}
+
+	/**
+	 * Draw a string
+	 * 
+	 * @param x
+	 *            The x position to draw the string
+	 * @param y
+	 *            The y position to draw the string
+	 * @param whatchars
+	 *            The string to draw
+	 * @param color
+	 *            The color to draw the text
+	 */
+	public void drawString(float x, float y, String whatchars,
+			org.newdawn.slick.Color color) {
+		drawString(x,y,whatchars,color,0,whatchars.length()-1);
+	}
+	
+	/**
+	 * @see Font#drawString(float, float, String, org.newdawn.slick.Color, int, int)
+	 */
+	public void drawString(float x, float y, String whatchars,
+			org.newdawn.slick.Color color, int startIndex, int endIndex) {
+		color.bind();
+		fontTexture.bind();
+
+		IntObject intObject = null;
+		int charCurrent;
+
+		GL.glBegin(SGL.GL_QUADS);
+
+		int totalwidth = 0;
+		for (int i = 0; i < whatchars.length(); i++) {
+			charCurrent = whatchars.charAt(i);
+			if (charCurrent < 256) {
+				intObject = charArray[charCurrent];
+			} else {
+				intObject = (IntObject)customChars.get( new Character( (char) charCurrent ) );
+			} 
+			
+			if( intObject != null ) {
+				if ((i >= startIndex) || (i <= endIndex)) {
+					drawQuad((x + totalwidth), y,
+							(x + totalwidth + intObject.width),
+							(y + intObject.height), intObject.storedX,
+							intObject.storedY, intObject.storedX + intObject.width,
+							intObject.storedY + intObject.height);
+				}
+				totalwidth += intObject.width;
+			}
+		}
+
+		GL.glEnd();
+	}
+
+	/**
+	 * Draw a string
+	 * 
+	 * @param x
+	 *            The x position to draw the string
+	 * @param y
+	 *            The y position to draw the string
+	 * @param whatchars
+	 *            The string to draw
+	 */
+	public void drawString(float x, float y, String whatchars) {
+		drawString(x, y, whatchars, org.newdawn.slick.Color.white);
+	}
+
+}
\ No newline at end of file
diff --git a/ext/slick/src/org/newdawn/slick/UnicodeFont.java b/ext/slick/src/org/newdawn/slick/UnicodeFont.java
new file mode 100644
index 0000000..6d35536
--- /dev/null
+++ b/ext/slick/src/org/newdawn/slick/UnicodeFont.java
@@ -0,0 +1,980 @@
+
+package org.newdawn.slick;
+
+import java.awt.Font;
+import java.awt.FontFormatException;
+import java.awt.FontMetrics;
+import java.awt.Rectangle;
+import java.awt.font.GlyphVector;
+import java.awt.font.TextAttribute;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.newdawn.slick.font.Glyph;
+import org.newdawn.slick.font.GlyphPage;
+import org.newdawn.slick.font.HieroSettings;
+import org.newdawn.slick.opengl.Texture;
+import org.newdawn.slick.opengl.TextureImpl;
+import org.newdawn.slick.opengl.renderer.Renderer;
+import org.newdawn.slick.opengl.renderer.SGL;
+import org.newdawn.slick.util.ResourceLoader;
+
+/**
+ * A Slick bitmap font that can display unicode glyphs from a TrueTypeFont.
+ * 
+ * For efficiency, glyphs are packed on to textures. Glyphs can be loaded to the textures on the fly, when they are first needed
+ * for display. However, it is best to load the glyphs that are known to be needed at startup.
+ * @author Nathan Sweet 
+ */
+public class UnicodeFont implements org.newdawn.slick.Font {
+	/** The number of display lists that will be cached for strings from this font */
+	private static final int DISPLAY_LIST_CACHE_SIZE = 200;
+	/** The highest glyph code allowed */
+	static private final int MAX_GLYPH_CODE = 0x10FFFF;
+	/** The number of glyphs on a page */
+	private static final int PAGE_SIZE = 512;
+	/** The number of pages */
+	private static final int PAGES = MAX_GLYPH_CODE / PAGE_SIZE;
+	/** Interface to OpenGL */
+	private static final SGL GL = Renderer.get();
+	/** A dummy display list used as a place holder */
+	private static final DisplayList EMPTY_DISPLAY_LIST = new DisplayList();
+
+	/**
+	 * Utility to create a Java font for a TTF file reference
+	 * 
+	 * @param ttfFileRef The file system or classpath location of the TrueTypeFont file.
+	 * @return The font created
+	 * @throws SlickException Indicates a failure to locate or load the font into Java's font
+	 * system.
+	 */
+	private static Font createFont (String ttfFileRef) throws SlickException {
+		try {
+			return Font.createFont(Font.TRUETYPE_FONT, ResourceLoader.getResourceAsStream(ttfFileRef));
+		} catch (FontFormatException ex) {
+			throw new SlickException("Invalid font: " + ttfFileRef, ex);
+		} catch (IOException ex) {
+			throw new SlickException("Error reading font: " + ttfFileRef, ex);
+		}
+	}
+
+	/**
+	 * Sorts glyphs by height, tallest first.
+	 */
+	private static final Comparator heightComparator = new Comparator() {
+		public int compare (Object o1, Object o2) {
+			return ((Glyph)o1).getHeight() - ((Glyph)o2).getHeight();
+		}
+	};
+	
+	/** The AWT font that is being rendered */
+	private Font font;
+	/** The reference to the True Type Font file that has kerning information */
+	private String ttfFileRef;
+	/** The ascent of the font */
+	private int ascent;
+	/** The decent of the font */
+	private int descent;
+	/** The leading edge of the font */
+	private int leading;
+	/** The width of a space for the font */
+	private int spaceWidth;
+	/** The glyphs that are available in this font */
+	private final Glyph[][] glyphs = new Glyph[PAGES][];
+	/** The pages that have been loaded for this font */
+	private final List glyphPages = new ArrayList();
+	/** The glyphs queued up to be rendered */
+	private final List queuedGlyphs = new ArrayList(256);
+	/** The effects that need to be applied to the font */
+	private final List effects = new ArrayList();
+	
+	/** The padding applied in pixels to the top of the glyph rendered area */
+	private int paddingTop;
+	/** The padding applied in pixels to the left of the glyph rendered area */
+	private int paddingLeft;
+	/** The padding applied in pixels to the bottom of the glyph rendered area */
+	private int paddingBottom;
+	/** The padding applied in pixels to the right of the glyph rendered area */
+	private int paddingRight;
+	/** The padding applied in pixels to horizontal advance for each glyph */
+	private int paddingAdvanceX;
+	/** The padding applied in pixels to vertical advance for each glyph */
+	private int paddingAdvanceY;
+	/** The glyph to display for missing glyphs in code points */
+	private Glyph missingGlyph;
+
+	/** The width of the glyph page generated */
+	private int glyphPageWidth = 512;
+	/** The height of the glyph page generated */
+	private int glyphPageHeight = 512;
+	
+	/** True if display list caching is turned on */
+	private boolean displayListCaching = true;
+	/** The based display list ID */
+	private int baseDisplayListID = -1;
+	/** The ID of the display list that has been around the longest time */
+	private int eldestDisplayListID;
+	/** The eldest display list  */
+	private DisplayList eldestDisplayList;
+	
+	/** The map fo the display list generated and cached - modified to allow removal of the oldest entry */
+	private final LinkedHashMap displayLists = new LinkedHashMap(DISPLAY_LIST_CACHE_SIZE, 1, true) {
+		protected boolean removeEldestEntry (Entry eldest) {
+			DisplayList displayList = (DisplayList)eldest.getValue();
+			if (displayList != null) eldestDisplayListID = displayList.id;
+			return size() > DISPLAY_LIST_CACHE_SIZE;
+		}
+	};
+
+	/**
+	 * Create a new unicode font based on a TTF file
+	 * 
+	 * @param ttfFileRef The file system or classpath location of the TrueTypeFont file.
+	 * @param hieroFileRef The file system or classpath location of the Hiero settings file.
+	 * @throws SlickException if the UnicodeFont could not be initialized.
+	 */
+	public UnicodeFont (String ttfFileRef, String hieroFileRef) throws SlickException {
+		this(ttfFileRef, new HieroSettings(hieroFileRef));
+	}
+
+	/**
+	 * Create a new unicode font based on a TTF file and a set of heiro configuration
+	 * 
+	 * @param ttfFileRef The file system or classpath location of the TrueTypeFont file.
+	 * @param settings The settings configured via the Hiero tool
+	 * @throws SlickException if the UnicodeFont could not be initialized.
+	 */
+	public UnicodeFont (String ttfFileRef, HieroSettings settings) throws SlickException {
+		this.ttfFileRef = ttfFileRef;
+		Font font = createFont(ttfFileRef);
+		initializeFont(font, settings.getFontSize(), settings.isBold(), settings.isItalic());
+		loadSettings(settings);
+	}
+
+	/**
+	 * Create a new unicode font based on a TTF file alone
+	 * 
+	 * @param ttfFileRef The file system or classpath location of the TrueTypeFont file.
+	 * @param size The point size of the font to generated
+	 * @param bold True if the font should be rendered in bold typeface
+	 * @param italic True if the font should be rendered in bold typeface
+	 * @throws SlickException if the UnicodeFont could not be initialized.
+	 */
+	public UnicodeFont (String ttfFileRef, int size, boolean bold, boolean italic) throws SlickException {
+		this.ttfFileRef = ttfFileRef;
+		initializeFont(createFont(ttfFileRef), size, bold, italic);
+	}
+
+	/**
+	 * Creates a new UnicodeFont.
+	 * 
+	 * @param font The AWT font to render
+	 * @param hieroFileRef The file system or classpath location of the Hiero settings file.
+	 * @throws SlickException if the UnicodeFont could not be initialized.
+	 */
+	public UnicodeFont (Font font, String hieroFileRef) throws SlickException {
+		this(font, new HieroSettings(hieroFileRef));
+	}
+
+	/**
+	 * Creates a new UnicodeFont.
+	 * 
+	 * @param font The AWT font to render
+	 * @param settings The settings configured via the Hiero tool
+	 */
+	public UnicodeFont (Font font, HieroSettings settings) {
+		initializeFont(font, settings.getFontSize(), settings.isBold(), settings.isItalic());
+		loadSettings(settings);
+	}
+
+	/**
+	 * Creates a new UnicodeFont.
+	 * 
+	 * @param font The AWT font to render
+	 */
+	public UnicodeFont (Font font) {
+		initializeFont(font, font.getSize(), font.isBold(), font.isItalic());
+	}
+
+	/**
+	 * Creates a new UnicodeFont.
+	 * 
+	 * @param font The AWT font to render
+	 * @param size The point size of the font to generated
+	 * @param bold True if the font should be rendered in bold typeface
+	 * @param italic True if the font should be rendered in bold typeface
+	 */
+	public UnicodeFont (Font font, int size, boolean bold, boolean italic) {
+		initializeFont(font, size, bold, italic);
+	}
+
+	/**
+	 * Initialise the font to be used based on configuration
+	 * 
+	 * @param baseFont The AWT font to render
+	 * @param size The point size of the font to generated
+	 * @param bold True if the font should be rendered in bold typeface
+	 * @param italic True if the font should be rendered in bold typeface
+	 */
+	private void initializeFont(Font baseFont, int size, boolean bold, boolean italic) {
+		Map attributes = baseFont.getAttributes();
+		attributes.put(TextAttribute.SIZE, new Float(size));
+		attributes.put(TextAttribute.WEIGHT, bold ? TextAttribute.WEIGHT_BOLD : TextAttribute.WEIGHT_REGULAR);
+		attributes.put(TextAttribute.POSTURE, italic ? TextAttribute.POSTURE_OBLIQUE : TextAttribute.POSTURE_REGULAR);
+		try {
+			attributes.put(TextAttribute.class.getDeclaredField("KERNING").get(null), TextAttribute.class.getDeclaredField(
+				"KERNING_ON").get(null));
+		} catch (Exception ignored) {
+		}
+		font = baseFont.deriveFont(attributes);
+
+		FontMetrics metrics = GlyphPage.getScratchGraphics().getFontMetrics(font);
+		ascent = metrics.getAscent();
+		descent = metrics.getDescent();
+		leading = metrics.getLeading();
+		
+		// Determine width of space glyph (getGlyphPixelBounds gives a width of zero).
+		char[] chars = " ".toCharArray();
+		GlyphVector vector = font.layoutGlyphVector(GlyphPage.renderContext, chars, 0, chars.length, Font.LAYOUT_LEFT_TO_RIGHT);
+		spaceWidth = vector.getGlyphLogicalBounds(0).getBounds().width;
+	}
+
+	/**
+	 * Load the hiero setting and configure the unicode font's rendering
+	 * 
+	 * @param settings The settings to be applied
+	 */
+	private void loadSettings(HieroSettings settings) {
+		paddingTop = settings.getPaddingTop();
+		paddingLeft = settings.getPaddingLeft();
+		paddingBottom = settings.getPaddingBottom();
+		paddingRight = settings.getPaddingRight();
+		paddingAdvanceX = settings.getPaddingAdvanceX();
+		paddingAdvanceY = settings.getPaddingAdvanceY();
+		glyphPageWidth = settings.getGlyphPageWidth();
+		glyphPageHeight = settings.getGlyphPageHeight();
+		effects.addAll(settings.getEffects());
+	}
+
+	/**
+	 * Queues the glyphs in the specified codepoint range (inclusive) to be loaded. Note that the glyphs are not actually loaded
+	 * until {@link #loadGlyphs()} is called.
+	 * 
+	 * Some characters like combining marks and non-spacing marks can only be rendered with the context of other glyphs. In this
+	 * case, use {@link #addGlyphs(String)}.
+	 * 
+	 * @param startCodePoint The code point of the first glyph to add
+	 * @param endCodePoint The code point of the last glyph to add
+	 */
+	public void addGlyphs(int startCodePoint, int endCodePoint) {
+		for (int codePoint = startCodePoint; codePoint <= endCodePoint; codePoint++)
+			addGlyphs(new String(Character.toChars(codePoint)));
+	}
+
+	/**
+	 * Queues the glyphs in the specified text to be loaded. Note that the glyphs are not actually loaded until
+	 * {@link #loadGlyphs()} is called.
+	 * 
+	 * @param text The text containing the glyphs to be added
+	 */
+	public void addGlyphs(String text) {
+		if (text == null) throw new IllegalArgumentException("text cannot be null.");
+
+		char[] chars = text.toCharArray();
+		GlyphVector vector = font.layoutGlyphVector(GlyphPage.renderContext, chars, 0, chars.length, Font.LAYOUT_LEFT_TO_RIGHT);
+		for (int i = 0, n = vector.getNumGlyphs(); i < n; i++) {
+			int codePoint = text.codePointAt(vector.getGlyphCharIndex(i));
+			Rectangle bounds = getGlyphBounds(vector, i, codePoint);
+			getGlyph(vector.getGlyphCode(i), codePoint, bounds, vector, i);
+		}
+	}
+
+	/**
+	 * Queues the glyphs in the ASCII character set (codepoints 32 through 255) to be loaded. Note that the glyphs are not actually
+	 * loaded until {@link #loadGlyphs()} is called.
+	 */
+	public void addAsciiGlyphs () {
+		addGlyphs(32, 255);
+	}
+
+	/**
+	 * Queues the glyphs in the NEHE character set (codepoints 32 through 128) to be loaded. Note that the glyphs are not actually
+	 * loaded until {@link #loadGlyphs()} is called.
+	 */
+	public void addNeheGlyphs () {
+		addGlyphs(32, 32 + 96);
+	}
+
+	/**
+	 * Loads all queued glyphs to the backing textures. Glyphs that are typically displayed together should be added and loaded at
+	 * the same time so that they are stored on the same backing texture. This reduces the number of backing texture binds required
+	 * to draw glyphs.
+	 * 
+	 * @return True if the glyphs were loaded entirely
+	 * @throws SlickException if the glyphs could not be loaded.
+	 */
+	public boolean loadGlyphs () throws SlickException {
+		return loadGlyphs(-1);
+	}
+
+	/**
+	 * Loads up to the specified number of queued glyphs to the backing textures. This is typically called from the game loop to
+	 * load glyphs on the fly that were requested for display but have not yet been loaded.
+	 * 
+	 * @param maxGlyphsToLoad The maximum number of glyphs to be loaded this time
+	 * @return True if the glyphs were loaded entirely
+	 * @throws SlickException if the glyphs could not be loaded.
+	 */
+	public boolean loadGlyphs (int maxGlyphsToLoad) throws SlickException {
+		if (queuedGlyphs.isEmpty()) return false;
+
+		if (effects.isEmpty())
+			throw new IllegalStateException("The UnicodeFont must have at least one effect before any glyphs can be loaded.");
+
+		for (Iterator iter = queuedGlyphs.iterator(); iter.hasNext();) {
+			Glyph glyph = (Glyph)iter.next();
+			int codePoint = glyph.getCodePoint();
+
+			// Don't load an image for a glyph with nothing to display.
+			if (glyph.getWidth() == 0 || codePoint == ' ') {
+				iter.remove();
+				continue;
+			}
+
+			// Only load the first missing glyph.
+			if (glyph.isMissing()) {
+				if (missingGlyph != null) {
+					if (glyph != missingGlyph) iter.remove();
+					continue;
+				}
+				missingGlyph = glyph;
+			}
+		}
+
+		Collections.sort(queuedGlyphs, heightComparator);
+
+		// Add to existing pages.
+		for (Iterator iter = glyphPages.iterator(); iter.hasNext();) {
+			GlyphPage glyphPage = (GlyphPage)iter.next();
+			maxGlyphsToLoad -= glyphPage.loadGlyphs(queuedGlyphs, maxGlyphsToLoad);
+			if (maxGlyphsToLoad == 0 || queuedGlyphs.isEmpty())
+				return true;
+		}
+
+		// Add to new pages.
+		while (!queuedGlyphs.isEmpty()) {
+			GlyphPage glyphPage = new GlyphPage(this, glyphPageWidth, glyphPageHeight);
+			glyphPages.add(glyphPage);
+			maxGlyphsToLoad -= glyphPage.loadGlyphs(queuedGlyphs, maxGlyphsToLoad);
+			if (maxGlyphsToLoad == 0) return true;
+		}
+
+		return true;
+	}
+
+	/**
+	 * Clears all loaded and queued glyphs.
+	 */
+	public void clearGlyphs () {
+		for (int i = 0; i < PAGES; i++)
+			glyphs[i] = null;
+
+		for (Iterator iter = glyphPages.iterator(); iter.hasNext();) {
+			GlyphPage page = (GlyphPage)iter.next();
+			try {
+				page.getImage().destroy();
+			} catch (SlickException ignored) {
+			}
+		}
+		glyphPages.clear();
+
+		if (baseDisplayListID != -1) {
+			GL.glDeleteLists(baseDisplayListID, displayLists.size());
+			baseDisplayListID = -1;
+		}
+
+		queuedGlyphs.clear();
+		missingGlyph = null;
+	}
+
+	/**
+	 * Releases all resources used by this UnicodeFont. This method should be called when this UnicodeFont instance is no longer
+	 * needed.
+	 */
+	public void destroy () {
+		// The destroy() method is just to provide a consistent API for releasing resources.
+		clearGlyphs();
+	}
+
+	/**
+	 * Identical to {@link #drawString(float, float, String, Color, int, int)} but returns a 
+	 * DisplayList which provides access to the width and height of the text drawn.
+	 * 
+	 * @param text The text to render
+	 * @param x The horizontal location to render at
+	 * @param y The vertical location to render at
+	 * @param color The colour to apply as a filter on the text
+	 * @param startIndex The start index into the string to start rendering at
+	 * @param endIndex The end index into the string to render to
+	 * @return The reference to the display list that was drawn and potentiall ygenerated
+	 */
+	public DisplayList drawDisplayList (float x, float y, String text, Color color, int startIndex, int endIndex) {
+		if (text == null) throw new IllegalArgumentException("text cannot be null.");
+		if (text.length() == 0) return EMPTY_DISPLAY_LIST;
+		if (color == null) throw new IllegalArgumentException("color cannot be null.");
+
+		x -= paddingLeft;
+		y -= paddingTop;
+
+		String displayListKey = text.substring(startIndex, endIndex);
+
+		color.bind();
+		TextureImpl.bindNone();
+
+		DisplayList displayList = null;
+		if (displayListCaching && queuedGlyphs.isEmpty()) {
+			if (baseDisplayListID == -1) {
+				baseDisplayListID = GL.glGenLists(DISPLAY_LIST_CACHE_SIZE);
+				if (baseDisplayListID == 0) {
+					baseDisplayListID = -1;
+					displayListCaching = false;
+					return new DisplayList();
+				}
+			}
+			// Try to use a display list compiled for this text.
+			displayList = (DisplayList)displayLists.get(displayListKey);
+			if (displayList != null) {
+				if (displayList.invalid)
+					displayList.invalid = false;
+				else {
+					GL.glTranslatef(x, y, 0);
+					GL.glCallList(displayList.id);
+					GL.glTranslatef(-x, -y, 0);
+					return displayList;
+				}
+			} else if (displayList == null) {
+				// Compile a new display list.
+				displayList = new DisplayList();
+				int displayListCount = displayLists.size();
+				displayLists.put(displayListKey, displayList);
+				if (displayListCount < DISPLAY_LIST_CACHE_SIZE)
+					displayList.id = baseDisplayListID + displayListCount;
+				else
+					displayList.id = eldestDisplayListID;
+			}
+			displayLists.put(displayListKey, displayList);
+		}
+
+		GL.glTranslatef(x, y, 0);
+
+		if (displayList != null) GL.glNewList(displayList.id, SGL.GL_COMPILE_AND_EXECUTE);
+
+		char[] chars = text.substring(0, endIndex).toCharArray();
+		GlyphVector vector = font.layoutGlyphVector(GlyphPage.renderContext, chars, 0, chars.length, Font.LAYOUT_LEFT_TO_RIGHT);
+
+		int maxWidth = 0, totalHeight = 0, lines = 0;
+		int extraX = 0, extraY = ascent;
+		boolean startNewLine = false;
+		Texture lastBind = null;
+		for (int glyphIndex = 0, n = vector.getNumGlyphs(); glyphIndex < n; glyphIndex++) {
+			int charIndex = vector.getGlyphCharIndex(glyphIndex);
+			if (charIndex < startIndex) continue;
+			if (charIndex > endIndex) break;
+
+			int codePoint = text.codePointAt(charIndex);
+
+			Rectangle bounds = getGlyphBounds(vector, glyphIndex, codePoint);
+			Glyph glyph = getGlyph(vector.getGlyphCode(glyphIndex), codePoint, bounds, vector, glyphIndex);
+
+			if (startNewLine && codePoint != '\n') {
+				extraX = -bounds.x;
+				startNewLine = false;
+			}
+
+			Image image = glyph.getImage();
+			if (image == null && missingGlyph != null && glyph.isMissing()) image = missingGlyph.getImage();
+			if (image != null) {
+				// Draw glyph, only binding a new glyph page texture when necessary.
+				Texture texture = image.getTexture();
+				if (lastBind != null && lastBind != texture) {
+					GL.glEnd();
+					lastBind = null;
+				}
+				if (lastBind == null) {
+					texture.bind();
+					GL.glBegin(SGL.GL_QUADS);
+					lastBind = texture;
+				}
+				image.drawEmbedded(bounds.x + extraX, bounds.y + extraY, image.getWidth(), image.getHeight());
+			}
+
+			if (glyphIndex > 0) extraX += paddingRight + paddingLeft + paddingAdvanceX;
+			maxWidth = Math.max(maxWidth, bounds.x + extraX + bounds.width);
+			totalHeight = Math.max(totalHeight, ascent + bounds.y + bounds.height);
+
+			if (codePoint == '\n') {
+				startNewLine = true; // Mac gives -1 for bounds.x of '\n', so use the bounds.x of the next glyph.
+				extraY += getLineHeight();
+				lines++;
+				totalHeight = 0;
+			}
+		}
+		if (lastBind != null) GL.glEnd();
+
+		if (displayList != null) {
+			GL.glEndList();
+			// Invalidate the display list if it had glyphs that need to be loaded.
+			if (!queuedGlyphs.isEmpty()) displayList.invalid = true;
+		}
+
+		GL.glTranslatef(-x, -y, 0);
+
+		if (displayList == null) displayList = new DisplayList();
+		displayList.width = (short)maxWidth;
+		displayList.height = (short)(lines * getLineHeight() + totalHeight);
+		return displayList;
+	}
+
+	public void drawString (float x, float y, String text, Color color, int startIndex, int endIndex) {
+		drawDisplayList(x, y, text, color, startIndex, endIndex);
+	}
+
+	public void drawString (float x, float y, String text) {
+		drawString(x, y, text, Color.white);
+	}
+
+	public void drawString (float x, float y, String text, Color col) {
+		drawString(x, y, text, col, 0, text.length());
+	}
+
+	/**
+	 * Returns the glyph for the specified codePoint. If the glyph does not exist yet, 
+	 * it is created and queued to be loaded.
+	 * 
+	 * @param glyphCode The code of the glyph to locate
+	 * @param codePoint The code point associated with the glyph
+	 * @param bounds The bounds of the glyph on the page
+	 * @param vector The vector the glyph is part of  
+	 * @param index The index of the glyph within the vector
+	 * @return The glyph requested
+	 */
+	private Glyph getGlyph (int glyphCode, int codePoint, Rectangle bounds, GlyphVector vector, int index) {
+		if (glyphCode < 0 || glyphCode >= MAX_GLYPH_CODE) {
+			// GlyphVector#getGlyphCode sometimes returns negative numbers on OS X.
+			return new Glyph(codePoint, bounds, vector, index, this) {
+				public boolean isMissing () {
+					return true;
+				}
+			};
+		}
+		int pageIndex = glyphCode / PAGE_SIZE;
+		int glyphIndex = glyphCode & (PAGE_SIZE - 1);
+		Glyph glyph = null;
+		Glyph[] page = glyphs[pageIndex];
+		if (page != null) {
+			glyph = page[glyphIndex];
+			if (glyph != null) return glyph;
+		} else
+			page = glyphs[pageIndex] = new Glyph[PAGE_SIZE];
+		// Add glyph so size information is available and queue it so its image can be loaded later.
+		glyph = page[glyphIndex] = new Glyph(codePoint, bounds, vector, index, this);
+		queuedGlyphs.add(glyph);
+		return glyph;
+	}
+
+	/**
+	 * Returns the bounds of the specified glyph.\
+	 * 
+	 * @param vector The vector the glyph is part of
+	 * @param index The index of the glyph within the vector
+	 * @param codePoint The code point associated with the glyph
+	 */
+	private Rectangle getGlyphBounds (GlyphVector vector, int index, int codePoint) {
+		Rectangle bounds = vector.getGlyphPixelBounds(index, GlyphPage.renderContext, 0, 0);
+		if (codePoint == ' ') bounds.width = spaceWidth;
+		return bounds;
+	}
+
+	/**
+	 * Returns the width of the space character.
+	 */
+	public int getSpaceWidth () {
+		return spaceWidth;
+	}
+
+	/**
+	 * @see org.newdawn.slick.Font#getWidth(java.lang.String)
+	 */
+	public int getWidth (String text) {
+		if (text == null) throw new IllegalArgumentException("text cannot be null.");
+		if (text.length() == 0) return 0;
+
+		if (displayListCaching) {
+			DisplayList displayList = (DisplayList)displayLists.get(text);
+			if (displayList != null) return displayList.width;
+		}
+
+		char[] chars = text.toCharArray();
+		GlyphVector vector = font.layoutGlyphVector(GlyphPage.renderContext, chars, 0, chars.length, Font.LAYOUT_LEFT_TO_RIGHT);
+
+		int width = 0;
+		int extraX = 0;
+		boolean startNewLine = false;
+		for (int glyphIndex = 0, n = vector.getNumGlyphs(); glyphIndex < n; glyphIndex++) {
+			int charIndex = vector.getGlyphCharIndex(glyphIndex);
+			int codePoint = text.codePointAt(charIndex);
+			Rectangle bounds = getGlyphBounds(vector, glyphIndex, codePoint);
+
+			if (startNewLine && codePoint != '\n') extraX = -bounds.x;
+
+			if (glyphIndex > 0) extraX += paddingLeft + paddingRight + paddingAdvanceX;
+			width = Math.max(width, bounds.x + extraX + bounds.width);
+
+			if (codePoint == '\n') startNewLine = true;
+		}
+
+		return width;
+	}
+
+	/**
+	 * @see org.newdawn.slick.Font#getHeight(java.lang.String)
+	 */
+	public int getHeight (String text) {
+		if (text == null) throw new IllegalArgumentException("text cannot be null.");
+		if (text.length() == 0) return 0;
+
+		if (displayListCaching) {
+			DisplayList displayList = (DisplayList)displayLists.get(text);
+			if (displayList != null) return displayList.height;
+		}
+
+		char[] chars = text.toCharArray();
+		GlyphVector vector = font.layoutGlyphVector(GlyphPage.renderContext, chars, 0, chars.length, Font.LAYOUT_LEFT_TO_RIGHT);
+
+		int lines = 0, height = 0;
+		for (int i = 0, n = vector.getNumGlyphs(); i < n; i++) {
+			int charIndex = vector.getGlyphCharIndex(i);
+			int codePoint = text.codePointAt(charIndex);
+			if (codePoint == ' ') continue;
+			Rectangle bounds = getGlyphBounds(vector, i, codePoint);
+
+			height = Math.max(height, ascent + bounds.y + bounds.height);
+
+			if (codePoint == '\n') {
+				lines++;
+				height = 0;
+			}
+		}
+		return lines * getLineHeight() + height;
+	}
+
+	/**
+	 * Returns the distance from the y drawing location to the top most pixel of the 
+	 * specified text.
+	 * 
+	 * @param text The text to analyse 
+	 * @return The distance fro the y drawing location ot the top most pixel of the specified text
+	 */
+	public int getYOffset (String text) {
+		if (text == null) throw new IllegalArgumentException("text cannot be null.");
+
+		DisplayList displayList = null;
+		if (displayListCaching) {
+			displayList = (DisplayList)displayLists.get(text);
+			if (displayList != null && displayList.yOffset != null) return displayList.yOffset.intValue();
+		}
+
+		int index = text.indexOf('\n');
+		if (index != -1) text = text.substring(0, index);
+		char[] chars = text.toCharArray();
+		GlyphVector vector = font.layoutGlyphVector(GlyphPage.renderContext, chars, 0, chars.length, Font.LAYOUT_LEFT_TO_RIGHT);
+		int yOffset = ascent + vector.getPixelBounds(null, 0, 0).y;
+
+		if (displayList != null) displayList.yOffset = new Short((short)yOffset);
+
+		return yOffset;
+	}
+
+	/**
+	 * Returns the TrueTypeFont for this UnicodeFont.
+	 * 
+	 * @return The AWT Font being rendered 
+	 */
+	public Font getFont() {
+		return font;
+	}
+
+	/**
+	 * Returns the padding above a glyph on the GlyphPage to allow for effects to be drawn.
+	 * 
+	 * @return The padding at the top of the glyphs when drawn
+	 */
+	public int getPaddingTop() {
+		return paddingTop;
+	}
+
+	/**
+	 * Sets the padding above a glyph on the GlyphPage to allow for effects to be drawn.
+	 * 
+	 * @param paddingTop The padding at the top of the glyphs when drawn
+	 */
+	public void setPaddingTop(int paddingTop) {
+		this.paddingTop = paddingTop;
+	}
+
+	/**
+	 * Returns the padding to the left of a glyph on the GlyphPage to allow for effects to be drawn.
+	 * 
+	 * @return The padding at the left of the glyphs when drawn
+	 */
+	public int getPaddingLeft() {
+		return paddingLeft;
+	}
+
+	/**
+	 * Sets the padding to the left of a glyph on the GlyphPage to allow for effects to be drawn.
+	 * 
+	 * @param paddingLeft The padding at the left of the glyphs when drawn
+	 */
+	public void setPaddingLeft(int paddingLeft) {
+		this.paddingLeft = paddingLeft;
+	}
+
+	/**
+	 * Returns the padding below a glyph on the GlyphPage to allow for effects to be drawn.
+	 * 
+	 * @return The padding at the bottom of the glyphs when drawn
+	 */
+	public int getPaddingBottom() {
+		return paddingBottom;
+	}
+
+	/**
+	 * Sets the padding below a glyph on the GlyphPage to allow for effects to be drawn.
+	 * 
+	 * @param paddingBottom The padding at the bottom of the glyphs when drawn
+	 */
+	public void setPaddingBottom(int paddingBottom) {
+		this.paddingBottom = paddingBottom;
+	}
+
+	/**
+	 * Returns the padding to the right of a glyph on the GlyphPage to allow for effects to be drawn.
+	 * 
+	 * @return The padding at the right of the glyphs when drawn
+	 */
+	public int getPaddingRight () {
+		return paddingRight;
+	}
+
+	/**
+	 * Sets the padding to the right of a glyph on the GlyphPage to allow for effects to be drawn.
+	 * 
+	 * @param paddingRight The padding at the right of the glyphs when drawn
+	 */
+	public void setPaddingRight (int paddingRight) {
+		this.paddingRight = paddingRight;
+	}
+
+	/**
+	 * Gets the additional amount to offset glyphs on the x axis.
+	 * 
+	 * @return The padding applied for each horizontal advance (i.e. when a glyph is rendered)
+	 */
+	public int getPaddingAdvanceX() {
+		return paddingAdvanceX;
+	}
+
+	/**
+	 * Sets the additional amount to offset glyphs on the x axis. This is typically set to a negative number when left or right
+	 * padding is used so that glyphs are not spaced too far apart.
+	 * 
+	 * @param paddingAdvanceX The padding applied for each horizontal advance (i.e. when a glyph is rendered)
+	 */
+	public void setPaddingAdvanceX (int paddingAdvanceX) {
+		this.paddingAdvanceX = paddingAdvanceX;
+	}
+
+	/**
+	 * Gets the additional amount to offset a line of text on the y axis.
+	 * 
+	 * @return The padding applied for each vertical advance (i.e. when a glyph is rendered)
+	 */
+	public int getPaddingAdvanceY () {
+		return paddingAdvanceY;
+	}
+
+	/**
+	 * Sets the additional amount to offset a line of text on the y axis. This is typically set to a negative number when top or
+	 * bottom padding is used so that lines of text are not spaced too far apart.
+	 * 
+	 * @param paddingAdvanceY The padding applied for each vertical advance (i.e. when a glyph is rendered)
+	 */
+	public void setPaddingAdvanceY (int paddingAdvanceY) {
+		this.paddingAdvanceY = paddingAdvanceY;
+	}
+
+	/**
+	 * Returns the distance from one line of text to the next. This is the sum of the descent, ascent, leading, padding top,
+	 * padding bottom, and padding advance y. To change the line height, use {@link #setPaddingAdvanceY(int)}.
+	 */
+	public int getLineHeight() {
+		return descent + ascent + leading + paddingTop + paddingBottom + paddingAdvanceY;
+	}
+
+	/**
+	 * Gets the distance from the baseline to the y drawing location.
+	 * 
+	 * @return The ascent of this font
+	 */
+	public int getAscent() {
+		return ascent;
+	}
+
+	/**
+	 * Gets the distance from the baseline to the bottom of most alphanumeric characters 
+	 * with descenders.
+	 * 
+	 * @return The distance from the baseline to the bottom of the font
+	 */
+	public int getDescent () {
+		return descent;
+	}
+
+	/**
+	 * Gets the extra distance between the descent of one line of text to the ascent of the next.
+	 * 
+	 * @return The leading edge of the font
+	 */
+	public int getLeading () {
+		return leading;
+	}
+
+	/**
+	 * Returns the width of the backing textures.
+	 * 
+	 * @return The width of the glyph pages in this font
+	 */
+	public int getGlyphPageWidth () {
+		return glyphPageWidth;
+	}
+
+	/**
+	 * Sets the width of the backing textures. Default is 512.
+	 * 
+	 * @param glyphPageWidth The width of the glyph pages in this font
+	 */
+	public void setGlyphPageWidth(int glyphPageWidth) {
+		this.glyphPageWidth = glyphPageWidth;
+	}
+
+	/**
+	 * Returns the height of the backing textures.
+	 * 
+	 * @return The height of the glyph pages in this font
+	 */
+	public int getGlyphPageHeight() {
+		return glyphPageHeight;
+	}
+
+	/**
+	 * Sets the height of the backing textures. Default is 512.
+	 * 
+	 * @param glyphPageHeight The width of the glyph pages in this font
+	 */
+	public void setGlyphPageHeight(int glyphPageHeight) {
+		this.glyphPageHeight = glyphPageHeight;
+	}
+
+	/**
+	 * Returns the GlyphPages for this UnicodeFont.
+	 * 
+	 * @return The glyph pages that have been loaded into this font
+	 */
+	public List getGlyphPages () {
+		return glyphPages;
+	}
+
+	/**
+	 * Returns a list of {@link org.newdawn.slick.font.effects.Effect}s that will be applied 
+	 * to the glyphs.
+	 * 
+	 * @return The list of effects to be applied to the font
+	 */
+	public List getEffects () {
+		return effects;
+	}
+
+	/**
+	 * Returns true if this UnicodeFont caches the glyph drawing instructions to 
+	 * improve performance.
+	 * 
+	 * @return True if caching is turned on
+	 */
+	public boolean isCaching () {
+		return displayListCaching;
+	}
+
+	/**
+	 * Sets if this UnicodeFont caches the glyph drawing instructions to improve performance. 
+	 * Default is true. Text rendering is very slow without display list caching.
+	 * 
+	 * @param displayListCaching True if caching should be turned on
+	 */
+	public void setDisplayListCaching (boolean displayListCaching) {
+		this.displayListCaching = displayListCaching;
+	}
+
+	/**
+	 * Returns the path to the TTF file for this UnicodeFont, or null. If this UnicodeFont was created without specifying the TTF
+	 * file, it will try to determine the path using Sun classes. If this fails, null is returned.
+	 * 
+	 * @return The reference to the font file that the kerning was loaded from
+	 */
+	public String getFontFile () {
+		if (ttfFileRef == null) {
+			// Worst case if this UnicodeFont was loaded without a ttfFileRef, try to get the font file from Sun's classes.
+			try {
+				Object font2D = Class.forName("sun.font.FontManager").getDeclaredMethod("getFont2D", new Class[] {Font.class})
+					.invoke(null, new Object[] {font});
+				Field platNameField = Class.forName("sun.font.PhysicalFont").getDeclaredField("platName");
+				platNameField.setAccessible(true);
+				ttfFileRef = (String)platNameField.get(font2D);
+			} catch (Throwable ignored) {
+			}
+			if (ttfFileRef == null) ttfFileRef = "";
+		}
+		if (ttfFileRef.length() == 0) return null;
+		return ttfFileRef;
+	}
+
+	/**
+	 * A simple descriptor for display lists cached within this font
+	 */
+	public static class DisplayList {
+		/** True if this display list has been invalidated */
+		boolean invalid;
+		/** The ID of the display list this descriptor represents */
+		int id;
+		/** The vertical offset to the top of this display list */
+		Short yOffset;
+
+		/** The width of rendered text in the list */
+		public short width;
+		/** The height of the rendered text in the list */
+		public short height;
+		/** Application data stored in the list */
+		public Object userData;
+
+		DisplayList () {
+		}
+	}
+}
diff --git a/ext/slick/src/org/newdawn/slick/XMLPackedSheet.java b/ext/slick/src/org/newdawn/slick/XMLPackedSheet.java
new file mode 100644
index 0000000..7212675
--- /dev/null
+++ b/ext/slick/src/org/newdawn/slick/XMLPackedSheet.java
@@ -0,0 +1,65 @@
+package org.newdawn.slick;
+
+import java.util.HashMap;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.newdawn.slick.util.ResourceLoader;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+/**
+ * A sprite sheet based on an XML descriptor generated from the simple slick tool
+ * 
+ * @author kevin
+ */
+public class XMLPackedSheet {
+	/** The full sheet image */
+	private Image image;
+	/** The sprites stored on the image */
+	private HashMap sprites = new HashMap();
+	
+	/**
+	 * Create a new XML packed sheet from the XML output by the slick tool
+	 * 
+	 * @param imageRef The reference to the image
+	 * @param xmlRef The reference to the XML
+	 * @throws SlickException Indicates a failure to parse the XML or read the image
+	 */
+	public XMLPackedSheet(String imageRef, String xmlRef) throws SlickException
+	{
+		image = new Image(imageRef, false, Image.FILTER_NEAREST);
+	
+		try {
+			DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+			Document doc = builder.parse(ResourceLoader.getResourceAsStream(xmlRef));
+			
+			NodeList list = doc.getElementsByTagName("sprite");
+			for (int i=0;iBasicCommand
+Provides abstract input by mapping physical device inputs (mouse, keyboard and controllers) to abstract
+commands that are relevant to a particular game.
+
\ No newline at end of file
diff --git a/ext/slick/src/org/newdawn/slick/data/defaultfont.fnt b/ext/slick/src/org/newdawn/slick/data/defaultfont.fnt
new file mode 100644
index 0000000..9e1e964
--- /dev/null
+++ b/ext/slick/src/org/newdawn/slick/data/defaultfont.fnt
@@ -0,0 +1,195 @@
+info face="Courier New Bold" size=16 bold=1 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1
+common lineHeight=19 base=26 scaleW=256 scaleH=256 pages=1 packed=0
+page id=0 file="defaultfont.png"
+chars count=189
+char id=32   x=0     y=0     width=0     height=0     xoffset=0     yoffset=14    xadvance=9     page=0  chnl=0 
+char id=253   x=0     y=0     width=12     height=16     xoffset=0     yoffset=2    xadvance=9     page=0  chnl=0 
+char id=254   x=12     y=0     width=11     height=15     xoffset=0     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=221   x=23     y=0     width=12     height=15     xoffset=0     yoffset=0    xadvance=9     page=0  chnl=0 
+char id=218   x=35     y=0     width=11     height=15     xoffset=0     yoffset=0    xadvance=9     page=0  chnl=0 
+char id=217   x=46     y=0     width=11     height=15     xoffset=0     yoffset=0    xadvance=9     page=0  chnl=0 
+char id=211   x=57     y=0     width=11     height=15     xoffset=0     yoffset=0    xadvance=9     page=0  chnl=0 
+char id=210   x=68     y=0     width=11     height=15     xoffset=0     yoffset=0    xadvance=9     page=0  chnl=0 
+char id=205   x=79     y=0     width=10     height=15     xoffset=1     yoffset=0    xadvance=9     page=0  chnl=0 
+char id=204   x=89     y=0     width=10     height=15     xoffset=1     yoffset=0    xadvance=9     page=0  chnl=0 
+char id=201   x=99     y=0     width=10     height=15     xoffset=0     yoffset=0    xadvance=9     page=0  chnl=0 
+char id=200   x=109     y=0     width=10     height=15     xoffset=0     yoffset=0    xadvance=9     page=0  chnl=0 
+char id=199   x=119     y=0     width=11     height=15     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=197   x=130     y=0     width=12     height=15     xoffset=0     yoffset=0    xadvance=9     page=0  chnl=0 
+char id=193   x=142     y=0     width=12     height=15     xoffset=0     yoffset=0    xadvance=9     page=0  chnl=0 
+char id=192   x=154     y=0     width=12     height=15     xoffset=0     yoffset=0    xadvance=9     page=0  chnl=0 
+char id=36   x=166     y=0     width=9     height=15     xoffset=1     yoffset=2    xadvance=9     page=0  chnl=0 
+char id=106   x=175     y=0     width=8     height=15     xoffset=1     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=255   x=183     y=0     width=12     height=14     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=219   x=195     y=0     width=11     height=14     xoffset=0     yoffset=1    xadvance=9     page=0  chnl=0 
+char id=213   x=206     y=0     width=11     height=14     xoffset=0     yoffset=1    xadvance=9     page=0  chnl=0 
+char id=212   x=217     y=0     width=11     height=14     xoffset=0     yoffset=1    xadvance=9     page=0  chnl=0 
+char id=209   x=228     y=0     width=11     height=14     xoffset=0     yoffset=1    xadvance=9     page=0  chnl=0 
+char id=206   x=239     y=0     width=10     height=14     xoffset=1     yoffset=1    xadvance=9     page=0  chnl=0 
+char id=202   x=0     y=16     width=10     height=14     xoffset=0     yoffset=1    xadvance=9     page=0  chnl=0 
+char id=195   x=10     y=16     width=12     height=14     xoffset=0     yoffset=1    xadvance=9     page=0  chnl=0 
+char id=194   x=22     y=16     width=12     height=14     xoffset=0     yoffset=1    xadvance=9     page=0  chnl=0 
+char id=162   x=34     y=16     width=10     height=14     xoffset=1     yoffset=2    xadvance=9     page=0  chnl=0 
+char id=92   x=44     y=16     width=9     height=14     xoffset=1     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=47   x=53     y=16     width=9     height=14     xoffset=1     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=93   x=62     y=16     width=6     height=14     xoffset=2     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=91   x=68     y=16     width=6     height=14     xoffset=4     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=41   x=74     y=16     width=5     height=14     xoffset=2     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=40   x=79     y=16     width=5     height=14     xoffset=3     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=250   x=84     y=16     width=11     height=13     xoffset=0     yoffset=2    xadvance=9     page=0  chnl=0 
+char id=249   x=95     y=16     width=11     height=13     xoffset=0     yoffset=2    xadvance=9     page=0  chnl=0 
+char id=243   x=106     y=16     width=10     height=13     xoffset=0     yoffset=2    xadvance=9     page=0  chnl=0 
+char id=242   x=116     y=16     width=10     height=13     xoffset=0     yoffset=2    xadvance=9     page=0  chnl=0 
+char id=237   x=126     y=16     width=10     height=13     xoffset=1     yoffset=2    xadvance=9     page=0  chnl=0 
+char id=236   x=136     y=16     width=10     height=13     xoffset=1     yoffset=2    xadvance=9     page=0  chnl=0 
+char id=233   x=146     y=16     width=11     height=13     xoffset=0     yoffset=2    xadvance=9     page=0  chnl=0 
+char id=232   x=157     y=16     width=11     height=13     xoffset=0     yoffset=2    xadvance=9     page=0  chnl=0 
+char id=229   x=168     y=16     width=10     height=13     xoffset=1     yoffset=2    xadvance=9     page=0  chnl=0 
+char id=225   x=178     y=16     width=10     height=13     xoffset=1     yoffset=2    xadvance=9     page=0  chnl=0 
+char id=224   x=188     y=16     width=10     height=13     xoffset=1     yoffset=2    xadvance=9     page=0  chnl=0 
+char id=220   x=198     y=16     width=11     height=13     xoffset=0     yoffset=2    xadvance=9     page=0  chnl=0 
+char id=216   x=209     y=16     width=11     height=13     xoffset=0     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=214   x=220     y=16     width=11     height=13     xoffset=0     yoffset=2    xadvance=9     page=0  chnl=0 
+char id=207   x=231     y=16     width=10     height=13     xoffset=1     yoffset=2    xadvance=9     page=0  chnl=0 
+char id=203   x=241     y=16     width=10     height=13     xoffset=0     yoffset=2    xadvance=9     page=0  chnl=0 
+char id=196   x=0     y=30     width=12     height=13     xoffset=0     yoffset=2    xadvance=9     page=0  chnl=0 
+char id=182   x=12     y=30     width=10     height=13     xoffset=1     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=167   x=22     y=30     width=10     height=13     xoffset=0     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=166   x=32     y=30     width=4     height=13     xoffset=4     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=124   x=36     y=30     width=4     height=13     xoffset=4     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=125   x=40     y=30     width=6     height=13     xoffset=3     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=123   x=46     y=30     width=6     height=13     xoffset=2     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=81   x=52     y=30     width=11     height=13     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=251   x=63     y=30     width=11     height=12     xoffset=0     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=245   x=74     y=30     width=10     height=12     xoffset=0     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=244   x=84     y=30     width=10     height=12     xoffset=0     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=241   x=94     y=30     width=11     height=12     xoffset=0     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=240   x=105     y=30     width=10     height=12     xoffset=0     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=238   x=115     y=30     width=10     height=12     xoffset=1     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=234   x=125     y=30     width=11     height=12     xoffset=0     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=231   x=136     y=30     width=10     height=12     xoffset=0     yoffset=7    xadvance=9     page=0  chnl=0 
+char id=227   x=146     y=30     width=10     height=12     xoffset=1     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=226   x=156     y=30     width=10     height=12     xoffset=1     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=223   x=166     y=30     width=9     height=12     xoffset=0     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=181   x=175     y=30     width=11     height=12     xoffset=0     yoffset=6    xadvance=9     page=0  chnl=0 
+char id=127   x=186     y=30     width=11     height=12     xoffset=0     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=35   x=197     y=30     width=10     height=12     xoffset=1     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=64   x=207     y=30     width=8     height=12     xoffset=1     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=48   x=215     y=30     width=9     height=12     xoffset=1     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=57   x=224     y=30     width=9     height=12     xoffset=1     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=56   x=233     y=30     width=9     height=12     xoffset=1     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=55   x=242     y=30     width=9     height=12     xoffset=0     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=54   x=0     y=43     width=9     height=12     xoffset=1     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=53   x=9     y=43     width=9     height=12     xoffset=1     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=52   x=18     y=43     width=9     height=12     xoffset=1     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=51   x=27     y=43     width=9     height=12     xoffset=0     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=50   x=36     y=43     width=8     height=12     xoffset=1     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=121   x=44     y=43     width=12     height=12     xoffset=0     yoffset=6    xadvance=9     page=0  chnl=0 
+char id=113   x=56     y=43     width=11     height=12     xoffset=0     yoffset=6    xadvance=9     page=0  chnl=0 
+char id=112   x=67     y=43     width=11     height=12     xoffset=0     yoffset=6    xadvance=9     page=0  chnl=0 
+char id=108   x=78     y=43     width=10     height=12     xoffset=1     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=107   x=88     y=43     width=11     height=12     xoffset=0     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=105   x=99     y=43     width=10     height=12     xoffset=1     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=104   x=109     y=43     width=11     height=12     xoffset=0     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=103   x=120     y=43     width=11     height=12     xoffset=0     yoffset=6    xadvance=9     page=0  chnl=0 
+char id=102   x=131     y=43     width=10     height=12     xoffset=1     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=100   x=141     y=43     width=11     height=12     xoffset=0     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=98   x=152     y=43     width=11     height=12     xoffset=0     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=252   x=163     y=43     width=11     height=11     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=248   x=174     y=43     width=10     height=11     xoffset=0     yoffset=5    xadvance=9     page=0  chnl=0 
+char id=246   x=184     y=43     width=10     height=11     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=239   x=194     y=43     width=10     height=11     xoffset=1     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=235   x=204     y=43     width=11     height=11     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=228   x=215     y=43     width=10     height=11     xoffset=1     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=222   x=225     y=43     width=10     height=11     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=208   x=235     y=43     width=11     height=11     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=198   x=0     y=55     width=12     height=11     xoffset=-1     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=191   x=12     y=55     width=9     height=11     xoffset=1     yoffset=7    xadvance=9     page=0  chnl=0 
+char id=190   x=21     y=55     width=12     height=11     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=189   x=33     y=55     width=12     height=11     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=188   x=45     y=55     width=12     height=11     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=177   x=57     y=55     width=9     height=11     xoffset=1     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=174   x=66     y=55     width=11     height=11     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=169   x=77     y=55     width=11     height=11     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=165   x=88     y=55     width=12     height=11     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=163   x=100     y=55     width=10     height=11     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=161   x=110     y=55     width=4     height=11     xoffset=3     yoffset=7    xadvance=9     page=0  chnl=0 
+char id=38   x=114     y=55     width=9     height=11     xoffset=1     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=37   x=123     y=55     width=9     height=11     xoffset=1     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=63   x=132     y=55     width=8     height=11     xoffset=1     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=33   x=140     y=55     width=4     height=11     xoffset=3     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=49   x=144     y=55     width=10     height=11     xoffset=1     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=116   x=154     y=55     width=9     height=11     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=90   x=163     y=55     width=9     height=11     xoffset=1     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=89   x=172     y=55     width=12     height=11     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=88   x=184     y=55     width=11     height=11     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=87   x=195     y=55     width=13     height=11     xoffset=-1     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=86   x=208     y=55     width=11     height=11     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=85   x=219     y=55     width=11     height=11     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=84   x=230     y=55     width=10     height=11     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=83   x=240     y=55     width=9     height=11     xoffset=1     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=82   x=0     y=66     width=12     height=11     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=80   x=12     y=66     width=10     height=11     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=79   x=22     y=66     width=11     height=11     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=78   x=33     y=66     width=11     height=11     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=77   x=44     y=66     width=13     height=11     xoffset=-1     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=76   x=57     y=66     width=11     height=11     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=75   x=68     y=66     width=11     height=11     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=74   x=79     y=66     width=11     height=11     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=73   x=90     y=66     width=10     height=11     xoffset=1     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=72   x=100     y=66     width=11     height=11     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=71   x=111     y=66     width=11     height=11     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=70   x=122     y=66     width=11     height=11     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=69   x=133     y=66     width=10     height=11     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=68   x=143     y=66     width=11     height=11     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=67   x=154     y=66     width=11     height=11     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=66   x=165     y=66     width=10     height=11     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=65   x=175     y=66     width=12     height=11     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=247   x=187     y=66     width=10     height=10     xoffset=0     yoffset=5    xadvance=9     page=0  chnl=0 
+char id=62   x=197     y=66     width=10     height=10     xoffset=0     yoffset=5    xadvance=9     page=0  chnl=0 
+char id=60   x=207     y=66     width=11     height=10     xoffset=0     yoffset=5    xadvance=9     page=0  chnl=0 
+char id=59   x=218     y=66     width=6     height=10     xoffset=2     yoffset=7    xadvance=9     page=0  chnl=0 
+char id=230   x=224     y=66     width=12     height=9     xoffset=0     yoffset=6    xadvance=9     page=0  chnl=0 
+char id=164   x=236     y=66     width=11     height=9     xoffset=0     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=43   x=0     y=77     width=9     height=9     xoffset=1     yoffset=5    xadvance=9     page=0  chnl=0 
+char id=122   x=9     y=77     width=9     height=9     xoffset=1     yoffset=6    xadvance=9     page=0  chnl=0 
+char id=120   x=18     y=77     width=11     height=9     xoffset=0     yoffset=6    xadvance=9     page=0  chnl=0 
+char id=119   x=29     y=77     width=13     height=9     xoffset=-1     yoffset=6    xadvance=9     page=0  chnl=0 
+char id=118   x=42     y=77     width=11     height=9     xoffset=0     yoffset=6    xadvance=9     page=0  chnl=0 
+char id=117   x=53     y=77     width=11     height=9     xoffset=0     yoffset=6    xadvance=9     page=0  chnl=0 
+char id=115   x=64     y=77     width=9     height=9     xoffset=1     yoffset=6    xadvance=9     page=0  chnl=0 
+char id=114   x=73     y=77     width=10     height=9     xoffset=0     yoffset=6    xadvance=9     page=0  chnl=0 
+char id=111   x=83     y=77     width=10     height=9     xoffset=0     yoffset=6    xadvance=9     page=0  chnl=0 
+char id=110   x=93     y=77     width=11     height=9     xoffset=0     yoffset=6    xadvance=9     page=0  chnl=0 
+char id=109   x=104     y=77     width=12     height=9     xoffset=0     yoffset=6    xadvance=9     page=0  chnl=0 
+char id=101   x=116     y=77     width=11     height=9     xoffset=0     yoffset=6    xadvance=9     page=0  chnl=0 
+char id=97   x=127     y=77     width=10     height=9     xoffset=1     yoffset=6    xadvance=9     page=0  chnl=0 
+char id=42   x=137     y=77     width=10     height=8     xoffset=1     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=58   x=147     y=77     width=3     height=8     xoffset=3     yoffset=7    xadvance=9     page=0  chnl=0 
+char id=99   x=150     y=77     width=10     height=8     xoffset=0     yoffset=7    xadvance=9     page=0  chnl=0 
+char id=215   x=160     y=77     width=7     height=7     xoffset=1     yoffset=7    xadvance=9     page=0  chnl=0 
+char id=187   x=167     y=77     width=10     height=7     xoffset=0     yoffset=8    xadvance=9     page=0  chnl=0 
+char id=186   x=177     y=77     width=8     height=7     xoffset=2     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=184   x=185     y=77     width=5     height=7     xoffset=3     yoffset=12    xadvance=9     page=0  chnl=0 
+char id=178   x=190     y=77     width=7     height=7     xoffset=2     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=172   x=197     y=77     width=11     height=7     xoffset=0     yoffset=8    xadvance=9     page=0  chnl=0 
+char id=171   x=208     y=77     width=11     height=7     xoffset=0     yoffset=8    xadvance=9     page=0  chnl=0 
+char id=94   x=219     y=77     width=8     height=7     xoffset=1     yoffset=3    xadvance=9     page=0  chnl=0 
+char id=44   x=227     y=77     width=4     height=7     xoffset=3     yoffset=11    xadvance=9     page=0  chnl=0 
+char id=39   x=231     y=77     width=4     height=7     xoffset=4     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=34   x=235     y=77     width=8     height=7     xoffset=2     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=185   x=243     y=77     width=5     height=6     xoffset=3     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=179   x=248     y=77     width=7     height=6     xoffset=3     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=170   x=0     y=86     width=7     height=6     xoffset=2     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=180   x=7     y=86     width=4     height=5     xoffset=3     yoffset=2    xadvance=9     page=0  chnl=0 
+char id=176   x=11     y=86     width=5     height=5     xoffset=2     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=126   x=16     y=86     width=9     height=5     xoffset=1     yoffset=7    xadvance=9     page=0  chnl=0 
+char id=61   x=25     y=86     width=11     height=5     xoffset=0     yoffset=7    xadvance=9     page=0  chnl=0 
+char id=96   x=36     y=86     width=4     height=5     xoffset=3     yoffset=2    xadvance=9     page=0  chnl=0 
+char id=183   x=40     y=86     width=3     height=3     xoffset=3     yoffset=8    xadvance=9     page=0  chnl=0 
+char id=175   x=43     y=86     width=13     height=3     xoffset=-1     yoffset=1    xadvance=9     page=0  chnl=0 
+char id=168   x=56     y=86     width=6     height=3     xoffset=2     yoffset=4    xadvance=9     page=0  chnl=0 
+char id=95   x=62     y=86     width=13     height=3     xoffset=-1     yoffset=17    xadvance=9     page=0  chnl=0 
+char id=45   x=75     y=86     width=9     height=3     xoffset=1     yoffset=8    xadvance=9     page=0  chnl=0 
+char id=46   x=84     y=86     width=3     height=3     xoffset=3     yoffset=12    xadvance=9     page=0  chnl=0 
+kernings count=-1
diff --git a/ext/slick/src/org/newdawn/slick/data/defaultfont.png b/ext/slick/src/org/newdawn/slick/data/defaultfont.png
new file mode 100644
index 0000000..0e56abd
Binary files /dev/null and b/ext/slick/src/org/newdawn/slick/data/defaultfont.png differ
diff --git a/ext/slick/src/org/newdawn/slick/data/helvetica_svg b/ext/slick/src/org/newdawn/slick/data/helvetica_svg
new file mode 100644
index 0000000..c52659b
--- /dev/null
+++ b/ext/slick/src/org/newdawn/slick/data/helvetica_svg
@@ -0,0 +1,131 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ !"#$%&'()*+,-./0123456789:;<>?
+@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
+`abcdefghijklmnopqrstuvwxyz{|}~
+
+
diff --git a/ext/slick/src/org/newdawn/slick/data/package.html b/ext/slick/src/org/newdawn/slick/data/package.html
new file mode 100644
index 0000000..9f42bf3
--- /dev/null
+++ b/ext/slick/src/org/newdawn/slick/data/package.html
@@ -0,0 +1,4 @@
+
+This package contains the default data required for the basic functions of YASL. Currently this includes a default
+font to ensure text can always be displayed.
+
\ No newline at end of file
diff --git a/ext/slick/src/org/newdawn/slick/data/particle.tga b/ext/slick/src/org/newdawn/slick/data/particle.tga
new file mode 100644
index 0000000..7d8ee51
Binary files /dev/null and b/ext/slick/src/org/newdawn/slick/data/particle.tga differ
diff --git a/ext/slick/src/org/newdawn/slick/fills/GradientFill.java b/ext/slick/src/org/newdawn/slick/fills/GradientFill.java
new file mode 100644
index 0000000..10b85b8
--- /dev/null
+++ b/ext/slick/src/org/newdawn/slick/fills/GradientFill.java
@@ -0,0 +1,253 @@
+package org.newdawn.slick.fills;
+
+import org.newdawn.slick.Color;
+import org.newdawn.slick.ShapeFill;
+import org.newdawn.slick.geom.Shape;
+import org.newdawn.slick.geom.Vector2f;
+
+/**
+ * A fill effect used to define gradients when filling and drawing shapes. A gradient is defined
+ * by two control points. Each point that is rendered is coloured based on it's proximity to the 
+ * points. Note that the points are defined relative to the center of the shape being drawn. This 
+ * is with the intention that the gradient fills can be used and do not need to be updated when
+ * the geometry is moved
+ *
+ * @author kevin
+ */
+public class GradientFill implements ShapeFill {
+	/** The contant offset */
+	private Vector2f none = new Vector2f(0,0);
+	/** The start position of the gradient */
+	private Vector2f start;
+	/** The end poisition of the gradient */
+	private Vector2f end;
+	/** The starting colour of the gradient */
+	private Color startCol;
+	/** The ending colour of the gradient */
+	private Color endCol;
+	/** True if the graident is defined in shape coordinates */
+	private boolean local = false;
+	
+	/**
+	 * Create a gradient fill
+	 * 
+	 * @param sx The x coordinate of the starting control point
+	 * @param sy The y coordinate of the starting control point
+	 * @param startCol The colour to apply at the starting control point
+	 * @param ex The x coordinate of the ending control point
+	 * @param ey The y coordinate of the ending control point
+	 * @param endCol The colour to apply at the ending control point
+	 */
+	public GradientFill(float sx, float sy, Color startCol, float ex, float ey, Color endCol) 
+	{
+		this(sx,sy,startCol,ex,ey,endCol,false);
+	}
+
+	/**
+	 * Create a gradient fill
+	 * 
+	 * @param sx The x coordinate of the starting control point
+	 * @param sy The y coordinate of the starting control point
+	 * @param startCol The colour to apply at the starting control point
+	 * @param ex The x coordinate of the ending control point
+	 * @param ey The y coordinate of the ending control point
+	 * @param endCol The colour to apply at the ending control point
+	 * @param local True if the gradient is defined in local shape coordinates
+	 */
+	public GradientFill(float sx, float sy, Color startCol, float ex, float ey, Color endCol, boolean local) 
+	{
+		this(new Vector2f(sx,sy), startCol, new Vector2f(ex,ey), endCol, local);
+	}
+	
+	/**
+	 * Create a gradient fill
+	 * 
+	 * @param start The position of the starting control point
+	 * @param startCol The colour to apply at the starting control point
+	 * @param end The position of the ending control point
+	 * @param endCol The colour to apply at the ending control point
+	 * @param local True if the gradient is defined in local shape coordinates
+	 */
+	public GradientFill(Vector2f start, Color startCol, Vector2f end, Color endCol, boolean local) {
+		this.start = new Vector2f(start);
+		this.end = new Vector2f(end);
+		this.startCol = new Color(startCol);
+		this.endCol = new Color(endCol);
+		this.local = local;
+	}
+	
+	/**
+	 * Get an inverted copy of the gradient
+	 * 
+	 * @return The copy with the colours inverted
+	 */
+	public GradientFill getInvertedCopy() {
+		return new GradientFill(start, endCol, end, startCol, local);
+	}
+	
+	/**
+	 * Indicate if the gradient is defined in shape local coordinates
+	 * 
+	 * @param local True if the gradient is defined in shape local coordinates
+ 	 */
+	public void setLocal(boolean local) {
+		this.local = local;
+	}
+	
+	/**
+	 * Get the position of the start control point
+	 * 
+	 * @return The position of the start control point
+	 */
+	public Vector2f getStart() {
+		return start;
+	}
+
+	/**
+	 * Get the position of the end control point
+	 * 
+	 * @return The position of the end control point
+	 */
+	public Vector2f getEnd() {
+		return end;
+	}
+	
+	/**
+	 * Get the colour at the start control point
+	 * 
+	 * @return The color at the start control point
+	 */
+	public Color getStartColor() {
+		return startCol;
+	}
+
+	/**
+	 * Get the colour at the end control point
+	 * 
+	 * @return The color at the end control point
+	 */
+	public Color getEndColor() {
+		return endCol;
+	}
+	
+	/**
+	 * Set the start point's position
+	 * 
+	 * @param x The x coordinate of the start control point
+	 * @param y The y coordinate of the start control point
+	 */
+	public void setStart(float x, float y) {
+		setStart(new Vector2f(x,y));
+	}
+	
+	/**
+	 * Set the start control point's position
+	 * 
+	 * @param start The new poisition for the start point
+	 */
+	public void setStart(Vector2f start) {
+		this.start = new Vector2f(start);
+	}
+	
+	/**
+	 * Set the end control point's position
+	 * 
+	 * @param x The x coordinate of the end control point
+	 * @param y The y coordinate of the end control point
+	 */
+	public void setEnd(float x, float y) {
+		setEnd(new Vector2f(x,y));
+	}
+	
+	/**
+	 * Set the end control point's position
+	 * 
+	 * @param end The new position for the end point
+	 */
+	public void setEnd(Vector2f end) {
+		this.end = new Vector2f(end);
+	}
+	
+	/**
+	 * Set the colour to apply at the start control's position
+	 * 
+	 * @param color The colour to apply at the start control point
+	 */
+	public void setStartColor(Color color) {
+		this.startCol = new Color(color);
+	}
+
+	/**
+	 * Set the colour to apply at the end control's position
+	 * 
+	 * @param color The colour to apply at the end control point
+	 */
+	public void setEndColor(Color color) {
+		this.endCol = new Color(color);
+	}
+	
+	/**
+	 * Get the colour that should be applied at the specified location
+	 * 
+	 * @param shape The shape being filled
+	 * @param x The x coordinate of the point being coloured 
+	 * @param y The y coordinate of the point being coloured
+	 * @return The colour that should be applied based on the control points of this gradient
+	 */
+	public Color colorAt(Shape shape, float x, float y) {
+		if (local) {
+			return colorAt(x-shape.getCenterX(),y-shape.getCenterY());
+		} else {
+			return colorAt(x,y);
+		}
+	}
+
+	/**
+	 * Get the colour that should be applied at the specified location
+	 * 
+	 * @param x The x coordinate of the point being coloured 
+	 * @param y The y coordinate of the point being coloured
+	 * @return The colour that should be applied based on the control points of this gradient
+	 */
+	public Color colorAt(float x, float y) {
+		float dx1 = end.getX() - start.getX();
+		float dy1 = end.getY() - start.getY();
+		
+		float dx2 = -dy1;
+		float dy2 = dx1;
+		float denom = (dy2 * dx1) - (dx2 * dy1);
+		
+		if (denom == 0) {
+			return Color.black;
+		}
+		
+		float ua = (dx2 * (start.getY() - y)) - (dy2 * (start.getX() - x));
+		ua /= denom;
+		float ub = (dx1 * (start.getY() - y)) - (dy1 * (start.getX() - x));
+		ub /= denom;
+		float u = ua;
+		if (u < 0) {
+			u = 0;
+		} 
+		if (u > 1) {
+			u = 1;
+		}
+		float v = 1 - u;
+
+		// u is the proportion down the line we are
+		Color col = new Color(1,1,1,1);
+		col.r = (u * endCol.r) + (v * startCol.r);
+		col.b = (u * endCol.b) + (v * startCol.b);
+		col.g = (u * endCol.g) + (v * startCol.g);
+		col.a = (u * endCol.a) + (v * startCol.a);
+		
+		return col;
+	}
+
+	/**
+	 * @see org.newdawn.slick.ShapeFill#getOffsetAt(org.newdawn.slick.geom.Shape, float, float)
+	 */
+	public Vector2f getOffsetAt(Shape shape, float x, float y) {
+		return none;
+	}
+}
diff --git a/ext/slick/src/org/newdawn/slick/fills/package.html b/ext/slick/src/org/newdawn/slick/fills/package.html
new file mode 100644
index 0000000..a6cbd4e
--- /dev/null
+++ b/ext/slick/src/org/newdawn/slick/fills/package.html
@@ -0,0 +1,3 @@
+
+Fill effects used to colour and mogrify shapes during rendering
+
\ No newline at end of file
diff --git a/ext/slick/src/org/newdawn/slick/font/Glyph.java b/ext/slick/src/org/newdawn/slick/font/Glyph.java
new file mode 100644
index 0000000..af0197a
--- /dev/null
+++ b/ext/slick/src/org/newdawn/slick/font/Glyph.java
@@ -0,0 +1,152 @@
+
+package org.newdawn.slick.font;
+
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.font.GlyphMetrics;
+import java.awt.font.GlyphVector;
+
+import org.newdawn.slick.Image;
+import org.newdawn.slick.UnicodeFont;
+
+/**
+ * Represents the glyph in a font for a unicode codepoint.
+ * 
+ * @author Nathan Sweet 
+ */
+public class Glyph {
+	/** The code point in which this glyph is found */
+	private int codePoint;
+	/** The width of this glyph in pixels */
+	private short width;
+	/** The height of this glyph in pixels */
+	private short height;
+	/** The offset on the y axis to draw the glyph at */
+	private short yOffset;
+	/** True if the glyph isn't defined */
+	private boolean isMissing;
+	/** The shape drawn for this glyph */
+	private Shape shape;
+	/** The image generated for this glyph */
+	private Image image;
+
+	/**
+	 * Create a new glyph
+	 * 
+	 * @param codePoint The code point in which this glyph can be found
+	 * @param bounds The bounds that this glrph can fill
+	 * @param vector The vector this glyph is part of
+	 * @param index The index of this glyph within the vector
+	 * @param unicodeFont The font this glyph forms part of
+	 */
+	public Glyph(int codePoint, Rectangle bounds, GlyphVector vector, int index, UnicodeFont unicodeFont) {
+		this.codePoint = codePoint;
+
+		GlyphMetrics metrics = vector.getGlyphMetrics(index);
+		int lsb = (int)metrics.getLSB();
+		if (lsb > 0) lsb = 0;
+		int rsb = (int)metrics.getRSB();
+		if (rsb > 0) rsb = 0;
+
+		int glyphWidth = bounds.width - lsb - rsb;
+		int glyphHeight = bounds.height;
+		if (glyphWidth > 0 && glyphHeight > 0) {
+			int padTop = unicodeFont.getPaddingTop();
+			int padRight = unicodeFont.getPaddingRight();
+			int padBottom = unicodeFont.getPaddingBottom();
+			int padLeft = unicodeFont.getPaddingLeft();
+			int glyphSpacing = 1; // Needed to prevent filtering problems.
+			width = (short)(glyphWidth + padLeft + padRight + glyphSpacing);
+			height = (short)(glyphHeight + padTop + padBottom + glyphSpacing);
+			yOffset = (short)(unicodeFont.getAscent() + bounds.y - padTop);
+		}
+
+		shape = vector.getGlyphOutline(index, -bounds.x + unicodeFont.getPaddingLeft(), -bounds.y + unicodeFont.getPaddingTop());
+
+		isMissing = !unicodeFont.getFont().canDisplay((char)codePoint);
+	}
+
+	/**
+	 * The unicode codepoint the glyph represents.
+	 * 
+	 * @return The codepoint the glyph represents
+	 */
+	public int getCodePoint () {
+		return codePoint;
+	}
+
+	/**
+	 * Returns true if the font does not have a glyph for this codepoint.
+	 * 
+	 * @return True if this glyph is not defined in the given code point
+	 */
+	public boolean isMissing () {
+		return isMissing;
+	}
+
+	/**
+	 * The width of the glyph's image.
+	 * 
+	 * @return The width in pixels of the glyphs image
+	 */
+	public int getWidth () {
+		return width;
+	}
+
+	/**
+	 * The height of the glyph's image.
+	 * 
+	 * @return The height in pixels of the glyphs image
+	 */
+	public int getHeight () {
+		return height;
+	}
+
+	/**
+	 * The shape to use to draw this glyph. This is set to null after the glyph is stored 
+	 * in a GlyphPage.
+	 * 
+	 * @return The shape drawn for this glyph
+	 */
+	public Shape getShape () {
+		return shape;
+	}
+
+	/**
+	 * Set the shape that should be drawn for this glyph
+	 * 
+	 * @param shape The shape that should be drawn for this glyph
+	 */
+	public void setShape(Shape shape) {
+		this.shape = shape;
+	}
+
+	/**
+	 * The image to use for this glyph. This is null until after the glyph is stored in a 
+	 * GlyphPage.
+	 * 
+	 * @return The image that has been generated for this glyph
+	 */
+	public Image getImage () {
+		return image;
+	}
+
+	/**
+	 * Set the image that has been generated for this glyph
+	 * 
+	 * @param image The image that has been generated for this glyph
+	 */
+	public void setImage(Image image) {
+		this.image = image;
+	}
+
+	/**
+	 * The distance from drawing y location to top of this glyph, causing the glyph to sit 
+	 * on the baseline.
+	 * 
+	 * @return The offset on the y axis this glyph should be drawn at
+	 */
+	public int getYOffset() {
+		return yOffset;
+	}
+}
diff --git a/ext/slick/src/org/newdawn/slick/font/GlyphPage.java b/ext/slick/src/org/newdawn/slick/font/GlyphPage.java
new file mode 100644
index 0000000..eb3e009
--- /dev/null
+++ b/ext/slick/src/org/newdawn/slick/font/GlyphPage.java
@@ -0,0 +1,262 @@
+
+package org.newdawn.slick.font;
+
+import java.awt.AlphaComposite;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.font.FontRenderContext;
+import java.awt.image.BufferedImage;
+import java.awt.image.WritableRaster;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.IntBuffer;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.lwjgl.opengl.GL11;
+import org.lwjgl.opengl.GL12;
+import org.newdawn.slick.Color;
+import org.newdawn.slick.Image;
+import org.newdawn.slick.SlickException;
+import org.newdawn.slick.UnicodeFont;
+import org.newdawn.slick.font.effects.Effect;
+import org.newdawn.slick.opengl.TextureImpl;
+import org.newdawn.slick.opengl.renderer.Renderer;
+import org.newdawn.slick.opengl.renderer.SGL;
+
+/**
+ * Stores a number of glyphs on a single texture.
+ * 
+ * @author Nathan Sweet 
+ */
+public class GlyphPage {
+	/** The interface to OpenGL */
+	private static final SGL GL = Renderer.get();
+
+	/** The maxium size of an individual glyph */
+	public static final int MAX_GLYPH_SIZE = 256;
+
+	/** A temporary working buffer */
+    private static ByteBuffer scratchByteBuffer = ByteBuffer.allocateDirect(MAX_GLYPH_SIZE * MAX_GLYPH_SIZE * 4);
+
+    static {
+		scratchByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
+    }
+    
+    /** A temporary working buffer */
+    private static IntBuffer scratchIntBuffer = scratchByteBuffer.asIntBuffer();
+    
+    
+	/** A temporary image used to generate the glyph page */
+	private static BufferedImage scratchImage = new BufferedImage(MAX_GLYPH_SIZE, MAX_GLYPH_SIZE, BufferedImage.TYPE_INT_ARGB);
+	/** The graphics context form the temporary image */
+	private static Graphics2D scratchGraphics = (Graphics2D)scratchImage.getGraphics();
+	
+	static {
+		scratchGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+		scratchGraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+		scratchGraphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
+	}
+	
+	/** The render context in which the glyphs will be generated */
+    public static FontRenderContext renderContext = scratchGraphics.getFontRenderContext();
+	
+	/**
+	 * Get the scratch graphics used to generate the page of glyphs
+	 * 
+	 * @return The scratch graphics used to build the page
+	 */
+	public static Graphics2D getScratchGraphics() {
+		return scratchGraphics;
+	}
+	
+	/** The font this page is part of */
+	private final UnicodeFont unicodeFont;
+	/** The width of this page's image */
+	private final int pageWidth;
+	/** The height of this page's image */
+	private final int pageHeight;
+	/** The image containing the glyphs */
+	private final Image pageImage;
+	/** The x position of the page */
+	private int pageX;
+	/** The y position of the page */
+	private int pageY;
+	/** The height of the last row on the page */
+	private int rowHeight;
+	/** True if the glyphs are ordered */
+	private boolean orderAscending;
+	/** The list of glyphs on this page */
+	private final List pageGlyphs = new ArrayList(32);
+
+	/**
+	 * Create a new page of glyphs
+	 * 
+	 * @param unicodeFont The font this page forms part of
+	 * @param pageWidth The width of the backing texture.
+	 * @param pageHeight The height of the backing texture.
+	 * @throws SlickException if the backing texture could not be created.
+	 */
+	public GlyphPage(UnicodeFont unicodeFont, int pageWidth, int pageHeight) throws SlickException {
+		this.unicodeFont = unicodeFont;
+		this.pageWidth = pageWidth;
+		this.pageHeight = pageHeight;
+
+		pageImage = new Image(pageWidth, pageHeight);
+	}
+
+	/**
+	 * Loads glyphs to the backing texture and sets the image on each loaded glyph. Loaded glyphs are removed from the list.
+	 * 
+	 * If this page already has glyphs and maxGlyphsToLoad is -1, then this method will return 0 if all the new glyphs don't fit.
+	 * This reduces texture binds when drawing since glyphs loaded at once are typically displayed together.
+	 * @param glyphs The glyphs to load.
+	 * @param maxGlyphsToLoad This is the maximum number of glyphs to load from the list. Set to -1 to attempt to load all the
+	 *           glyphs.
+	 * @return The number of glyphs that were actually loaded.
+	 * @throws SlickException if the glyph could not be rendered.
+	 */
+	public int loadGlyphs (List glyphs, int maxGlyphsToLoad) throws SlickException {
+		if (rowHeight != 0 && maxGlyphsToLoad == -1) {
+			// If this page has glyphs and we are not loading incrementally, return zero if any of the glyphs don't fit.
+			int testX = pageX;
+			int testY = pageY;
+			int testRowHeight = rowHeight;
+			for (Iterator iter = getIterator(glyphs); iter.hasNext();) {
+				Glyph glyph = (Glyph)iter.next();
+				int width = glyph.getWidth();
+				int height = glyph.getHeight();
+				if (testX + width >= pageWidth) {
+					testX = 0;
+					testY += testRowHeight;
+					testRowHeight = height;
+				} else if (height > testRowHeight) {
+					testRowHeight = height;
+				}
+				if (testY + testRowHeight >= pageWidth) return 0;
+				testX += width;
+			}
+		}
+
+		Color.white.bind();
+		pageImage.bind();
+
+		int i = 0;
+		for (Iterator iter = getIterator(glyphs); iter.hasNext();) {
+			Glyph glyph = (Glyph)iter.next();
+			int width = Math.min(MAX_GLYPH_SIZE, glyph.getWidth());
+			int height = Math.min(MAX_GLYPH_SIZE, glyph.getHeight());
+
+			if (rowHeight == 0) {
+				// The first glyph always fits.
+				rowHeight = height;
+			} else {
+				// Wrap to the next line if needed, or break if no more fit.
+				if (pageX + width >= pageWidth) {
+					if (pageY + rowHeight + height >= pageHeight) break;
+					pageX = 0;
+					pageY += rowHeight;
+					rowHeight = height;
+				} else if (height > rowHeight) {
+					if (pageY + height >= pageHeight) break;
+					rowHeight = height;
+				}
+			}
+
+			renderGlyph(glyph, width, height);
+			pageGlyphs.add(glyph);
+
+			pageX += width;
+
+			iter.remove();
+			i++;
+			if (i == maxGlyphsToLoad) {
+				// If loading incrementally, flip orderAscending so it won't change, since we'll probably load the rest next time.
+				orderAscending = !orderAscending;
+				break;
+			}
+		}
+
+		TextureImpl.bindNone();
+
+		// Every other batch of glyphs added to a page are sorted the opposite way to attempt to keep same size glyps together.
+		orderAscending = !orderAscending;
+
+		return i;
+	}
+
+	/**
+	 * Loads a single glyph to the backing texture, if it fits.
+	 * 
+	 * @param glyph The glyph to be rendered
+	 * @param width The expected width of the glyph
+	 * @param height The expected height of the glyph
+	 * @throws SlickException if the glyph could not be rendered.
+	 */
+	private void renderGlyph(Glyph glyph, int width, int height) throws SlickException {
+		// Draw the glyph to the scratch image using Java2D.
+		scratchGraphics.setComposite(AlphaComposite.Clear);
+		scratchGraphics.fillRect(0, 0, MAX_GLYPH_SIZE, MAX_GLYPH_SIZE);
+		scratchGraphics.setComposite(AlphaComposite.SrcOver);
+		scratchGraphics.setColor(java.awt.Color.white);
+		for (Iterator iter = unicodeFont.getEffects().iterator(); iter.hasNext();)
+			((Effect)iter.next()).draw(scratchImage, scratchGraphics, unicodeFont, glyph);
+		glyph.setShape(null); // The shape will never be needed again.
+
+		WritableRaster raster = scratchImage.getRaster();
+		int[] row = new int[width];
+		for (int y = 0; y < height; y++) {
+			raster.getDataElements(0, y, width, 1, row);
+			scratchIntBuffer.put(row);
+		}
+		GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, pageX, pageY, width, height, GL12.GL_BGRA, GL11.GL_UNSIGNED_BYTE,
+			scratchByteBuffer);
+		scratchIntBuffer.clear();
+
+		glyph.setImage(pageImage.getSubImage(pageX, pageY, width, height));
+	}
+
+	/**
+	 * Returns an iterator for the specified glyphs, sorted either ascending or descending.
+	 * 
+	 * @param glyphs The glyphs to return if present
+	 * @return An iterator of the sorted list of glyphs
+	 */
+	private Iterator getIterator(List glyphs) {
+		if (orderAscending) return glyphs.iterator();
+		final ListIterator iter = glyphs.listIterator(glyphs.size());
+		return new Iterator() {
+			public boolean hasNext () {
+				return iter.hasPrevious();
+			}
+
+			public Object next () {
+				return iter.previous();
+			}
+
+			public void remove () {
+				iter.remove();
+			}
+		};
+	}
+
+	/**
+	 * Returns the glyphs stored on this page.
+	 * 
+	 * @return A list of {@link Glyph} elements on this page
+	 */
+	public List getGlyphs () {
+		return pageGlyphs;
+	}
+
+	/**
+	 * Returns the backing texture for this page.
+	 * 
+	 * @return The image of this page of glyphs
+	 */
+	public Image getImage () {
+		return pageImage;
+	}
+}
diff --git a/ext/slick/src/org/newdawn/slick/font/HieroSettings.java b/ext/slick/src/org/newdawn/slick/font/HieroSettings.java
new file mode 100644
index 0000000..b47fe6e
--- /dev/null
+++ b/ext/slick/src/org/newdawn/slick/font/HieroSettings.java
@@ -0,0 +1,369 @@
+
+package org.newdawn.slick.font;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.newdawn.slick.SlickException;
+import org.newdawn.slick.UnicodeFont;
+import org.newdawn.slick.font.effects.ConfigurableEffect;
+import org.newdawn.slick.font.effects.ConfigurableEffect.Value;
+import org.newdawn.slick.util.ResourceLoader;
+
+/**
+ * Holds the settings needed to configure a UnicodeFont.
+ * 
+ * @author Nathan Sweet 
+ */
+public class HieroSettings {
+	/** The size of the font to be generated */
+	private int fontSize = 12;
+	/** True if the font is rendered bold */
+	private boolean bold = false;
+	/** True fi the font if rendered italic */
+	private boolean italic = false;
+	/** The padding applied in pixels to the top of the glyph rendered area */
+	private int paddingTop;
+	/** The padding applied in pixels to the left of the glyph rendered area */
+	private int paddingLeft;
+	/** The padding applied in pixels to the bottom of the glyph rendered area */
+	private int paddingBottom;
+	/** The padding applied in pixels to the right of the glyph rendered area */
+	private int paddingRight;
+	/** The padding applied in pixels to horizontal advance for each glyph */
+	private int paddingAdvanceX;
+	/** The padding applied in pixels to vertical advance for each glyph */
+	private int paddingAdvanceY;
+	/** The width of the glyph page generated */
+	private int glyphPageWidth = 512;
+	/** The height of the glyph page generated */
+	private int glyphPageHeight = 512;
+	/** The list of effects applied */
+	private final List effects = new ArrayList();
+
+	/**
+	 * Default constructor for injection
+	 */
+	public HieroSettings() {
+	}
+
+	/**
+	 * Create a new set of configuration from a file
+	 * 
+	 * @param hieroFileRef The file system or classpath location of the Hiero settings file.
+	 * @throws SlickException if the file could not be read.
+	 */
+	public HieroSettings(String hieroFileRef) throws SlickException {
+		try {
+			BufferedReader reader = new BufferedReader(new InputStreamReader(ResourceLoader.getResourceAsStream(hieroFileRef)));
+			while (true) {
+				String line = reader.readLine();
+				if (line == null) break;
+				line = line.trim();
+				if (line.length() == 0) continue;
+				String[] pieces = line.split("=", 2);
+				String name = pieces[0].trim();
+				String value = pieces[1];
+				if (name.equals("font.size")) {
+					fontSize = Integer.parseInt(value);
+				} else if (name.equals("font.bold")) {
+					bold = Boolean.valueOf(value).booleanValue();
+				} else if (name.equals("font.italic")) {
+					italic = Boolean.valueOf(value).booleanValue();
+				} else if (name.equals("pad.top")) {
+					paddingTop = Integer.parseInt(value);
+				} else if (name.equals("pad.right")) {
+					paddingRight = Integer.parseInt(value);
+				} else if (name.equals("pad.bottom")) {
+					paddingBottom = Integer.parseInt(value);
+				} else if (name.equals("pad.left")) {
+					paddingLeft = Integer.parseInt(value);
+				} else if (name.equals("pad.advance.x")) {
+					paddingAdvanceX = Integer.parseInt(value);
+				} else if (name.equals("pad.advance.y")) {
+					paddingAdvanceY = Integer.parseInt(value);
+				} else if (name.equals("glyph.page.width")) {
+					glyphPageWidth = Integer.parseInt(value);
+				} else if (name.equals("glyph.page.height")) {
+					glyphPageHeight = Integer.parseInt(value);
+				} else if (name.equals("effect.class")) {
+					try {
+						effects.add(Class.forName(value).newInstance());
+					} catch (Exception ex) {
+						throw new SlickException("Unable to create effect instance: " + value, ex);
+					}
+				} else if (name.startsWith("effect.")) {
+					// Set an effect value on the last added effect.
+					name = name.substring(7);
+					ConfigurableEffect effect = (ConfigurableEffect)effects.get(effects.size() - 1);
+					List values = effect.getValues();
+					for (Iterator iter = values.iterator(); iter.hasNext();) {
+						Value effectValue = (Value)iter.next();
+						if (effectValue.getName().equals(name)) {
+							effectValue.setString(value);
+							break;
+						}
+					}
+					effect.setValues(values);
+				}
+			}
+			reader.close();
+		} catch (Exception ex) {
+			throw new SlickException("Unable to load Hiero font file: " + hieroFileRef, ex);
+		}
+	}
+
+	/**
+	 * @see UnicodeFont#getPaddingTop()
+	 * 
+	 * @return The padding for the top of the glyph area in pixels
+	 */
+	public int getPaddingTop () {
+		return paddingTop;
+	}
+
+	/**
+	 * @see UnicodeFont#setPaddingTop(int)
+	 * 
+	 * @param paddingTop The padding for the top of the glyph area in pixels
+	 */
+	public void setPaddingTop(int paddingTop) {
+		this.paddingTop = paddingTop;
+	}
+
+	/**
+	 * @see UnicodeFont#getPaddingLeft()
+	 * 
+	 * @return The padding for the left of the glyph area in pixels
+	 */
+	public int getPaddingLeft() {
+		return paddingLeft;
+	}
+
+	/**
+	 * @see UnicodeFont#setPaddingLeft(int)
+	 * 
+	 * @param paddingLeft The padding for the left of the glyph area in pixels
+	 */
+	public void setPaddingLeft(int paddingLeft) {
+		this.paddingLeft = paddingLeft;
+	}
+
+	/**
+	 * @see UnicodeFont#getPaddingBottom()
+	 * 
+	 * @return The padding for the bottom of the glyph area in pixels
+	 */
+	public int getPaddingBottom() {
+		return paddingBottom;
+	}
+
+	/**
+	 * @see UnicodeFont#setPaddingBottom(int)
+	 * 
+	 * @param paddingBottom The padding for the bottom of the glyph area in pixels
+	 */
+	public void setPaddingBottom(int paddingBottom) {
+		this.paddingBottom = paddingBottom;
+	}
+
+	/**
+	 * @see UnicodeFont#getPaddingRight()
+	 * 
+	 * @return The padding for the right of the glyph area in pixels
+	 */
+	public int getPaddingRight() {
+		return paddingRight;
+	}
+
+	/**
+	 * @see UnicodeFont#setPaddingRight(int)
+	 * 
+	 * @param paddingRight The padding for the right of the glyph area in pixels
+	 */
+	public void setPaddingRight(int paddingRight) {
+		this.paddingRight = paddingRight;
+	}
+
+	/**
+	 * @see UnicodeFont#getPaddingAdvanceX()
+	 * 
+	 * @return The padding for the horizontal advance of each glyph
+	 */
+	public int getPaddingAdvanceX() {
+		return paddingAdvanceX;
+	}
+
+	/**
+	 * @see UnicodeFont#setPaddingAdvanceX(int)
+	 * 
+	 * @param paddingAdvanceX The padding for the horizontal advance of each glyph
+	 */
+	public void setPaddingAdvanceX(int paddingAdvanceX) {
+		this.paddingAdvanceX = paddingAdvanceX;
+	}
+
+	/**
+	 * @see UnicodeFont#getPaddingAdvanceY()
+	 * 
+	 * @return The padding for the vertical advance of each glyph
+	 */
+	public int getPaddingAdvanceY() {
+		return paddingAdvanceY;
+	}
+
+	/**
+	 * @see UnicodeFont#setPaddingAdvanceY(int)
+	 * 
+	 * @param paddingAdvanceY The padding for the vertical advance of each glyph
+	 */
+	public void setPaddingAdvanceY(int paddingAdvanceY) {
+		this.paddingAdvanceY = paddingAdvanceY;
+	}
+
+	/**
+	 * @see UnicodeFont#getGlyphPageWidth()
+	 * 
+	 * @return The width of the generate glyph pages
+	 */
+	public int getGlyphPageWidth() {
+		return glyphPageWidth;
+	}
+
+	/**
+	 * @see UnicodeFont#setGlyphPageWidth(int)
+	 * 
+	 * @param glyphPageWidth The width of the generate glyph pages
+	 */
+	public void setGlyphPageWidth(int glyphPageWidth) {
+		this.glyphPageWidth = glyphPageWidth;
+	}
+
+	/**
+	 * @see UnicodeFont#getGlyphPageHeight()
+	 * 
+	 * @return The height of the generate glyph pages
+	 */
+	public int getGlyphPageHeight() {
+		return glyphPageHeight;
+	}
+
+	/**
+	 * @see UnicodeFont#setGlyphPageHeight(int)
+	 * 
+	 * @param glyphPageHeight The height of the generate glyph pages
+	 */
+	public void setGlyphPageHeight(int glyphPageHeight) {
+		this.glyphPageHeight = glyphPageHeight;
+	}
+
+	/**
+	 * @see UnicodeFont#UnicodeFont(String, int, boolean, boolean)
+	 * @see UnicodeFont#UnicodeFont(java.awt.Font, int, boolean, boolean)
+	 * 
+	 * @return The point size of the font generated
+	 */
+	public int getFontSize() {
+		return fontSize;
+	}
+
+	/**
+	 * @see UnicodeFont#UnicodeFont(String, int, boolean, boolean)
+	 * @see UnicodeFont#UnicodeFont(java.awt.Font, int, boolean, boolean)
+	 * 
+	 * @param fontSize The point size of the font generated
+	 */
+	public void setFontSize (int fontSize) {
+		this.fontSize = fontSize;
+	}
+
+	/**
+	 * @see UnicodeFont#UnicodeFont(String, int, boolean, boolean)
+	 * @see UnicodeFont#UnicodeFont(java.awt.Font, int, boolean, boolean)
+	 * 
+	 * @return True if the font was generated in bold typeface
+	 */
+	public boolean isBold () {
+		return bold;
+	}
+
+	/**
+	 * @see UnicodeFont#UnicodeFont(String, int, boolean, boolean)
+	 * @see UnicodeFont#UnicodeFont(java.awt.Font, int, boolean, boolean)
+	 * 
+	 * @param bold True if the font was generated in bold typeface
+	 */
+	public void setBold (boolean bold) {
+		this.bold = bold;
+	}
+
+	/**
+	 * @see UnicodeFont#UnicodeFont(String, int, boolean, boolean)
+	 * @see UnicodeFont#UnicodeFont(java.awt.Font, int, boolean, boolean)
+	 * 
+	 * @return True if the font was generated in italic typeface
+	 */
+	public boolean isItalic () {
+		return italic;
+	}
+
+	/**
+	 * @see UnicodeFont#UnicodeFont(String, int, boolean, boolean)
+	 * @see UnicodeFont#UnicodeFont(java.awt.Font, int, boolean, boolean)
+	 * 
+	 * @param italic True if the font was generated in italic typeface
+	 */
+	public void setItalic (boolean italic) {
+		this.italic = italic;
+	}
+
+	/**
+	 * @see UnicodeFont#getEffects()
+	 * 
+	 * @return The list of effects applied to the text
+	 */
+	public List getEffects() {
+		return effects;
+	}
+
+	/**
+	 * Saves the settings to a file.
+	 * 
+	 * @param file The file we're saving to
+	 * @throws IOException if the file could not be saved.
+	 */
+	public void save(File file) throws IOException {
+		PrintStream out = new PrintStream(new FileOutputStream(file));
+		out.println("font.size=" + fontSize);
+		out.println("font.bold=" + bold);
+		out.println("font.italic=" + italic);
+		out.println();
+		out.println("pad.top=" + paddingTop);
+		out.println("pad.right=" + paddingRight);
+		out.println("pad.bottom=" + paddingBottom);
+		out.println("pad.left=" + paddingLeft);
+		out.println("pad.advance.x=" + paddingAdvanceX);
+		out.println("pad.advance.y=" + paddingAdvanceY);
+		out.println();
+		out.println("glyph.page.width=" + glyphPageWidth);
+		out.println("glyph.page.height=" + glyphPageHeight);
+		out.println();
+		for (Iterator iter = effects.iterator(); iter.hasNext();) {
+			ConfigurableEffect effect = (ConfigurableEffect)iter.next();
+			out.println("effect.class=" + effect.getClass().getName());
+			for (Iterator iter2 = effect.getValues().iterator(); iter2.hasNext();) {
+				Value value = (Value)iter2.next();
+				out.println("effect." + value.getName() + "=" + value.getString());
+			}
+			out.println();
+		}
+		out.close();
+	}
+}
diff --git a/ext/slick/src/org/newdawn/slick/font/effects/ColorEffect.java b/ext/slick/src/org/newdawn/slick/font/effects/ColorEffect.java
new file mode 100644
index 0000000..722bdc5
--- /dev/null
+++ b/ext/slick/src/org/newdawn/slick/font/effects/ColorEffect.java
@@ -0,0 +1,92 @@
+
+package org.newdawn.slick.font.effects;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.newdawn.slick.UnicodeFont;
+import org.newdawn.slick.font.Glyph;
+
+/**
+ * Makes glyphs a solid color.
+ * 
+ * @author Nathan Sweet 
+ */
+public class ColorEffect implements ConfigurableEffect {
+	/** The colour that will be applied across the text */
+	private Color color = Color.white;
+
+	/**
+	 * Default constructor for injection
+	 */
+	public ColorEffect() {
+	}
+
+	/**
+	 * Create a new effect to colour the text
+	 * 
+	 * @param color The colour to apply across the text
+	 */
+	public ColorEffect(Color color) {
+		this.color = color;
+	}
+
+	/**
+	 * @see org.newdawn.slick.font.effects.Effect#draw(java.awt.image.BufferedImage, java.awt.Graphics2D, org.newdawn.slick.UnicodeFont, org.newdawn.slick.font.Glyph)
+	 */
+	public void draw(BufferedImage image, Graphics2D g, UnicodeFont unicodeFont, Glyph glyph) {
+		g.setColor(color);
+		g.fill(glyph.getShape());
+	}
+
+	/**
+	 * Get the colour being applied by this effect
+	 * 
+	 * @return The colour being applied by this effect
+	 */
+	public Color getColor() {
+		return color;
+	}
+
+	/**
+	 * Set the colour being applied by this effect
+	 * 
+	 * @param color The colour being applied by this effect
+	 */
+	public void setColor(Color color) {
+		if (color == null) throw new IllegalArgumentException("color cannot be null.");
+		this.color = color;
+	}
+
+	/**
+	 * @see java.lang.Object#toString()
+	 */
+	public String toString () {
+		return "Color";
+	}
+
+	/**
+	 * @see org.newdawn.slick.font.effects.ConfigurableEffect#getValues()
+	 */
+	public List getValues() {
+		List values = new ArrayList();
+		values.add(EffectUtil.colorValue("Color", color));
+		return values;
+	}
+
+	/**
+	 * @see org.newdawn.slick.font.effects.ConfigurableEffect#setValues(java.util.List)
+	 */
+	public void setValues(List values) {
+		for (Iterator iter = values.iterator(); iter.hasNext();) {
+			Value value = (Value)iter.next();
+			if (value.getName().equals("Color")) {
+				setColor((Color)value.getObject());
+			}
+		}
+	}
+}
diff --git a/ext/slick/src/org/newdawn/slick/font/effects/ConfigurableEffect.java b/ext/slick/src/org/newdawn/slick/font/effects/ConfigurableEffect.java
new file mode 100644
index 0000000..a610a3e
--- /dev/null
+++ b/ext/slick/src/org/newdawn/slick/font/effects/ConfigurableEffect.java
@@ -0,0 +1,53 @@
+
+package org.newdawn.slick.font.effects;
+
+import java.util.List;
+
+/**
+ * An effect that has a number of configuration values. This allows the effect to be configured in the Hiero GUI and to be saved
+ * and loaded to and from a file.
+ * 
+ * @author Nathan Sweet 
+ */
+public interface ConfigurableEffect extends Effect {
+	/**
+	 * Returns the list of {@link Value}s for this effect. This list is not typically backed by the effect, so changes to the
+	 * values will not take affect until {@link #setValues(List)} is called.
+	 */
+	public List getValues();
+
+	/**
+	 * Sets the list of {@link Value}s for this effect.
+	 */
+	public void setValues(List values);
+
+	/**
+	 * Represents a configurable value for an effect.
+	 */
+	static public interface Value {
+		/**
+		 * Returns the name of the value.
+		 */
+		public String getName ();
+
+		/**
+		 * Sets the string representation of the value.
+		 */
+		public void setString (String value);
+
+		/**
+		 * Gets the string representation of the value.
+		 */
+		public String getString ();
+
+		/**
+		 * Gets the object representation of the value.
+		 */
+		public Object getObject ();
+
+		/**
+		 * Shows a dialog allowing a user to configure this value.
+		 */
+		public void showDialog ();
+	}
+}
diff --git a/ext/slick/src/org/newdawn/slick/font/effects/Effect.java b/ext/slick/src/org/newdawn/slick/font/effects/Effect.java
new file mode 100644
index 0000000..4fc141e
--- /dev/null
+++ b/ext/slick/src/org/newdawn/slick/font/effects/Effect.java
@@ -0,0 +1,25 @@
+
+package org.newdawn.slick.font.effects;
+
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+
+import org.newdawn.slick.UnicodeFont;
+import org.newdawn.slick.font.Glyph;
+
+/**
+ * A graphical effect that is applied to glyphs in a {@link UnicodeFont}.
+ * 
+ * @author Nathan Sweet 
+ */
+public interface Effect {
+	/**
+	 * Called to draw the effect.
+	 * 
+	 * @param image The image to draw into
+	 * @param g The graphics context to use for applying the effect
+	 * @param unicodeFont The font being rendered
+	 * @param glyph The particular glyph being rendered
+	 */
+	public void draw (BufferedImage image, Graphics2D g, UnicodeFont unicodeFont, Glyph glyph);
+}
diff --git a/ext/slick/src/org/newdawn/slick/font/effects/EffectUtil.java b/ext/slick/src/org/newdawn/slick/font/effects/EffectUtil.java
new file mode 100644
index 0000000..79e64d7
--- /dev/null
+++ b/ext/slick/src/org/newdawn/slick/font/effects/EffectUtil.java
@@ -0,0 +1,368 @@
+
+package org.newdawn.slick.font.effects;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.EventQueue;
+import java.awt.Graphics2D;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.image.BufferedImage;
+
+import javax.swing.BorderFactory;
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JColorChooser;
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JSpinner;
+import javax.swing.JTextArea;
+import javax.swing.SpinnerNumberModel;
+
+import org.newdawn.slick.font.GlyphPage;
+import org.newdawn.slick.font.effects.ConfigurableEffect.Value;
+
+/**
+ * Provides utility methods for effects.
+ * 
+ * @author Nathan Sweet 
+ */
+public class EffectUtil {
+	/** A graphics 2D temporary surface to be used when generating effects */
+	static private BufferedImage scratchImage = new BufferedImage(GlyphPage.MAX_GLYPH_SIZE, GlyphPage.MAX_GLYPH_SIZE,
+		BufferedImage.TYPE_INT_ARGB);
+
+	/**
+	 * Returns an image that can be used by effects as a temp image.
+	 * 
+	 * @return The scratch image used for temporary operations
+	 */
+	static public BufferedImage getScratchImage() {
+		Graphics2D g = (Graphics2D)scratchImage.getGraphics();
+		g.setComposite(AlphaComposite.Clear);
+		g.fillRect(0, 0, GlyphPage.MAX_GLYPH_SIZE, GlyphPage.MAX_GLYPH_SIZE);
+		g.setComposite(AlphaComposite.SrcOver);
+		g.setColor(java.awt.Color.white);
+		return scratchImage;
+	}
+
+	/**
+	 * Prompts the user for a colour value
+	 * 
+	 * @param name Thename of the value being configured
+	 * @param currentValue The default value that should be selected
+	 * @return The value selected
+	 */
+	static public Value colorValue(String name, Color currentValue) {
+		return new DefaultValue(name, EffectUtil.toString(currentValue)) {
+			public void showDialog () {
+				Color newColor = JColorChooser.showDialog(null, "Choose a color", EffectUtil.fromString(value));
+				if (newColor != null) value = EffectUtil.toString(newColor);
+			}
+
+			public Object getObject () {
+				return EffectUtil.fromString(value);
+			}
+		};
+	}
+
+	/**
+	 * Prompts the user for int value
+	 * 
+	 * @param name The name of the dialog to show
+	 * @param currentValue The current value to be displayed
+	 * @param description The help text to provide
+	 * @return The value selected by the user
+	 */
+	static public Value intValue (String name, final int currentValue, final String description) {
+		return new DefaultValue(name, String.valueOf(currentValue)) {
+			public void showDialog () {
+				JSpinner spinner = new JSpinner(new SpinnerNumberModel(currentValue, Short.MIN_VALUE, Short.MAX_VALUE, 1));
+				if (showValueDialog(spinner, description)) value = String.valueOf(spinner.getValue());
+			}
+
+			public Object getObject () {
+				return Integer.valueOf(value);
+			}
+		};
+	}
+
+	/**
+	 * Prompts the user for float value
+	 * 
+	 * @param name The name of the dialog to show
+	 * @param currentValue The current value to be displayed
+	 * @param description The help text to provide
+	 * @param min The minimum value to allow
+	 * @param max The maximum value to allow
+	 * @return The value selected by the user
+	 */
+	static public Value floatValue (String name, final float currentValue, final float min, final float max,
+		final String description) {
+		return new DefaultValue(name, String.valueOf(currentValue)) {
+			public void showDialog () {
+				JSpinner spinner = new JSpinner(new SpinnerNumberModel(currentValue, min, max, 0.1f));
+				if (showValueDialog(spinner, description)) value = String.valueOf(((Double)spinner.getValue()).floatValue());
+			}
+
+			public Object getObject () {
+				return Float.valueOf(value);
+			}
+		};
+	}
+
+	/**
+	 * Prompts the user for boolean value
+	 * 
+	 * @param name The name of the dialog to show
+	 * @param currentValue The current value to be displayed
+	 * @param description The help text to provide
+	 * @return The value selected by the user
+	 */
+	static public Value booleanValue (String name, final boolean currentValue, final String description) {
+		return new DefaultValue(name, String.valueOf(currentValue)) {
+			public void showDialog () {
+				JCheckBox checkBox = new JCheckBox();
+				checkBox.setSelected(currentValue);
+				if (showValueDialog(checkBox, description)) value = String.valueOf(checkBox.isSelected());
+			}
+
+			public Object getObject () {
+				return Boolean.valueOf(value);
+			}
+		};
+	}
+
+	
+	/**
+	 * Prompts the user for a value that represents a fixed number of options. 
+	 * All options are strings.
+	 * 
+	 * @param options The first array has an entry for each option. Each entry is either a String[1] that is both the display value
+	 *           and actual value, or a String[2] whose first element is the display value and second element is the actual value.
+	 *
+	 * @param name The name of the value being prompted for
+	 * @param currentValue The current value to show as default
+	 * @param description The description of the value
+	 * @return The value selected by the user
+	 */
+	static public Value optionValue (String name, final String currentValue, final String[][] options, final String description) {
+		return new DefaultValue(name, currentValue.toString()) {
+			public void showDialog () {
+				int selectedIndex = -1;
+				DefaultComboBoxModel model = new DefaultComboBoxModel();
+				for (int i = 0; i < options.length; i++) {
+					model.addElement(options[i][0]);
+					if (getValue(i).equals(currentValue)) selectedIndex = i;
+				}
+				JComboBox comboBox = new JComboBox(model);
+				comboBox.setSelectedIndex(selectedIndex);
+				if (showValueDialog(comboBox, description)) value = getValue(comboBox.getSelectedIndex());
+			}
+
+			private String getValue (int i) {
+				if (options[i].length == 1) return options[i][0];
+				return options[i][1];
+			}
+
+			public String toString () {
+				for (int i = 0; i < options.length; i++)
+					if (getValue(i).equals(value)) return options[i][0].toString();
+				return "";
+			}
+
+			public Object getObject () {
+				return value;
+			}
+		};
+	}
+
+	/**
+	 * Convers a color to a string.
+	 * 
+	 * @param color The color to encode to a string
+	 * @return The colour as a string
+	 */
+	static public String toString (Color color) {
+		if (color == null) throw new IllegalArgumentException("color cannot be null.");
+		String r = Integer.toHexString(color.getRed());
+		if (r.length() == 1) r = "0" + r;
+		String g = Integer.toHexString(color.getGreen());
+		if (g.length() == 1) g = "0" + g;
+		String b = Integer.toHexString(color.getBlue());
+		if (b.length() == 1) b = "0" + b;
+		return r + g + b;
+	}
+
+	/**
+	 * Converts a string to a color.
+	 * 
+	 * @param rgb The string encoding the colour
+	 * @return The colour represented by the given encoded string
+	 */
+	static public Color fromString (String rgb) {
+		if (rgb == null || rgb.length() != 6) return Color.white;
+		return new Color(Integer.parseInt(rgb.substring(0, 2), 16), Integer.parseInt(rgb.substring(2, 4), 16), Integer.parseInt(rgb
+			.substring(4, 6), 16));
+	}
+
+	/**
+	 * Provides generic functionality for an effect's configurable value.
+	 */
+	static private abstract class DefaultValue implements Value {
+		/** The value being held */
+		String value;
+		/** The key/name of the value */
+		String name;
+
+		/**
+		 * Create a default value
+		 * 
+		 * @param name The name of the value being configured  
+		 * @param value The value to use for the default
+		 */
+		public DefaultValue(String name, String value) {
+			this.value = value;
+			this.name = name;
+		}
+
+		/**
+		 * @see org.newdawn.slick.font.effects.ConfigurableEffect.Value#setString(java.lang.String)
+		 */
+		public void setString(String value) {
+			this.value = value;
+		}
+
+		/**
+		 * @see org.newdawn.slick.font.effects.ConfigurableEffect.Value#getString()
+		 */
+		public String getString() {
+			return value;
+		}
+
+		/**
+		 * @see org.newdawn.slick.font.effects.ConfigurableEffect.Value#getName()
+		 */
+		public String getName() {
+			return name;
+		}
+
+		/**
+		 * @see java.lang.Object#toString()
+		 */
+		public String toString() {
+			if (value == null) {
+				return "";
+			}
+			return value.toString();
+		}
+
+		/**
+		 * Prompt the user for a value
+		 * 
+		 * @param component The component to use as parent for the prompting dialog
+		 * @param description The description of the value being prompted for
+		 * @return True if the value was configured
+		 */
+		public boolean showValueDialog(final JComponent component, String description) {
+			ValueDialog dialog = new ValueDialog(component, name, description);
+			dialog.setTitle(name);
+			dialog.setLocationRelativeTo(null);
+			EventQueue.invokeLater(new Runnable() {
+				public void run () {
+					JComponent focusComponent = component;
+					if (focusComponent instanceof JSpinner)
+						focusComponent = ((JSpinner.DefaultEditor)((JSpinner)component).getEditor()).getTextField();
+					focusComponent.requestFocusInWindow();
+				}
+			});
+			dialog.setVisible(true);
+			return dialog.okPressed;
+		}
+	};
+
+	/**
+	 * Provides generic functionality for a dialog to configure a value.
+	 */
+	static private class ValueDialog extends JDialog {
+		/** True if OK was pressed */
+		public boolean okPressed = false;
+
+		/**
+		 * Create a new dialog to configure a specific value
+		 * 
+		 * @param component The component to use as the parent of the dialog prompting the user
+		 * @param name The name of the value being configured
+		 * @param description The description of the value being configured
+		 */
+		public ValueDialog(JComponent component, String name, String description) {
+			setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+			setLayout(new GridBagLayout());
+			setModal(true);
+
+			if (component instanceof JSpinner)
+				((JSpinner.DefaultEditor)((JSpinner)component).getEditor()).getTextField().setColumns(4);
+
+			JPanel descriptionPanel = new JPanel();
+			descriptionPanel.setLayout(new GridBagLayout());
+			getContentPane().add(
+				descriptionPanel,
+				new GridBagConstraints(0, 0, 2, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0,
+					0), 0, 0));
+			descriptionPanel.setBackground(Color.white);
+			descriptionPanel.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.black));
+			{
+				JTextArea descriptionText = new JTextArea(description);
+				descriptionPanel.add(descriptionText, new GridBagConstraints(0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER,
+					GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 0, 0));
+				descriptionText.setWrapStyleWord(true);
+				descriptionText.setLineWrap(true);
+				descriptionText.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
+				descriptionText.setEditable(false);
+			}
+
+			JPanel panel = new JPanel();
+			getContentPane().add(
+				panel,
+				new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(5, 5, 0,
+					5), 0, 0));
+			panel.add(new JLabel(name + ":"));
+			panel.add(component);
+
+			JPanel buttonPanel = new JPanel();
+			getContentPane().add(
+				buttonPanel,
+				new GridBagConstraints(0, 2, 2, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE,
+					new Insets(0, 0, 0, 0), 0, 0));
+			{
+				JButton okButton = new JButton("OK");
+				buttonPanel.add(okButton);
+				okButton.addActionListener(new ActionListener() {
+					public void actionPerformed (ActionEvent evt) {
+						okPressed = true;
+						setVisible(false);
+					}
+				});
+			}
+			{
+				JButton cancelButton = new JButton("Cancel");
+				buttonPanel.add(cancelButton);
+				cancelButton.addActionListener(new ActionListener() {
+					public void actionPerformed (ActionEvent evt) {
+						setVisible(false);
+					}
+				});
+			}
+
+			setSize(new Dimension(320, 175));
+		}
+	}
+}
diff --git a/ext/slick/src/org/newdawn/slick/font/effects/FilterEffect.java b/ext/slick/src/org/newdawn/slick/font/effects/FilterEffect.java
new file mode 100644
index 0000000..a7f1512
--- /dev/null
+++ b/ext/slick/src/org/newdawn/slick/font/effects/FilterEffect.java
@@ -0,0 +1,62 @@
+
+package org.newdawn.slick.font.effects;
+
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.BufferedImageOp;
+
+import org.newdawn.slick.UnicodeFont;
+import org.newdawn.slick.font.Glyph;
+
+/**
+ * Applys a {@link BufferedImageOp} filter to glyphs. Many filters can be found 
+ * here: http://www.jhlabs.com/ip/filters/index.html
+ * 
+ * @author Nathan Sweet 
+ */
+public class FilterEffect implements Effect {
+	/** The filter to be applied */
+	private BufferedImageOp filter;
+
+	/**
+	 * Default constructor for injection
+	 */
+	public FilterEffect () {
+	}
+
+	/**
+	 * Create a new filtering effect based on a convolution operation
+	 * 
+	 * @param filter The filter to apply
+	 */
+	public FilterEffect (BufferedImageOp filter) {
+		this.filter = filter;
+	}
+
+	/**
+	 * @see org.newdawn.slick.font.effects.Effect#draw(java.awt.image.BufferedImage, java.awt.Graphics2D, org.newdawn.slick.UnicodeFont, org.newdawn.slick.font.Glyph)
+	 */
+	public void draw(BufferedImage image, Graphics2D g, UnicodeFont unicodeFont, Glyph glyph) {
+		BufferedImage scratchImage = EffectUtil.getScratchImage();
+		filter.filter(image, scratchImage);
+		image.getGraphics().drawImage(scratchImage, 0, 0, null);
+	}
+
+	/**
+	 * Get the filter being applied by this effect
+	 * 
+	 * @return The filter being applied by this effect
+	 */
+	public BufferedImageOp getFilter() {
+		return filter;
+	}
+
+	/**
+	 * Set the filter being applied by this effect
+	 * 
+	 * @param filter The filter being used by this effect
+	 */
+	public void setFilter(BufferedImageOp filter) {
+		this.filter = filter;
+	}
+}
diff --git a/ext/slick/src/org/newdawn/slick/font/effects/GradientEffect.java b/ext/slick/src/org/newdawn/slick/font/effects/GradientEffect.java
new file mode 100644
index 0000000..aa7c2c3
--- /dev/null
+++ b/ext/slick/src/org/newdawn/slick/font/effects/GradientEffect.java
@@ -0,0 +1,195 @@
+
+package org.newdawn.slick.font.effects;
+
+import java.awt.Color;
+import java.awt.GradientPaint;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.newdawn.slick.UnicodeFont;
+import org.newdawn.slick.font.Glyph;
+
+/**
+ * Paints glyphs with a gradient fill.
+ * 
+ * @author Nathan Sweet 
+ */
+public class GradientEffect implements ConfigurableEffect {
+	/** The top of gradients colour */
+	private Color topColor = Color.cyan;
+	/** The bottom of the gradient's colour */
+	private Color bottomColor = Color.blue;
+	/** The offset the gradient starts at */
+	private int offset = 0;
+	/** The scaling of the graident */
+	private float scale = 1;
+	/** True if the graident should cycle back and forth across the surface */
+	private boolean cyclic;
+
+	/**
+	 * Default constructor for injection
+	 */
+	public GradientEffect() {
+	}
+
+	/**
+	 * Create a new effect to apply a graident
+	 * 
+	 * @param topColor The colour at the top of the graident
+	 * @param bottomColor The colour at the bottom of the gradient
+	 * @param scale The scale of the graident
+	 */
+	public GradientEffect(Color topColor, Color bottomColor, float scale) {
+		this.topColor = topColor;
+		this.bottomColor = bottomColor;
+		this.scale = scale;
+	}
+
+	/**
+	 * @see org.newdawn.slick.font.effects.Effect#draw(java.awt.image.BufferedImage, java.awt.Graphics2D, org.newdawn.slick.UnicodeFont, org.newdawn.slick.font.Glyph)
+	 */
+	public void draw(BufferedImage image, Graphics2D g, UnicodeFont unicodeFont, Glyph glyph) {
+		int ascent = unicodeFont.getAscent();
+		float height = (ascent) * scale;
+		float top = -glyph.getYOffset() + unicodeFont.getDescent() + offset + ascent / 2 - height / 2;
+		g.setPaint(new GradientPaint(0, top, topColor, 0, top + height, bottomColor, cyclic));
+		g.fill(glyph.getShape());
+	}
+
+	/**
+	 * Get the colour at the top of the graident
+	 * 
+	 * @return The colour at the top of the gradient
+	 */
+	public Color getTopColor() {
+		return topColor;
+	}
+
+	/**
+	 * Set the colour at the top of the graident
+	 * 
+	 * @param topColor The colour at the top of the graident
+	 */
+	public void setTopColor(Color topColor) {
+		this.topColor = topColor;
+	}
+
+	/**
+	 * Get the colour at the bottom of the graident
+	 * 
+	 * @return The colour at the bottom of the gradient
+	 */
+	public Color getBottomColor () {
+		return bottomColor;
+	}
+
+	/**
+	 * Set the colour at the bottom of the graident
+	 * 
+	 * @param bottomColor The colour at the bottom of the graident
+	 */
+	public void setBottomColor(Color bottomColor) {
+		this.bottomColor = bottomColor;
+	}
+
+	/**
+	 * Get the offset the gradients starts at
+	 * 
+	 * @return The offset the gradient starts at
+	 */
+	public int getOffset() {
+		return offset;
+	}
+
+	/**
+	 * Sets the pixel offset to move the gradient up or down. 
+	 * The gradient is normally centered on the glyph.
+	 * 
+	 * @param offset The offset the gradient is moved by
+	 */
+	public void setOffset (int offset) {
+		this.offset = offset;
+	}
+
+	/**
+	 * Get the percentage scaling being applied to the gradient across the surface
+	 * 
+	 * @return The scale of the graident
+	 */
+	public float getScale() {
+		return scale;
+	}
+
+	/**
+	 * Changes the height of the gradient by a percentage. The gradient is 
+	 * normally the height of most glyphs in the font.
+	 * 
+	 * @param scale The scale to apply
+	 */
+	public void setScale (float scale) {
+		this.scale = scale;
+	}
+
+	/**
+	 * Check if the graident is repeating
+	 * 
+	 * @return True if the gradient is repeating
+	 */
+	public boolean isCyclic() {
+		return cyclic;
+	}
+
+	/**
+	 * If set to true, the gradient will repeat.
+	 * 
+	 * @param cyclic True if the graident repeats
+	 */
+	public void setCyclic(boolean cyclic) {
+		this.cyclic = cyclic;
+	}
+
+	/**
+	 * @see java.lang.Object#toString()
+	 */
+	public String toString() {
+		return "Gradient";
+	}
+
+	/**
+	 * @see org.newdawn.slick.font.effects.ConfigurableEffect#getValues()
+	 */
+	public List getValues() {
+		List values = new ArrayList();
+		values.add(EffectUtil.colorValue("Top color", topColor));
+		values.add(EffectUtil.colorValue("Bottom color", bottomColor));
+		values.add(EffectUtil.intValue("Offset", offset,
+			"This setting allows you to move the gradient up or down. The gradient is normally centered on the glyph."));
+		values.add(EffectUtil.floatValue("Scale", scale, 0, 1, "This setting allows you to change the height of the gradient by a"
+			+ "percentage. The gradient is normally the height of most glyphs in the font."));
+		values.add(EffectUtil.booleanValue("Cyclic", cyclic, "If this setting is checked, the gradient will repeat."));
+		return values;
+	}
+
+	/**
+	 * @see org.newdawn.slick.font.effects.ConfigurableEffect#setValues(java.util.List)
+	 */
+	public void setValues(List values) {
+		for (Iterator iter = values.iterator(); iter.hasNext();) {
+			Value value = (Value)iter.next();
+			if (value.getName().equals("Top color")) {
+				topColor = (Color)value.getObject();
+			} else if (value.getName().equals("Bottom color")) {
+				bottomColor = (Color)value.getObject();
+			} else if (value.getName().equals("Offset")) {
+				offset = ((Integer)value.getObject()).intValue();
+			} else if (value.getName().equals("Scale")) {
+				scale = ((Float)value.getObject()).floatValue();
+			} else if (value.getName().equals("Cyclic")) {
+				cyclic = ((Boolean)value.getObject()).booleanValue();
+			}
+		}
+	}
+}
diff --git a/ext/slick/src/org/newdawn/slick/font/effects/OutlineEffect.java b/ext/slick/src/org/newdawn/slick/font/effects/OutlineEffect.java
new file mode 100644
index 0000000..1db6960
--- /dev/null
+++ b/ext/slick/src/org/newdawn/slick/font/effects/OutlineEffect.java
@@ -0,0 +1,178 @@
+
+package org.newdawn.slick.font.effects;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Stroke;
+import java.awt.image.BufferedImage;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.newdawn.slick.UnicodeFont;
+import org.newdawn.slick.font.Glyph;
+
+/**
+ * Strokes glyphs with an outline.
+ * 
+ * @author Nathan Sweet 
+ */
+public class OutlineEffect implements ConfigurableEffect {
+	/** The width of the outline in pixels */
+	private float width = 2;
+	/** The colour of the outline */
+	private Color color = Color.black;
+	/** The type of join at the line joins of the out line */
+	private int join = BasicStroke.JOIN_BEVEL;
+	/** The stroke used to draw the outline */
+	private Stroke stroke;
+
+	/**
+	 * Default constructor for injection
+	 */
+	public OutlineEffect() {
+	}
+
+	/**
+	 * Create a new effect to draw the outline of the text
+	 * 
+	 * @param width The width of the outline
+	 * @param color The colour of the outline
+	 */
+	public OutlineEffect(int width, Color color) {
+		this.width = width;
+		this.color = color;
+	}
+
+	/**
+	 * @see org.newdawn.slick.font.effects.Effect#draw(java.awt.image.BufferedImage, java.awt.Graphics2D, org.newdawn.slick.UnicodeFont, org.newdawn.slick.font.Glyph)
+	 */
+	public void draw(BufferedImage image, Graphics2D g, UnicodeFont unicodeFont, Glyph glyph) {
+		g = (Graphics2D)g.create();
+		if (stroke != null)
+			g.setStroke(stroke);
+		else
+			g.setStroke(getStroke());
+		g.setColor(color);
+		g.draw(glyph.getShape());
+		g.dispose();
+	}
+
+	/**
+	 * Get the width of the outline being drawn
+	 * 
+	 * @return The width of the outline being drawn
+	 */
+	public float getWidth() {
+		return width;
+	}
+
+	/**
+	 * Sets the width of the outline. The glyphs will need padding so the 
+	 * outline doesn't get clipped.
+	 * 
+	 * @param width The width of the outline being drawn
+	 */
+	public void setWidth (int width) {
+		this.width = width;
+	}
+
+	/**
+	 * Get the colour of the outline being drawn
+	 * 
+	 * @return The colour of the outline being drawn
+	 */
+	public Color getColor() {
+		return color;
+	}
+
+	/**
+	 * Set the colour of the outline being drawn
+	 * 
+	 * @param color The colour of the outline to draw
+	 */
+	public void setColor(Color color) {
+		this.color = color;
+	}
+
+	/**
+	 * Get the join type as indicated by @see BasicStroke
+	 * 
+	 * @return The join type between segments in the outline 
+	 */
+	public int getJoin() {
+		return join;
+	}
+
+	/**
+	 * Get the stroke being used to draw the outline
+	 * 
+	 * @return The stroke being used to draw the outline
+	 */
+	public Stroke getStroke() {
+		if (stroke == null) {
+			return new BasicStroke(width, BasicStroke.CAP_SQUARE, join);
+		}
+		
+		return stroke;
+	}
+
+	/**
+	 * Sets the stroke to use for the outline. If this is set, 
+	 * the other outline settings are ignored.
+	 * 
+	 * @param stroke The stroke to be used to draw the outline
+	 */
+	public void setStroke (Stroke stroke) {
+		this.stroke = stroke;
+	}
+
+	/**
+	 * Sets how the corners of the outline are drawn. This is usually only noticeable 
+	 * at large outline widths.
+	 * 
+	 * @param join One of: {@link BasicStroke#JOIN_BEVEL}, {@link BasicStroke#JOIN_MITER}, {@link BasicStroke#JOIN_ROUND}
+	 */
+	public void setJoin (int join) {
+		this.join = join;
+	}
+
+	/**
+	 * @see java.lang.Object#toString()
+	 */
+	public String toString () {
+		return "Outline";
+	}
+
+	/**
+	 * @see org.newdawn.slick.font.effects.ConfigurableEffect#getValues()
+	 */
+	public List getValues () {
+		List values = new ArrayList();
+		values.add(EffectUtil.colorValue("Color", color));
+		values.add(EffectUtil.floatValue("Width", width, 0.1f, 999, "This setting controls the width of the outline. "
+			+ "The glyphs will need padding so the outline doesn't get clipped."));
+		values.add(EffectUtil.optionValue("Join", String.valueOf(join), new String[][] { {"Bevel", BasicStroke.JOIN_BEVEL + ""},
+			{"Miter", BasicStroke.JOIN_MITER + ""}, {"Round", BasicStroke.JOIN_ROUND + ""}},
+			"This setting defines how the corners of the outline are drawn. "
+				+ "This is usually only noticeable at large outline widths."));
+		return values;
+	}
+
+	/**
+	 * @see org.newdawn.slick.font.effects.ConfigurableEffect#setValues(java.util.List)
+	 */
+	public void setValues (List values) {
+		for (Iterator iter = values.iterator(); iter.hasNext();) {
+			Value value = (Value)iter.next();
+			if (value.getName().equals("Color")) {
+				color = (Color)value.getObject();
+			} else if (value.getName().equals("Width")) {
+				width = ((Float)value.getObject()).floatValue();
+			} else if (value.getName().equals("Join")) {
+				join = Integer.parseInt((String)value.getObject());
+			}
+		}
+	}
+}
diff --git a/ext/slick/src/org/newdawn/slick/font/effects/OutlineWobbleEffect.java b/ext/slick/src/org/newdawn/slick/font/effects/OutlineWobbleEffect.java
new file mode 100644
index 0000000..917579e
--- /dev/null
+++ b/ext/slick/src/org/newdawn/slick/font/effects/OutlineWobbleEffect.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2006 Jerry Huxtable
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
+ * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+package org.newdawn.slick.font.effects;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Shape;
+import java.awt.Stroke;
+import java.awt.geom.FlatteningPathIterator;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.PathIterator;
+import java.util.Iterator;
+import java.util.List;
+
+
+/**
+ * An effect that genrates a wobbly line around the outline of the text
+ * 
+ * @author Jerry Huxtable
+ * @author Nathan Sweet 
+ */
+public class OutlineWobbleEffect extends OutlineEffect {
+	/** How often the line wobbles */
+	private float detail = 1;
+	/** The amount of the line wobbles */
+	private float amplitude = 1;
+
+	/**
+	 * Default constructor for injection
+	 */
+	public OutlineWobbleEffect () {
+		setStroke(new WobbleStroke());
+	}
+
+	/**
+	 * Gets the detail of the wobble effect.
+	 * 
+	 * @return The detail of the wobble effect
+	 */
+	public float getDetail() {
+		return detail;
+	}
+
+	/**
+	 * Sets the detail of the wobble effect.
+	 * 
+	 * @param detail The detail of the wobble effect
+	 */
+	public void setDetail(float detail) {
+		this.detail = detail;
+	}
+
+	/**
+	 * Gets the amplitude of the wobble effect.
+	 * 
+	 * @return The amplitude of the wobble effect
+	 */
+	public float getAmplitude() {
+		return amplitude;
+	}
+
+	/**
+	 * Sets the amplitude of the wobble effect.
+	 * 
+	 * @param amplitude The detail of the wobble effect
+	 */
+	public void setAmplitude(float amplitude) {
+		this.amplitude = amplitude;
+	}
+
+	/**
+	 * Create a new effect that generates a wobbly line around the text
+	 * 
+	 * @param width The width of the line
+	 * @param color The colour of the line
+	 */
+	public OutlineWobbleEffect (int width, Color color) {
+		super(width, color);
+	}
+
+	/**
+	 * @see org.newdawn.slick.font.effects.OutlineEffect#toString()
+	 */
+	public String toString() {
+		return "Outline (Wobble)";
+	}
+
+	/**
+	 * @see org.newdawn.slick.font.effects.OutlineEffect#getValues()
+	 */
+	public List getValues() {
+		List values = super.getValues();
+		values.remove(2); // Remove "Join".
+		values.add(EffectUtil.floatValue("Detail", detail, 1, 50, "This setting controls how detailed the outline will be. "
+			+ "Smaller numbers cause the outline to have more detail."));
+		values.add(EffectUtil.floatValue("Amplitude", amplitude, 0.5f, 50, "This setting controls the amplitude of the outline."));
+		return values;
+	}
+
+	/**
+	 * @see org.newdawn.slick.font.effects.OutlineEffect#setValues(java.util.List)
+	 */
+	public void setValues(List values) {
+		super.setValues(values);
+		for (Iterator iter = values.iterator(); iter.hasNext();) {
+			Value value = (Value)iter.next();
+			if (value.getName().equals("Detail")) {
+				detail = ((Float)value.getObject()).floatValue();
+			} else if (value.getName().equals("Amplitude")) {
+				amplitude = ((Float)value.getObject()).floatValue();
+			}
+		}
+	}
+
+	/**
+	 * A stroke that generate a wobbly line
+	 * 
+	 * @author Jerry Huxtable
+	 * @author Nathan Sweet 
+	 */
+	private class WobbleStroke implements Stroke {
+		/** The flattening factor of the stroke */
+		private static final float FLATNESS = 1;
+
+		/**
+		 * @see java.awt.Stroke#createStrokedShape(java.awt.Shape)
+		 */
+		public Shape createStrokedShape (Shape shape) {
+			GeneralPath result = new GeneralPath();
+			shape = new BasicStroke(getWidth(), BasicStroke.CAP_SQUARE, getJoin()).createStrokedShape(shape);
+			PathIterator it = new FlatteningPathIterator(shape.getPathIterator(null), FLATNESS);
+			float points[] = new float[6];
+			float moveX = 0, moveY = 0;
+			float lastX = 0, lastY = 0;
+			float thisX = 0, thisY = 0;
+			int type = 0;
+			float next = 0;
+			while (!it.isDone()) {
+				type = it.currentSegment(points);
+				switch (type) {
+				case PathIterator.SEG_MOVETO:
+					moveX = lastX = randomize(points[0]);
+					moveY = lastY = randomize(points[1]);
+					result.moveTo(moveX, moveY);
+					next = 0;
+					break;
+
+				case PathIterator.SEG_CLOSE:
+					points[0] = moveX;
+					points[1] = moveY;
+					// Fall into....
+
+				case PathIterator.SEG_LINETO:
+					thisX = randomize(points[0]);
+					thisY = randomize(points[1]);
+					float dx = thisX - lastX;
+					float dy = thisY - lastY;
+					float distance = (float)Math.sqrt(dx * dx + dy * dy);
+					if (distance >= next) {
+						float r = 1.0f / distance;
+						while (distance >= next) {
+							float x = lastX + next * dx * r;
+							float y = lastY + next * dy * r;
+							result.lineTo(randomize(x), randomize(y));
+							next += detail;
+						}
+					}
+					next -= distance;
+					lastX = thisX;
+					lastY = thisY;
+					break;
+				}
+				it.next();
+			}
+
+			return result;
+		}
+
+		/**
+		 * Get a random wobble factor
+		 * 
+		 * @param x The position on the line
+		 * @return The wobble factor
+		 */
+		private float randomize(float x) {
+			return x + (float)Math.random() * amplitude * 2 - 1;
+		}
+	}
+}
diff --git a/ext/slick/src/org/newdawn/slick/font/effects/OutlineZigzagEffect.java b/ext/slick/src/org/newdawn/slick/font/effects/OutlineZigzagEffect.java
new file mode 100644
index 0000000..cbdb3e3
--- /dev/null
+++ b/ext/slick/src/org/newdawn/slick/font/effects/OutlineZigzagEffect.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2006 Jerry Huxtable
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
+ * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+package org.newdawn.slick.font.effects;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Shape;
+import java.awt.Stroke;
+import java.awt.geom.FlatteningPathIterator;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.PathIterator;
+import java.util.Iterator;
+import java.util.List;
+
+
+/**
+ * An effect to generate a uniformly zigzaging line around text
+ * 
+ * @author Jerry Huxtable
+ * @author Nathan Sweet 
+ */
+public class OutlineZigzagEffect extends OutlineEffect {
+	/** The amount the line moves away from the text */
+	private float amplitude = 1;
+	/** How often the line zigs and zags */
+	private float wavelength = 3;
+
+	/**
+	 * Default constructor for injection
+	 */
+	public OutlineZigzagEffect() {
+		setStroke(new ZigzagStroke());
+	}
+
+	/**
+	 * Gets the wavelength of the wobble effect.
+	 * 
+	 * @return The wavelength of the wobble effect
+	 */
+	public float getWavelength() {
+		return wavelength;
+	}
+
+	/**
+	 * Sets the wavelength of the wobble effect.
+	 * 
+	 * @param wavelength The wavelength of the wobble effect
+	 */
+	public void setWavelength(float wavelength) {
+		this.wavelength = wavelength;
+	}
+
+	/**
+	 * Gets the amplitude of the wobble effect.
+	 * 
+	 * @return The amplitude of the wobble effect
+	 */
+	public float getAmplitude() {
+		return amplitude;
+	}
+
+	/**
+	 * Sets the amplitude of the wobble effect.
+	 * 
+	 * @param amplitude The detail of the wobble effect
+	 */
+	public void setAmplitude(float amplitude) {
+		this.amplitude = amplitude;
+	}
+	
+	/**
+	 * Create a new effect to generate a zigzagging line around the text
+	 * 
+	 * @param width The width of the line
+	 * @param color The colour of the line
+	 */
+	public OutlineZigzagEffect(int width, Color color) {
+		super(width, color);
+	}
+
+	/**
+	 * @see org.newdawn.slick.font.effects.OutlineEffect#toString()
+	 */
+	public String toString () {
+		return "Outline (Zigzag)";
+	}
+
+	/**
+	 * @see org.newdawn.slick.font.effects.OutlineEffect#getValues()
+	 */
+	public List getValues() {
+		List values = super.getValues();
+		values.add(EffectUtil.floatValue("Wavelength", wavelength, 1, 100, "This setting controls the wavelength of the outline. "
+			+ "The smaller the value, the more segments will be used to draw the outline."));
+		values.add(EffectUtil.floatValue("Amplitude", amplitude, 0.5f, 50, "This setting controls the amplitude of the outline. "
+			+ "The bigger the value, the more the zigzags will vary."));
+		return values;
+	}
+
+	/**
+	 * @see org.newdawn.slick.font.effects.OutlineEffect#setValues(java.util.List)
+	 */
+	public void setValues(List values) {
+		super.setValues(values);
+		for (Iterator iter = values.iterator(); iter.hasNext();) {
+			Value value = (Value)iter.next();
+			if (value.getName().equals("Wavelength")) {
+				wavelength = ((Float)value.getObject()).floatValue();
+			} else if (value.getName().equals("Amplitude")) {
+				amplitude = ((Float)value.getObject()).floatValue();
+			}
+		}
+	}
+
+	/**
+	 * A stroke to generate zigzags
+	 * 
+	 * @author Jerry Huxtable
+	 * @author Nathan Sweet 
+	 */
+	private class ZigzagStroke implements Stroke {
+		/** The flattening factor applied to the path iterator */
+		private static final float FLATNESS = 1;
+
+		/** 
+		 * @see java.awt.Stroke#createStrokedShape(java.awt.Shape)
+		 */
+		public Shape createStrokedShape (Shape shape) {
+			GeneralPath result = new GeneralPath();
+			PathIterator it = new FlatteningPathIterator(shape.getPathIterator(null), FLATNESS);
+			float points[] = new float[6];
+			float moveX = 0, moveY = 0;
+			float lastX = 0, lastY = 0;
+			float thisX = 0, thisY = 0;
+			int type = 0;
+			float next = 0;
+			int phase = 0;
+			while (!it.isDone()) {
+				type = it.currentSegment(points);
+				switch (type) {
+				case PathIterator.SEG_MOVETO:
+					moveX = lastX = points[0];
+					moveY = lastY = points[1];
+					result.moveTo(moveX, moveY);
+					next = wavelength / 2;
+					break;
+
+				case PathIterator.SEG_CLOSE:
+					points[0] = moveX;
+					points[1] = moveY;
+					// Fall into....
+
+				case PathIterator.SEG_LINETO:
+					thisX = points[0];
+					thisY = points[1];
+					float dx = thisX - lastX;
+					float dy = thisY - lastY;
+					float distance = (float)Math.sqrt(dx * dx + dy * dy);
+					if (distance >= next) {
+						float r = 1.0f / distance;
+						while (distance >= next) {
+							float x = lastX + next * dx * r;
+							float y = lastY + next * dy * r;
+							if ((phase & 1) == 0)
+								result.lineTo(x + amplitude * dy * r, y - amplitude * dx * r);
+							else
+								result.lineTo(x - amplitude * dy * r, y + amplitude * dx * r);
+							next += wavelength;
+							phase++;
+						}
+					}
+					next -= distance;
+					lastX = thisX;
+					lastY = thisY;
+					if (type == PathIterator.SEG_CLOSE) result.closePath();
+					break;
+				}
+				it.next();
+			}
+			return new BasicStroke(getWidth(), BasicStroke.CAP_SQUARE, getJoin()).createStrokedShape(result);
+		}
+	}
+}
diff --git a/ext/slick/src/org/newdawn/slick/font/effects/ShadowEffect.java b/ext/slick/src/org/newdawn/slick/font/effects/ShadowEffect.java
new file mode 100644
index 0000000..1b02126
--- /dev/null
+++ b/ext/slick/src/org/newdawn/slick/font/effects/ShadowEffect.java
@@ -0,0 +1,321 @@
+
+package org.newdawn.slick.font.effects;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.image.BufferedImage;
+import java.awt.image.ConvolveOp;
+import java.awt.image.Kernel;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.newdawn.slick.UnicodeFont;
+import org.newdawn.slick.font.Glyph;
+
+/**
+ * An effect to generate soft shadows beneath text 
+ * 
+ * @author Nathan Sweet 
+ */
+public class ShadowEffect implements ConfigurableEffect {
+	/** The number of kernels to apply */
+	public static final int NUM_KERNELS = 16;
+	/** The blur kernels applied across the effect */
+	public static final float[][] GAUSSIAN_BLUR_KERNELS = generateGaussianBlurKernels(NUM_KERNELS);
+
+	/** The colour of the shadow to render */
+	private Color color = Color.black;
+	/** The transparency factor of the shadow */
+	private float opacity = 0.6f;
+	/** The distance on the x axis of the shadow from the text */
+	private float xDistance = 2;
+	/** The distance on the y axis of the shadow from the text */
+	private float yDistance = 2;
+	/** The size of the kernel used to blur the shadow */
+	private int blurKernelSize = 0;
+	/** The number of passes applied to create the blur */
+	private int blurPasses = 1;
+
+	/**
+	 * Default constructor for injection
+	 */
+	public ShadowEffect() {
+	}
+
+	/**
+	 * Create a new effect to apply a drop shadow to text
+	 * 
+	 * @param color The colour of the shadow to generate
+	 * @param xDistance The distance from the text on the x axis the shadow should be rendered
+	 * @param yDistance The distance from the text on the y axis the shadow should be rendered
+	 * @param opacity The transparency factor of the shadow
+	 */
+	public ShadowEffect (Color color, int xDistance, int yDistance, float opacity) {
+		this.color = color;
+		this.xDistance = xDistance;
+		this.yDistance = yDistance;
+		this.opacity = opacity;
+	}
+
+	/**
+	 * @see org.newdawn.slick.font.effects.Effect#draw(java.awt.image.BufferedImage, java.awt.Graphics2D, org.newdawn.slick.UnicodeFont, org.newdawn.slick.font.Glyph)
+	 */
+	public void draw(BufferedImage image, Graphics2D g, UnicodeFont unicodeFont, Glyph glyph) {
+		g = (Graphics2D)g.create();
+		g.translate(xDistance, yDistance);
+		g.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), Math.round(opacity * 255)));
+		g.fill(glyph.getShape());
+
+		// Also shadow the outline, if one exists.
+		for (Iterator iter = unicodeFont.getEffects().iterator(); iter.hasNext();) {
+			Effect effect = (Effect)iter.next();
+			if (effect instanceof OutlineEffect) {
+				Composite composite = g.getComposite();
+				g.setComposite(AlphaComposite.Src); // Prevent shadow and outline shadow alpha from combining.
+
+				g.setStroke(((OutlineEffect)effect).getStroke());
+				g.draw(glyph.getShape());
+
+				g.setComposite(composite);
+				break;
+			}
+		}
+
+		g.dispose();
+		if (blurKernelSize > 1 && blurKernelSize < NUM_KERNELS && blurPasses > 0) blur(image);
+	}
+
+	/**
+	 * Apply blurring to the generate image
+	 * 
+	 * @param image The image to be blurred
+	 */
+	private void blur(BufferedImage image) {
+		float[] matrix = GAUSSIAN_BLUR_KERNELS[blurKernelSize - 1];
+		Kernel gaussianBlur1 = new Kernel(matrix.length, 1, matrix);
+		Kernel gaussianBlur2 = new Kernel(1, matrix.length, matrix);
+		RenderingHints hints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
+		ConvolveOp gaussianOp1 = new ConvolveOp(gaussianBlur1, ConvolveOp.EDGE_NO_OP, hints);
+		ConvolveOp gaussianOp2 = new ConvolveOp(gaussianBlur2, ConvolveOp.EDGE_NO_OP, hints);
+		BufferedImage scratchImage = EffectUtil.getScratchImage();
+		for (int i = 0; i < blurPasses; i++) {
+			gaussianOp1.filter(image, scratchImage);
+			gaussianOp2.filter(scratchImage, image);
+		}
+	}
+
+	/**
+	 * Get the colour of the shadow generated
+	 * 
+	 * @return The colour of the shadow generated
+	 */
+	public Color getColor() {
+		return color;
+	}
+
+	/**
+	 * Set the colour of the shadow to be generated
+	 * 
+	 * @param color The colour ofthe shadow to be generated
+	 */
+	public void setColor(Color color) {
+		this.color = color;
+	}
+
+	/**
+	 * Get the distance on the X axis from the text the shadow should
+	 * be generated at
+	 * 
+	 * @return The distance on the X axis the shadow will be from the text
+	 */
+	public float getXDistance() {
+		return xDistance;
+	}
+
+	/**
+	 * Sets the pixels to offset the shadow on the x axis. The glyphs will need padding so the 
+	 * shadow doesn't get clipped.
+	 * 
+	 * @param distance The offset on the x axis
+	 */
+	public void setXDistance(float distance) {
+		xDistance = distance;
+	}
+
+	/**
+	 * Get the distance on the Y axis from the text the shadow should
+	 * be generated at
+	 * 
+	 * @return The distance on the Y axis the shadow will be from the text
+	 */
+	public float getYDistance() {
+		return yDistance;
+	}
+
+	/**
+	 * Sets the pixels to offset the shadow on the y axis. The glyphs will need 
+	 * padding so the shadow doesn't get clipped.
+	 * 
+	 * @param distance The offset on the y axis
+	 */
+	public void setYDistance (float distance) {
+		yDistance = distance;
+	}
+
+	/**
+	 * Get the size of the kernel used to apply the blur
+	 * 
+	 * @return The blur kernel size
+	 */
+	public int getBlurKernelSize() {
+		return blurKernelSize;
+	}
+
+	/**
+	 * Sets how many neighboring pixels are used to blur the shadow. Set to 0 for no blur.
+	 * 
+	 * @param blurKernelSize The size of the kernel to apply the blur with
+	 */
+	public void setBlurKernelSize (int blurKernelSize) {
+		this.blurKernelSize = blurKernelSize;
+	}
+
+	/**
+	 * Get the number of passes to apply the kernel for blurring
+	 * 
+	 * @return The number of passes
+	 */
+	public int getBlurPasses() {
+		return blurPasses;
+	}
+
+	/**
+	 * Sets the number of times to apply a blur to the shadow. Set to 0 for no blur.
+	 * 
+	 * @param blurPasses The number of passes to apply when blurring
+	 */
+	public void setBlurPasses (int blurPasses) {
+		this.blurPasses = blurPasses;
+	}
+
+	/**
+	 * Get the opacity of the shadow, i.e. how transparent it is
+	 * 
+	 * @return The opacity of the shadow
+	 */
+	public float getOpacity() {
+		return opacity;
+	}
+
+	/**
+	 * Set the opacity of the shadow, i.e. how transparent it is
+	 * 
+	 * @param opacity The opacity of the shadow
+	 */
+	public void setOpacity(float opacity) {
+		this.opacity = opacity;
+	}
+
+	/**
+	 * @see java.lang.Object#toString()
+	 */
+	public String toString() {
+		return "Shadow";
+	}
+
+	/**
+	 * @see org.newdawn.slick.font.effects.ConfigurableEffect#getValues()
+	 */
+	public List getValues() {
+		List values = new ArrayList();
+		values.add(EffectUtil.colorValue("Color", color));
+		values.add(EffectUtil.floatValue("Opacity", opacity, 0, 1, "This setting sets the translucency of the shadow."));
+		values.add(EffectUtil.floatValue("X distance", xDistance, Float.MIN_VALUE, Float.MAX_VALUE, "This setting is the amount of pixels to offset the shadow on the"
+			+ " x axis. The glyphs will need padding so the shadow doesn't get clipped."));
+		values.add(EffectUtil.floatValue("Y distance", yDistance, Float.MIN_VALUE, Float.MAX_VALUE, "This setting is the amount of pixels to offset the shadow on the"
+			+ " y axis. The glyphs will need padding so the shadow doesn't get clipped."));
+
+		List options = new ArrayList();
+		options.add(new String[] {"None", "0"});
+		for (int i = 2; i < NUM_KERNELS; i++)
+			options.add(new String[] {String.valueOf(i)});
+		String[][] optionsArray = (String[][])options.toArray(new String[options.size()][]);
+		values.add(EffectUtil.optionValue("Blur kernel size", String.valueOf(blurKernelSize), optionsArray,
+			"This setting controls how many neighboring pixels are used to blur the shadow. Set to \"None\" for no blur."));
+
+		values.add(EffectUtil.intValue("Blur passes", blurPasses,
+			"The setting is the number of times to apply a blur to the shadow. Set to \"0\" for no blur."));
+		return values;
+	}
+
+	/**
+	 * @see org.newdawn.slick.font.effects.ConfigurableEffect#setValues(java.util.List)
+	 */
+	public void setValues(List values) {
+		for (Iterator iter = values.iterator(); iter.hasNext();) {
+			Value value = (Value)iter.next();
+			if (value.getName().equals("Color")) {
+				color = (Color)value.getObject();
+			} else if (value.getName().equals("Opacity")) {
+				opacity = ((Float)value.getObject()).floatValue();
+			} else if (value.getName().equals("X distance")) {
+				xDistance = ((Float)value.getObject()).floatValue();
+			} else if (value.getName().equals("Y distance")) {
+				yDistance = ((Float)value.getObject()).floatValue();
+			} else if (value.getName().equals("Blur kernel size")) {
+				blurKernelSize = Integer.parseInt((String)value.getObject());
+			} else if (value.getName().equals("Blur passes")) {
+				blurPasses = ((Integer)value.getObject()).intValue();
+			}
+		}
+	}
+
+	/**
+	 * Generate the blur kernels which will be repeatedly applied when blurring images
+	 * 
+	 * @param level The number of kernels to generate
+	 * @return The kernels generated
+	 */
+	private static float[][] generateGaussianBlurKernels(int level) {
+		float[][] pascalsTriangle = generatePascalsTriangle(level);
+		float[][] gaussianTriangle = new float[pascalsTriangle.length][];
+		for (int i = 0; i < gaussianTriangle.length; i++) {
+			float total = 0.0f;
+			gaussianTriangle[i] = new float[pascalsTriangle[i].length];
+			for (int j = 0; j < pascalsTriangle[i].length; j++)
+				total += pascalsTriangle[i][j];
+			float coefficient = 1 / total;
+			for (int j = 0; j < pascalsTriangle[i].length; j++)
+				gaussianTriangle[i][j] = coefficient * pascalsTriangle[i][j];
+		}
+		return gaussianTriangle;
+	}
+
+	/**
+	 * Generate Pascal's triangle
+	 * 
+	 * @param level The level of the triangle to generate
+	 * @return The Pascal's triangle kernel
+	 */
+	private static float[][] generatePascalsTriangle(int level) {
+		if (level < 2) level = 2;
+		float[][] triangle = new float[level][];
+		triangle[0] = new float[1];
+		triangle[1] = new float[2];
+		triangle[0][0] = 1.0f;
+		triangle[1][0] = 1.0f;
+		triangle[1][1] = 1.0f;
+		for (int i = 2; i < level; i++) {
+			triangle[i] = new float[i + 1];
+			triangle[i][0] = 1.0f;
+			triangle[i][i] = 1.0f;
+			for (int j = 1; j < triangle[i].length - 1; j++)
+				triangle[i][j] = triangle[i - 1][j - 1] + triangle[i - 1][j];
+		}
+		return triangle;
+	}
+}
diff --git a/ext/slick/src/org/newdawn/slick/geom/BasicTriangulator.java b/ext/slick/src/org/newdawn/slick/geom/BasicTriangulator.java
new file mode 100644
index 0000000..cb03a92
--- /dev/null
+++ b/ext/slick/src/org/newdawn/slick/geom/BasicTriangulator.java
@@ -0,0 +1,437 @@
+package org.newdawn.slick.geom;
+
+import java.util.ArrayList;
+
+/**
+ * Triangulates a polygon into triangles - duh. Doesn't handle
+ * holes in polys
+ * 
+ * @author Based on Public Source from FlipCode
+ */
+public class BasicTriangulator implements Triangulator {
+	/** The accepted error value */
+	private static final float EPSILON = 0.0000000001f;
+	/** The list of points to be triangulated */
+	private PointList poly = new PointList();
+	/** The list of points describing the triangles */
+	private PointList tris = new PointList();
+	/** True if we've tried to triangulate */
+	private boolean tried;
+	
+	/**
+	 * Create a new triangulator
+	 */
+	public BasicTriangulator() {
+	}
+	
+	/**
+	 * Add a point describing the polygon to be triangulated
+	 * 
+	 * @param x The x coordinate of the point
+	 * @param y the y coordinate of the point
+	 */
+	public void addPolyPoint(float x, float y) {
+		Point p = new Point(x,y);
+		if (!poly.contains(p)) {
+			poly.add(p);
+		}
+	}
+	
+	/**
+	 * Get the number of points in the polygon
+	 * 
+	 * @return The number of points in the polygon
+	 */
+	public int getPolyPointCount() {
+		return poly.size();
+	}
+
+	/**
+	 * Get the coordinates of the point at the specified index
+	 * 
+	 * @param index The index of the point to retrieve
+	 * @return The oordinates of the point at the specified index
+	 */
+	public float[] getPolyPoint(int index) {
+		return new float[] {poly.get(index).x,poly.get(index).y};
+	}
+	
+	/**
+	 * Cause the triangulator to split the polygon
+	 * 
+	 * @return True if we managed the task
+	 */
+	public boolean triangulate() {
+		tried = true;
+		
+		boolean worked = process(poly,tris);
+		return worked;
+	}
+	
+	/**
+	 * Get a count of the number of triangles produced
+	 * 
+	 * @return The number of triangles produced
+	 */
+	public int getTriangleCount() {
+		if (!tried) {
+			throw new RuntimeException("Call triangulate() before accessing triangles");
+		}
+		return tris.size() / 3;
+	}
+	
+	/**
+	 * Get a point on a specified generated triangle
+	 * 
+	 * @param tri The index of the triangle to interegate
+	 * @param i The index of the point within the triangle to retrieve
+	 * (0 - 2)
+	 * @return The x,y coordinate pair for the point
+	 */
+	public float[] getTrianglePoint(int tri, int i) {
+		if (!tried) {
+			throw new RuntimeException("Call triangulate() before accessing triangles");
+		}
+		
+		return tris.get((tri*3)+i).toArray();
+	}
+	
+	/** 
+	 * Find the area of a polygon defined by the series of points
+	 * in the list
+	 * 
+	 * @param contour The list of points defined the contour of the polygon
+	 * (Vector2f)
+	 * @return The area of the polygon defined
+	 */
+	private float area(PointList contour) {
+		int n = contour.size();
+
+		float A = 0.0f;
+
+		for (int p = n - 1, q = 0; q < n; p = q++) {
+			Point contourP = contour.get(p);
+			Point contourQ = contour.get(q);
+
+			A += contourP.getX() * contourQ.getY() - contourQ.getX()
+					* contourP.getY();
+		}
+		return A * 0.5f;
+	}
+
+	/**
+	 * Check if the point P is inside the triangle defined by
+	 * the points A,B,C
+	 * 
+	 * @param Ax Point A x-coordinate
+	 * @param Ay Point A y-coordinate
+	 * @param Bx Point B x-coordinate
+	 * @param By Point B y-coordinate
+	 * @param Cx Point C x-coordinate
+	 * @param Cy Point C y-coordinate
+	 * @param Px Point P x-coordinate
+	 * @param Py Point P y-coordinate
+	 * @return True if the point specified is within the triangle
+	 */
+	private boolean insideTriangle(float Ax, float Ay, float Bx,
+			float By, float Cx, float Cy, float Px, float Py) {
+		float ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy;
+		float cCROSSap, bCROSScp, aCROSSbp;
+
+		ax = Cx - Bx;
+		ay = Cy - By;
+		bx = Ax - Cx;
+		by = Ay - Cy;
+		cx = Bx - Ax;
+		cy = By - Ay;
+		apx = Px - Ax;
+		apy = Py - Ay;
+		bpx = Px - Bx;
+		bpy = Py - By;
+		cpx = Px - Cx;
+		cpy = Py - Cy;
+
+		aCROSSbp = ax * bpy - ay * bpx;
+		cCROSSap = cx * apy - cy * apx;
+		bCROSScp = bx * cpy - by * cpx;
+
+		return ((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f));
+	}
+
+	/**
+	 * Cut a the contour and add a triangle into V to describe the 
+	 * location of the cut
+	 * 
+	 * @param contour The list of points defining the polygon
+	 * @param u The index of the first point
+	 * @param v The index of the second point
+	 * @param w The index of the third point
+	 * @param n ?
+	 * @param V The array to populate with indicies of triangles
+	 * @return True if a triangle was found
+	 */
+	private boolean snip(PointList contour, int u, int v, int w, int n,
+			int[] V) {
+		int p;
+		float Ax, Ay, Bx, By, Cx, Cy, Px, Py;
+
+		Ax = contour.get(V[u]).getX();
+		Ay = contour.get(V[u]).getY();
+
+		Bx = contour.get(V[v]).getX();
+		By = contour.get(V[v]).getY();
+
+		Cx = contour.get(V[w]).getX();
+		Cy = contour.get(V[w]).getY();
+
+		if (EPSILON > (((Bx - Ax) * (Cy - Ay)) - ((By - Ay) * (Cx - Ax)))) {
+			return false;
+		}
+
+		for (p = 0; p < n; p++) {
+			if ((p == u) || (p == v) || (p == w)) {
+				continue;
+			}
+
+			Px = contour.get(V[p]).getX();
+			Py = contour.get(V[p]).getY();
+
+			if (insideTriangle(Ax, Ay, Bx, By, Cx, Cy, Px, Py)) {
+				return false;
+			}
+		}
+
+		return true;
+	}
+
+	/**
+	 * Process a list of points defining a polygon
+	 * @param contour The list of points describing the polygon
+	 * @param result The list of points describing the triangles. Groups
+	 * of 3 describe each triangle 
+	 * 
+	 * @return True if we succeeded in completing triangulation
+	 */
+	private boolean process(PointList contour, PointList result) {
+		result.clear();
+		
+		/* allocate and initialize list of Vertices in polygon */
+
+		int n = contour.size();
+		if (n < 3)
+			return false;
+
+		int[] V = new int[n];
+
+		/* we want a counter-clockwise polygon in V */
+
+		if (0.0f < area(contour)) {
+			for (int v = 0; v < n; v++)
+				V[v] = v;
+		} else {
+			for (int v = 0; v < n; v++)
+				V[v] = (n - 1) - v;
+		}
+
+		int nv = n;
+
+		/*  remove nv-2 Vertices, creating 1 triangle every time */
+		int count = 2 * nv; /* error detection */
+
+		for (int m = 0, v = nv - 1; nv > 2;) {
+			/* if we loop, it is probably a non-simple polygon */
+			if (0 >= (count--)) {
+				//** Triangulator4: ERROR - probable bad polygon!
+				return false;
+			}
+
+			/* three consecutive vertices in current polygon,  */
+			int u = v;
+			if (nv <= u)
+				u = 0; /* previous */
+			v = u + 1;
+			if (nv <= v)
+				v = 0; /* new v    */
+			int w = v + 1;
+			if (nv <= w)
+				w = 0; /* next     */
+
+			if (snip(contour, u, v, w, nv, V)) {
+				int a, b, c, s, t;
+
+				/* true names of the vertices */
+				a = V[u];
+				b = V[v];
+				c = V[w];
+
+				/* output Triangle */
+				result.add(contour.get(a));
+				result.add(contour.get(b));
+				result.add(contour.get(c));
+
+				m++;
+
+				/* remove v from remaining polygon */
+				for (s = v, t = v + 1; t < nv; s++, t++) {
+					V[s] = V[t];
+				}
+				nv--;
+
+				/* resest error detection counter */
+				count = 2 * nv;
+			}
+		}
+
+		return true;
+	}
+
+	/**
+	 * A single point handled by the triangulator
+	 * 
+	 * @author Kevin Glass
+	 */
+	private class Point {
+		/** The x coorindate of this point */
+		private float x;
+		/** The y coorindate of this point */
+		private float y;
+		/** The points in an array */
+		private float[] array;
+		
+		/**
+		 * Create a new point
+		 * 
+		 * @param x The x coordindate of the point
+		 * @param y The y coordindate of the point
+		 */
+		public Point(float x, float y) {
+			this.x = x;
+			this.y = y;
+			array = new float[] {x,y};
+		}
+
+		/**
+		 * Get the x coordinate of the point
+		 * 
+		 * @return The x coordinate of the point
+		 */
+		public float getX() {
+			return x;
+		}
+
+		/**
+		 * Get the y coordinate of the point
+		 * 
+		 * @return The y coordinate of the point
+		 */
+		public float getY() {
+			return y;
+		}
+	
+		/**
+		 * Convert this point into a float array
+		 * 
+		 * @return The contents of this point as a float array
+		 */
+		public float[] toArray() {
+			return array;
+		}
+		
+		/**
+		 * @see java.lang.Object#hashCode()
+		 */
+		public int hashCode() {
+			return (int) (x * y * 31);
+		}
+		
+		/**
+		 * @see java.lang.Object#equals(java.lang.Object)
+		 */
+		public boolean equals(Object other) {
+			if (other instanceof Point) {
+				Point p = (Point) other;
+				return (p.x == x) && (p.y == y);
+			}
+			
+			return false;
+		}
+	}
+	
+	/**
+	 * A list of type Point
+	 * 
+	 * @author Kevin Glass
+	 */
+	private class PointList {
+		/** The list of points */
+		private ArrayList points = new ArrayList();
+		
+		/**
+		 * Create a new empty list
+		 */
+		public PointList() {
+		}
+		
+		/**
+		 * Check if the list contains a point
+		 * 
+		 * @param p The point to look for
+		 * @return True if the point is in the list
+		 */
+		public boolean contains(Point p) {
+			return points.contains(p);
+		}
+		
+		/**
+		 * Add a point to the list 
+		 * 
+		 * @param point The point to add
+		 */
+		public void add(Point point) {
+			points.add(point);
+		}
+		
+		/**
+		 * Remove a point from the list
+		 * 
+		 * @param point The point to remove
+		 */
+		public void remove(Point point) {
+			points.remove(point);
+		}
+		
+		/**
+		 * Get the size of the list
+		 * 
+		 * @return The size of the list
+		 */
+		public int size() {
+			return points.size();
+		}
+		
+		/**
+		 * Get a point a specific index in the list
+		 * 
+		 * @param i The index of the point to retrieve
+		 * @return The point
+		 */
+		public Point get(int i) {
+			return (Point) points.get(i);
+		}
+		
+		/**
+		 * Clear the list
+		 */
+		public void clear() {
+			points.clear();
+		}
+	}
+
+	/**
+	 * @see org.newdawn.slick.geom.Triangulator#startHole()
+	 */
+	public void startHole() {
+		// TODO Auto-generated method stub
+		
+	}
+}
diff --git a/ext/slick/src/org/newdawn/slick/geom/Circle.java b/ext/slick/src/org/newdawn/slick/geom/Circle.java
new file mode 100644
index 0000000..950ff74
--- /dev/null
+++ b/ext/slick/src/org/newdawn/slick/geom/Circle.java
@@ -0,0 +1,178 @@
+package org.newdawn.slick.geom;
+
+/**
+ * A simple Circle geometry
+ * 
+ * @author Kevin Glass
+ */
+public strictfp class Circle extends Ellipse {
+	/** The radius of the circle */
+	public float radius;
+	
+	/**
+	 * Create a new circle based on its radius
+	 * 
+	 * @param centerPointX The x location of the center of the circle
+	 * @param centerPointY The y location of the center of the circle
+	 * @param radius The radius of the circle
+	 */
+	public Circle(float centerPointX, float centerPointY, float radius) {
+        this(centerPointX, centerPointY, radius, DEFAULT_SEGMENT_COUNT);
+	}
+
+	/**
+	 * Create a new circle based on its radius
+	 * 
+	 * @param centerPointX The x location of the center of the circle
+	 * @param centerPointY The y location of the center of the circle
+	 * @param radius The radius of the circle
+	 * @param segmentCount The number of segments to build the circle out of
+	 */
+	public Circle(float centerPointX, float centerPointY, float radius, int segmentCount) {
+        super(centerPointX, centerPointY, radius, radius, segmentCount);
+        this.x = centerPointX - radius;
+        this.y = centerPointY - radius;
+        this.radius = radius;
+        boundingCircleRadius = radius;
+	}
+	
+	/** 
+	 * Get the x coordinate of the centre of the circle
+	 * 
+	 * @return The x coordinate of the centre of the circle
+	 */
+	public float getCenterX() {
+		return getX() + radius;
+	}
+	
+	/** 
+	 * Get the y coordinate of the centre of the circle
+	 * 
+	 * @return The y coordinate of the centre of the circle
+	 */
+	public float getCenterY() {
+		return getY() + radius;
+	}
+	
+	/**
+	 * Set the radius of this circle
+	 * 
+	 * @param radius The radius of this circle
+	 */
+	public void setRadius(float radius) {
+		if (radius != this.radius) {
+	        pointsDirty = true;
+			this.radius = radius;
+	        setRadii(radius, radius);
+		}
+	}
+	
+	/**
+	 * Get the radius of the circle
+	 * 
+	 * @return The radius of the circle
+	 */
+	public float getRadius() {
+		return radius;
+	}
+	
+	/**
+	 * Check if this circle touches another
+	 * 
+	 * @param shape The other circle
+	 * @return True if they touch
+	 */
+	public boolean intersects(Shape shape) {
+        if(shape instanceof Circle) {
+            Circle other = (Circle)shape;
+    		float totalRad2 = getRadius() + other.getRadius();
+    		
+    		if (Math.abs(other.getCenterX() - getCenterX()) > totalRad2) {
+    			return false;
+    		}
+    		if (Math.abs(other.getCenterY() - getCenterY()) > totalRad2) {
+    			return false;
+    		}
+    		
+    		totalRad2 *= totalRad2;
+    		
+    		float dx = Math.abs(other.getCenterX() - getCenterX());
+    		float dy = Math.abs(other.getCenterY() - getCenterY());
+    		
+    		return totalRad2 >= ((dx*dx) + (dy*dy));
+        }
+        else if(shape instanceof Rectangle) {
+            return intersects((Rectangle)shape);
+        }
+        else {
+            return super.intersects(shape);
+        }
+	}
+	
+	/**
+	 * Check if a point is contained by this circle
+	 * 
+	 * @param x The x coordinate of the point to check
+	 * @param y The y coorindate of the point to check
+	 * @return True if the point is contained by this circle
+	 */
+	public boolean contains(float x, float y) {
+		return intersects(new Circle(x,y,0));
+	}
+	
+	/**
+	 * @see org.newdawn.slick.geom.Ellipse#findCenter()
+	 */
+    protected void findCenter() {
+        center = new float[2];
+        center[0] = x + radius;
+        center[1] = y + radius;
+    }
+
+    /**
+     * @see org.newdawn.slick.geom.Ellipse#calculateRadius()
+     */
+    protected void calculateRadius() {
+        boundingCircleRadius = radius;
+    }
+
+    /**
+	 * Check if this circle touches a rectangle
+	 * 
+	 * @param other The rectangle to check against
+	 * @return True if they touch
+	 */
+	private boolean intersects(Rectangle other) {
+		Rectangle box = other;
+		Circle circle = this;
+		
+		if (box.contains(x,y)) {
+			return true;
+		}
+		
+		float x1 = box.getX();
+		float y1 = box.getY();
+		float x2 = box.getX() + box.getWidth();
+		float y2 = box.getY() + box.getHeight();
+		
+		Line[] lines = new Line[4];
+		lines[0] = new Line(x1,y1,x2,y1);
+		lines[1] = new Line(x2,y1,x2,y2);
+		lines[2] = new Line(x2,y2,x1,y2);
+		lines[3] = new Line(x1,y2,x1,y1);
+		
+		float r2 = circle.getRadius() * circle.getRadius();
+		
+		Vector2f pos = new Vector2f(circle.getCenterX(), circle.getCenterY());
+		
+		for (int i=0;i<4;i++) {
+			float dis = lines[i].distanceSquared(pos);
+			if (dis < r2) {
+				return true;
+			}
+		}
+		
+		return false;
+	}
+
+}
diff --git a/ext/slick/src/org/newdawn/slick/geom/Curve.java b/ext/slick/src/org/newdawn/slick/geom/Curve.java
new file mode 100644
index 0000000..159d070
--- /dev/null
+++ b/ext/slick/src/org/newdawn/slick/geom/Curve.java
@@ -0,0 +1,113 @@
+package org.newdawn.slick.geom;
+
+/**
+ * A beizer curve implementation. The curve is defined by a start point, an end point
+ * and two control points that it will tend towards. This is implementation is fixed
+ * segmenting meaning it doesn't scale too well.
+ *
+ * @author kevin
+ */
+public class Curve extends Shape {
+	/** The start point of the curve */
+	private Vector2f p1;
+	/** The first control point */
+	private Vector2f c1;
+	/** The second control point */
+	private Vector2f c2;
+	/** The end point of the curve */
+	private Vector2f p2;
+	/** The number of lines segments the curve is built out of */
+	private int segments;
+	
+	/**
+	 * Create a new curve with the default segments (20)
+	 * 
+	 * @param p1 The start of the curve
+	 * @param c1 The first control point
+	 * @param c2 The second control point
+	 * @param p2 The end of the curve
+	 */
+	public Curve(Vector2f p1, Vector2f c1, Vector2f c2, Vector2f p2) {
+		this(p1,c1,c2,p2,20);
+	}
+
+	/**
+	 * Create a new curve 
+	 * 
+	 * @param p1 The start of the curve
+	 * @param c1 The first control point
+	 * @param c2 The second control point
+	 * @param p2 The end of the curve
+	 * @param segments The number of segments to use
+	 */
+	public Curve(Vector2f p1, Vector2f c1, Vector2f c2, Vector2f p2, int segments) {
+		this.p1 = new Vector2f(p1);
+		this.c1 = new Vector2f(c1);
+		this.c2 = new Vector2f(c2);
+		this.p2 = new Vector2f(p2);
+	
+		this.segments = segments;
+		pointsDirty = true;
+	}
+	
+	/**
+	 * Get the point at a particular location on the curve
+	 * 
+	 * @param t A value between 0 and 1 defining the location of the curve the point is at
+	 * @return The point on the curve
+	 */
+	public Vector2f pointAt(float t) {
+		float a = 1 - t;
+		float b = t;
+		
+		float f1 = a * a * a;
+		float f2 = 3 * a * a * b;
+		float f3 = 3 * a * b * b;
+		float f4 = b * b * b;
+		
+		float nx = (p1.x * f1) + (c1.x * f2) + (c2.x * f3) + (p2.x * f4);
+		float ny = (p1.y * f1) + (c1.y * f2) + (c2.y * f3) + (p2.y * f4);
+		
+		return new Vector2f(nx,ny);
+	}
+
+	/**
+	 * @see org.newdawn.slick.geom.Shape#createPoints()
+	 */
+	protected void createPoints() {
+		float step = 1.0f / segments;
+		points = new float[(segments+1) * 2];
+		for (int i=0;iShape contract. The ellipse is actually an approximation using 
+ * a series of points generated around the contour of the ellipse.
+ * 
+ * @author Mark
+ */
+public class Ellipse extends Shape {
+    /**
+     * Default number of segments to draw this ellipse with
+     */
+    protected static final int DEFAULT_SEGMENT_COUNT = 50;
+    
+    /**
+     * The number of segments for graphical representation.
+     */
+    private int segmentCount;
+    /**
+     * horizontal radius
+     */
+    private float radius1;
+    /**
+     * vertical radius
+     */
+    private float radius2;
+
+    /**
+     * Creates a new Ellipse object.
+     *
+     * @param centerPointX x coordinate of the center of the ellipse
+     * @param centerPointY y coordinate of the center of the ellipse
+     * @param radius1 horizontal radius
+     * @param radius2 vertical radius
+     */
+    public Ellipse(float centerPointX, float centerPointY, float radius1, float radius2) {
+        this(centerPointX, centerPointY, radius1, radius2, DEFAULT_SEGMENT_COUNT);
+    }
+
+    /**
+     * Creates a new Ellipse object.
+     *
+     * @param centerPointX x coordinate of the center of the ellipse
+     * @param centerPointY y coordinate of the center of the ellipse
+     * @param radius1 horizontal radius
+     * @param radius2 vertical radius
+     * @param segmentCount how fine to make the ellipse.
+     */
+    public Ellipse(float centerPointX, float centerPointY, float radius1, float radius2, int segmentCount) {
+        this.x = centerPointX - radius1;
+        this.y = centerPointY - radius2;
+        this.radius1 = radius1;
+        this.radius2 = radius2;
+        this.segmentCount = segmentCount;
+        checkPoints();
+    }
+
+    /**
+     * Change the shape of this Ellipse
+     * 
+     * @param radius1 horizontal radius
+     * @param radius2 vertical radius
+     */
+    public void setRadii(float radius1, float radius2) {
+    	setRadius1(radius1);
+    	setRadius2(radius2);
+    }
+
+    /**
+     * Get the horizontal radius of the ellipse
+     * 
+     * @return The horizontal radius of the ellipse
+     */
+    public float getRadius1() {
+        return radius1;
+    }
+
+    /**
+     * Set the horizontal radius of the ellipse
+     * 
+     * @param radius1 The horizontal radius to set
+     */
+    public void setRadius1(float radius1) {
+    	if (radius1 != this.radius1) {
+	        this.radius1 = radius1;
+	        pointsDirty = true;
+    	}
+    }
+
+    /**
+     * Get the vertical radius of the ellipse
+     * 
+     * @return The vertical radius of the ellipse
+     */
+    public float getRadius2() {
+        return radius2;
+    }
+
+    /**
+     * Set the vertical radius of the ellipse
+     * 
+     * @param radius2 The vertical radius to set
+     */
+    public void setRadius2(float radius2) {
+    	if (radius2 != this.radius2) {
+	        this.radius2 = radius2;
+	        pointsDirty = true;
+    	}
+    }
+
+    /**
+     * Generate the points to outline this ellipse.
+     *
+     */
+    protected void createPoints() {
+        ArrayList tempPoints = new ArrayList();
+
+        maxX = -Float.MIN_VALUE;
+        maxY = -Float.MIN_VALUE;
+        minX = Float.MAX_VALUE;
+        minY = Float.MAX_VALUE;
+
+        float start = 0;
+        float end = 359;
+        
+        float cx = x + radius1;
+        float cy = y + radius2;
+        
+        int step = 360 / segmentCount;
+        
+        for (float a=start;a<=end+step;a+=step) {
+            float ang = a;
+            if (ang > end) {
+                ang = end;
+            }
+            float newX = (float) (cx + (FastTrig.cos(Math.toRadians(ang)) * radius1));
+            float newY = (float) (cy + (FastTrig.sin(Math.toRadians(ang)) * radius2));
+
+            if(newX > maxX) {
+                maxX = newX;
+            }
+            if(newY > maxY) {
+                maxY = newY;
+            }
+            if(newX < minX) {
+            	minX = newX;
+            }
+            if(newY < minY) {
+            	minY = newY;
+            }
+            
+            tempPoints.add(new Float(newX));
+            tempPoints.add(new Float(newY));
+        }
+        points = new float[tempPoints.size()];
+        for(int i=0;i radius2) ? radius1 : radius2;
+    }
+}
diff --git a/ext/slick/src/org/newdawn/slick/geom/GeomUtil.java b/ext/slick/src/org/newdawn/slick/geom/GeomUtil.java
new file mode 100644
index 0000000..a730850
--- /dev/null
+++ b/ext/slick/src/org/newdawn/slick/geom/GeomUtil.java
@@ -0,0 +1,449 @@
+package org.newdawn.slick.geom;
+
+import java.util.ArrayList;
+
+/**
+ * A set of utilities to play with geometry
+ * 
+ * @author kevin
+ */
+public class GeomUtil {
+	/** The tolerance for determining changes and steps */
+	public float EPSILON = 0.0001f;
+	/** The tolerance for determining direction change */
+	public float EDGE_SCALE = 1f;
+	/** The maximum number of points returned by an operation - prevents full lockups */
+	public int MAX_POINTS = 10000;
+	/** The listener to notify of operations */
+	public GeomUtilListener listener;
+	
+	/**
+	 * Subtract one shape from another - note this is experimental and doesn't
+	 * currently handle islands
+	 *  
+	 * @param target The target to be subtracted from
+	 * @param missing The shape to subtract 
+	 * @return The newly created shapes
+	 */
+	public Shape[] subtract(Shape target, Shape missing) {	
+		target = target.transform(new Transform());
+		missing = missing.transform(new Transform());
+
+		int count = 0;
+		for (int i=0;i MAX_POINTS) {
+				break;
+			}
+			
+			// add the current point to the result shape
+			poly.addPoint(px,py);
+			if (listener != null) {
+				listener.pointUsed(px,py);
+			}
+
+			// if the line between the current point and the next one intersect the
+			// other shape work out where on the other shape and start traversing it's 
+			// path instead
+			Line line = getLine(current, px, py, rationalPoint(current, point+dir));
+			HitResult hit = intersect(other, line);
+			
+			if (hit != null) {
+				Line hitLine = hit.line;
+				Vector2f pt = hit.pt;
+				px = pt.x;
+				py = pt.y;
+				
+				if (listener != null) {
+					listener.pointIntersected(px,py);
+				}
+				
+				if (other.hasVertex(px, py)) {
+					point = other.indexOf(pt.x,pt.y);
+					dir = 1;
+					px = pt.x;
+					py = pt.y;
+					
+					Shape temp = current;
+					current = other;
+					other = temp;
+					continue;
+				}
+				
+				float dx = hitLine.getDX() / hitLine.length();
+				float dy = hitLine.getDY() / hitLine.length();
+				dx *= EDGE_SCALE;
+				dy *= EDGE_SCALE;
+				
+				if (current.contains(pt.x + dx, pt.y + dy)) {
+					// the point is the next one, we need to take the first and traverse
+					// the path backwards
+					if (subtract) {
+						if (current == missing) {
+							point = hit.p2;
+							dir = -1;
+						} else {
+							point = hit.p1;
+							dir = 1;
+						}
+					} else {
+						if (current == target) {
+							point = hit.p2;
+							dir = -1;
+						} else {
+							point = hit.p2;
+							dir = -1;
+						}
+					}
+					
+					// swap the shapes over, we'll traverse the other one 
+					Shape temp = current;
+					current = other;
+					other = temp;
+				} else if (current.contains(pt.x - dx, pt.y - dy)) {
+					if (subtract) {
+						if (current == target) {
+							point = hit.p2;
+							dir = -1;
+						} else {
+							point = hit.p1;
+							dir = 1;
+						}
+					} else {
+						if (current == missing) {
+							point = hit.p1;
+							dir = 1;
+						} else {
+							point = hit.p1;
+							dir = 1;
+						}
+					}
+					
+					// swap the shapes over, we'll traverse the other one 
+					Shape temp = current;
+					current = other;
+					other = temp;
+				} else {
+					// give up
+					if (subtract) {
+						break;
+					} else {
+						point = hit.p1;
+						dir = 1;
+						Shape temp = current;
+						current = other;
+						other = temp;
+						
+						point = rationalPoint(current, point+dir);
+						px = current.getPoint(point)[0];
+						py = current.getPoint(point)[1];
+					}
+				}
+			} else {
+				// otherwise just move to the next point in the current shape
+				point = rationalPoint(current, point+dir);
+				px = current.getPoint(point)[0];
+				py = current.getPoint(point)[1];
+			}
+		}
+
+		poly.addPoint(px,py);
+		if (listener != null) {
+			listener.pointUsed(px,py);
+		}
+		
+		return poly;
+	}
+	
+	/**
+	 * Intersect a line with a shape
+	 * 
+	 * @param shape The shape to compare
+	 * @param line The line to intersect against the shape
+	 * @return The result describing the intersection or null if none
+	 */
+	public HitResult intersect(Shape shape, Line line) {
+		float distance = Float.MAX_VALUE;
+		HitResult hit = null;
+		
+		for (int i=0;i EPSILON)) {
+					hit = new HitResult();
+					hit.pt = pt;
+					hit.line = local;
+					hit.p1 = i;
+					hit.p2 = next;
+					distance = newDis;
+				}
+			}
+		}
+		
+		return hit;
+	}
+	
+	/**
+	 * Rationalise a point in terms of a given shape
+	 * 
+	 * @param shape The shape 
+	 * @param p The index of the point
+	 * @return The index that is rational for the shape
+	 */
+	public static int rationalPoint(Shape shape, int p) {
+		while (p < 0) {
+			p += shape.getPointCount();
+		}
+		while (p >= shape.getPointCount()) {
+			p -=  shape.getPointCount();
+		}
+		
+		return p;
+	}
+	
+	/**
+	 * Get a line between two points in a shape
+	 * 
+	 * @param shape The shape
+	 * @param s The index of the start point
+	 * @param e The index of the end point
+	 * @return The line between the two points
+	 */
+	public Line getLine(Shape shape, int s, int e) {
+		float[] start = shape.getPoint(s);
+		float[] end = shape.getPoint(e);
+		
+		Line line = new Line(start[0],start[1],end[0],end[1]);
+		return line;
+	}
+
+	/**
+	 * Get a line between two points in a shape
+	 * 
+	 * @param shape The shape
+	 * @param sx The x coordinate of the start point
+	 * @param sy The y coordinate of the start point
+	 * @param e The index of the end point
+	 * @return The line between the two points
+	 */
+	public Line getLine(Shape shape, float sx, float sy, int e) {
+		float[] end = shape.getPoint(e);
+		
+		Line line = new Line(sx,sy,end[0],end[1]);
+		return line;
+	}
+	
+	/**
+	 * A lightweigtht description of a intersection between a shape and 
+	 * line.
+	 */
+	public class HitResult {
+		/** The line on the target shape that intersected */
+		public Line line;
+		/** The index of the first point on the target shape that forms the line */
+		public int p1;
+		/** The index of the second point on the target shape that forms the line */
+		public int p2;
+		/** The position of the intersection */
+		public Vector2f pt;
+	}
+}
diff --git a/ext/slick/src/org/newdawn/slick/geom/GeomUtilListener.java b/ext/slick/src/org/newdawn/slick/geom/GeomUtilListener.java
new file mode 100644
index 0000000..a054873
--- /dev/null
+++ b/ext/slick/src/org/newdawn/slick/geom/GeomUtilListener.java
@@ -0,0 +1,32 @@
+package org.newdawn.slick.geom;
+
+/**
+ * Debug listener for notifications assocaited with geometry utilities
+ * 
+ * @author kevin
+ */
+public interface GeomUtilListener {
+	/**
+	 * Notification that a point was excluded from geometry 
+	 * 
+	 * @param x The x coordinate of the point
+	 * @param y The y coordinate of the point
+	 */
+	public void pointExcluded(float x, float y);
+
+	/**
+	 * Notification that a point was intersected between two geometries 
+	 * 
+	 * @param x The x coordinate of the point
+	 * @param y The y coordinate of the point
+	 */
+	public void pointIntersected(float x, float y);
+
+	/**
+	 * Notification that a point was used to build a new geometry
+	 * 
+	 * @param x The x coordinate of the point
+	 * @param y The y coordinate of the point
+	 */
+	public void pointUsed(float x, float y);
+}
diff --git a/ext/slick/src/org/newdawn/slick/geom/Line.java b/ext/slick/src/org/newdawn/slick/geom/Line.java
new file mode 100644
index 0000000..c3c6abf
--- /dev/null
+++ b/ext/slick/src/org/newdawn/slick/geom/Line.java
@@ -0,0 +1,408 @@
+package org.newdawn.slick.geom;
+
+/**
+ * Implemenation of a bunch of maths functions to do with lines. Note
+ * that lines can't be used as dynamic shapes right now - also collision 
+ * with the end of a line is undefined.
+ * 
+ * @author Kevin Glass
+ */
+public class Line extends Shape {
+	/** The start point of the line */
+	private Vector2f start;
+	/** The end point of the line */
+	private Vector2f end;
+	/** The vector between the two points */
+	private Vector2f vec;
+	/** The length of the line squared */
+	private float lenSquared;
+	
+	/** Temporary storage - declared globally to reduce GC */
+	private Vector2f loc = new Vector2f(0,0);
+	/** Temporary storage - declared globally to reduce GC */
+	private Vector2f v = new Vector2f(0,0);
+	/** Temporary storage - declared globally to reduce GC */
+	private Vector2f v2 = new Vector2f(0,0);
+	/** Temporary storage - declared globally to reduce GC */
+	private Vector2f proj = new Vector2f(0,0);
+
+	/** Temporary storage - declared globally to reduce GC */
+	private Vector2f closest = new Vector2f(0,0);
+	/** Temporary storage - declared globally to reduce GC */
+	private Vector2f other = new Vector2f(0,0);
+
+	/** True if this line blocks on the outer edge */
+	private boolean outerEdge = true;
+	/** True if this line blocks on the inner edge */
+	private boolean innerEdge = true;
+	
+	/**
+	 * Create a new line based on the origin and a single point
+	 * 
+	 * @param x The end point of the line
+	 * @param y The end point of the line
+	 * @param inner True if this line blocks on it's inner edge
+	 * @param outer True if this line blocks on it's outer edge
+	 */
+	public Line(float x, float y, boolean inner, boolean outer) {
+		this(0,0,x,y);
+	}
+
+	/**
+	 * Create a new line based on the origin and a single point
+	 * 
+	 * @param x The end point of the line
+	 * @param y The end point of the line
+	 */
+	public Line(float x, float y) {
+		this(x,y,true,true);
+	}
+	
+	/**
+	 * Create a new line based on two points
+	 * 
+	 * @param x1 The x coordinate of the start point
+	 * @param y1 The y coordinate of the start point
+	 * @param x2 The x coordinate of the end point
+	 * @param y2 The y coordinate of the end point
+	 */
+	public Line(float x1, float y1, float x2, float y2) {
+		this(new Vector2f(x1,y1), new Vector2f(x2,y2));
+	}
+
+	/**
+	 * Create a line with relative second point
+	 * 
+	 * @param x1 The x coordinate of the start point
+	 * @param y1 The y coordinate of the start point
+	 * @param dx The x change to get to the second point
+	 * @param dy The y change to get to the second point
+	 * @param dummy A dummy value
+	 */
+	public Line(float x1, float y1, float dx, float dy, boolean dummy) {
+		this(new Vector2f(x1,y1), new Vector2f(x1+dx,y1+dy));
+	}
+	
+	/**
+	 * Create a new line based on two points
+	 * 
+	 * @param start The start point
+	 * @param end The end point
+	 */
+	public Line(Vector2f start, Vector2f end) {
+		super();
+		
+		set(start,end);
+	}
+	
+	/**
+	 * Get the start point of the line
+	 * 
+	 * @return The start point of the line
+	 */
+	public Vector2f getStart() {
+		return start;
+	}
+
+	/**
+	 * Get the end point of the line
+	 * 
+	 * @return The end point of the line
+	 */
+	public Vector2f getEnd() {
+		return end;
+	}
+	
+	/**
+	 * Find the length of the line
+	 * 
+	 * @return The the length of the line
+	 */
+	public float length() {
+		return vec.length();
+	}
+	
+	/**
+	 * Find the length of the line squared (cheaper and good for comparisons)
+	 * 
+	 * @return The length of the line squared
+	 */
+	public float lengthSquared() {
+		return vec.lengthSquared();
+	}
+	
+	/**
+	 * Configure the line
+	 * 
+	 * @param start The start point of the line
+	 * @param end The end point of the line
+	 */
+	public void set(Vector2f start, Vector2f end) {
+		super.pointsDirty = true;
+		if (this.start == null) {
+			this.start = new Vector2f();
+		}
+		this.start.set(start);
+		
+		if (this.end == null) {
+			this.end = new Vector2f();
+		}
+		this.end.set(end);;
+		
+		vec = new Vector2f(end);
+		vec.sub(start);
+		
+		lenSquared = vec.lengthSquared();
+	}
+	
+	/**
+	 * Configure the line without garbage
+	 *  
+	 * @param sx The x coordinate of the start
+	 * @param sy The y coordinate of the start
+	 * @param ex The x coordiante of the end
+	 * @param ey The y coordinate of the end
+	 */
+	public void set(float sx, float sy, float ex, float ey) {
+		super.pointsDirty = true;
+		start.set(sx, sy);
+		end.set(ex,ey);
+		
+		float dx = (ex-sx);
+		float dy = (ey-sy);
+		
+		lenSquared = (dx*dx)+(dy*dy);
+	}
+	
+	/**
+	 * Get the x direction of this line
+	 * 
+	 * @return The x direction of this line
+	 */
+	public float getDX() {
+		return end.getX() - start.getX();
+	}
+
+	/**
+	 * Get the y direction of this line
+	 * 
+	 * @return The y direction of this line
+	 */
+	public float getDY() {
+		return end.getY() - start.getY();
+	}
+
+	/**
+	 * @see org.newdawn.slick.geom.Shape#getX()
+	 */
+	public float getX() {
+		return getX1();
+	}
+
+	/**
+	 * @see org.newdawn.slick.geom.Shape#getY()
+	 */
+	public float getY() {
+		return getY1();
+	}
+	
+	/**
+	 * Get the x coordinate of the start point
+	 * 
+	 * @return The x coordinate of the start point
+	 */
+	public float getX1() {
+		return start.getX();
+	}
+
+	/**
+	 * Get the y coordinate of the start point
+	 * 
+	 * @return The y coordinate of the start point
+	 */
+	public float getY1() {
+		return start.getY();
+	}
+
+	/**
+	 * Get the x coordinate of the end point
+	 * 
+	 * @return The x coordinate of the end point
+	 */
+	public float getX2() {
+		return end.getX();
+	}
+
+	/**
+	 * Get the y coordinate of the end point
+	 * 
+	 * @return The y coordinate of the end point
+	 */
+	public float getY2() {
+		return end.getY();
+	}
+	
+	/**
+	 * Get the shortest distance from a point to this line
+	 * 
+	 * @param point The point from which we want the distance
+	 * @return The distance from the line to the point
+	 */
+	public float distance(Vector2f point) {
+		return (float) Math.sqrt(distanceSquared(point));
+	}
+	
+	/**
+	 * Check if the given point is on the line
+	 * 
+	 * @param point The point to check
+	 * @return True if the point is on this line
+	 */
+	public boolean on(Vector2f point) {
+		getClosestPoint(point, closest);
+		
+		return point.equals(closest);
+	}
+	
+	/** 
+	 * Get the shortest distance squared from a point to this line
+	 * 
+	 * @param point The point from which we want the distance
+	 * @return The distance squared from the line to the point
+	 */
+	public float distanceSquared(Vector2f point) {
+		getClosestPoint(point, closest);
+		closest.sub(point);
+		
+		float result = closest.lengthSquared();
+		
+		return result;
+	}
+	
+	/**
+	 * Get the closest point on the line to a given point
+	 * 
+	 * @param point The point which we want to project
+	 * @param result The point on the line closest to the given point
+	 */
+	public void getClosestPoint(Vector2f point, Vector2f result) {
+		loc.set(point);
+		loc.sub(start);
+
+		float projDistance = vec.dot(loc);
+
+		projDistance /= vec.lengthSquared();
+
+		if (projDistance < 0) {
+			result.set(start);
+			return;
+		}
+		if (projDistance > 1) {
+			result.set(end);
+			return;
+		}
+
+		result.x = start.getX() + projDistance * vec.getX();
+		result.y = start.getY() + projDistance * vec.getY();
+	}
+
+	/**
+	 * @see java.lang.Object#toString()
+	 */
+	public String toString() {
+		return "[Line "+start+","+end+"]";
+	}
+
+	/**
+	 * Intersect this line with another
+	 * 
+	 * @param other The other line we should intersect with
+	 * @return The intersection point or null if the lines are parallel
+	 */
+	public Vector2f intersect(Line other) {
+		return intersect(other, false);
+	}
+
+	/**
+	 * Intersect this line with another
+	 * 
+	 * @param other The other line we should intersect with
+	 * @param limit True if the collision is limited to the extent of the lines
+	 * @return The intersection point or null if the lines don't intersect
+	 */
+	public Vector2f intersect(Line other, boolean limit) {
+		Vector2f temp = new Vector2f();
+		
+		if (!intersect(other, limit, temp)) {
+			return null;
+		}
+		
+		return temp;
+	}
+	
+	/**
+	 * Intersect this line with another
+	 * 
+	 * @param other The other line we should intersect with
+	 * @param limit True if the collision is limited to the extent of the lines
+	 * @param result The resulting intersection point if any
+	 * @return True if the lines intersect
+	 */
+	public boolean intersect(Line other, boolean limit, Vector2f result) {
+		float dx1 = end.getX() - start.getX();
+		float dx2 = other.end.getX() - other.start.getX();
+		float dy1 = end.getY() - start.getY();
+		float dy2 = other.end.getY() - other.start.getY();
+		float denom = (dy2 * dx1) - (dx2 * dy1);
+		
+		if (denom == 0) {
+			return false;
+		}
+		
+		float ua = (dx2 * (start.getY() - other.start.getY())) - (dy2 * (start.getX() - other.start.getX()));
+		ua /= denom;
+		float ub = (dx1 * (start.getY() - other.start.getY())) - (dy1 * (start.getX() - other.start.getX()));
+		ub /= denom;
+		
+		if ((limit) && ((ua < 0) || (ua > 1) || (ub < 0) || (ub > 1))) {
+			return false;			
+		}
+		
+		float u = ua;
+		
+		float ix = start.getX() + (u * (end.getX() - start.getX()));
+		float iy = start.getY() + (u * (end.getY() - start.getY()));
+		
+		result.set(ix,iy);
+		return true;
+	}
+
+	/**
+	 * @see org.newdawn.slick.geom.Shape#createPoints()
+	 */
+	protected void createPoints() {
+		points = new float[4];
+		points[0] = getX1();
+		points[1] = getY1();
+		points[2] = getX2();
+		points[3] = getY2();
+	}
+
+	/**
+	 * @see org.newdawn.slick.geom.Shape#transform(org.newdawn.slick.geom.Transform)
+	 */
+	public Shape transform(Transform transform) {
+		float[] temp = new float[4];
+		createPoints();
+		transform.transform(points, 0, temp, 0, 2);
+		
+		return new Line(temp[0],temp[1],temp[2],temp[3]);
+	}
+	
+	/**
+	 * @see org.newdawn.slick.geom.Shape#closed()
+	 */
+	public boolean closed() {
+		return false;
+	}
+}
diff --git a/ext/slick/src/org/newdawn/slick/geom/MannTriangulator.java b/ext/slick/src/org/newdawn/slick/geom/MannTriangulator.java
new file mode 100644
index 0000000..85c3f3e
--- /dev/null
+++ b/ext/slick/src/org/newdawn/slick/geom/MannTriangulator.java
@@ -0,0 +1,616 @@
+/*
+ * Triangulator0.java
+ *
+ * (BSD license)
+ *
+ * Copyright (c) 2005, Matthias Mann (www.matthiasmann.de)
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ *   * Neither the name of the Matthias Mann nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Created on 17. March 2005, 22:19
+ */
+package org.newdawn.slick.geom;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A 2D Triangulator. Graciously made available by the man(n) himself.
+ * 
+ * @author Matthias Mann
+ */
+public class MannTriangulator implements Triangulator {
+	/** The allowed error value */
+	private static final double EPSILON = 1e-5;
+	
+	/** The outer countour of the shape */
+	protected PointBag contour;
+	/** The holes defined in the polygon */
+	protected PointBag holes;
+	/** The next available point bag */
+	private PointBag nextFreePointBag;
+	/** The next available point */
+	private Point nextFreePoint;
+	/** The list of triangles created (or rather points in triangles, 3xn) */
+	private List triangles = new ArrayList();
+	
+	/** Creates a new instance of Triangulator0 */
+	public MannTriangulator() {
+		contour = getPointBag();
+	}
+	
+	/**
+	 * @see org.newdawn.slick.geom.Triangulator#addPolyPoint(float, float)
+	 */
+	public void addPolyPoint(float x, float y) {
+    	addPoint(new Vector2f(x,y));
+    }
+
+	/**
+	 * Reset the internal state of the triangulator
+	 */
+	public void reset() {
+		while (holes != null) {
+			holes = freePointBag(holes);
+		}
+
+		contour.clear();
+		holes = null;
+	}
+
+	/**
+	 * Begin adding a hole to the polygon
+	 */
+	public void startHole() {
+		PointBag newHole = getPointBag();
+		newHole.next = holes;
+		holes = newHole;
+	}
+
+	/**
+	 * Add a defined point to the current contour
+	 * 
+	 * @param pt The point to add
+	 */
+	private void addPoint(Vector2f pt) {
+		if (holes == null) {
+			Point p = getPoint(pt);
+			contour.add(p);
+		} else {
+			Point p = getPoint(pt);
+			holes.add(p);
+		}
+	}
+
+	/**
+	 * Triangulate the points given
+	 * 
+	 * @param result The array to fill with the result or use to determine type
+	 * @return The resultng triangles
+	 */
+	private Vector2f[] triangulate(Vector2f[] result) {
+		// Step 1: Compute all angles
+		contour.computeAngles();
+		for (PointBag hole = holes; hole != null; hole = hole.next) {
+			hole.computeAngles();
+		}
+
+		// Step 2: Connect the holes with the contour (build bridges)
+		while (holes != null) {
+			Point pHole = holes.first;
+			outer: do {
+				if (pHole.angle <= 0) {
+					Point pContour = contour.first;
+					do {
+						inner: if (pHole.isInfront(pContour)
+								&& pContour.isInfront(pHole)) {
+							if (!contour.doesIntersectSegment(pHole.pt,
+									pContour.pt)) {
+								PointBag hole = holes;
+								do {
+									if (hole.doesIntersectSegment(pHole.pt,
+											pContour.pt)) {
+										break inner;
+									}
+								} while ((hole = hole.next) != null);
+
+								Point newPtContour = getPoint(pContour.pt);
+								pContour.insertAfter(newPtContour);
+
+								Point newPtHole = getPoint(pHole.pt);
+								pHole.insertBefore(newPtHole);
+
+								pContour.next = pHole;
+								pHole.prev = pContour;
+
+								newPtHole.next = newPtContour;
+								newPtContour.prev = newPtHole;
+
+								pContour.computeAngle();
+								pHole.computeAngle();
+								newPtContour.computeAngle();
+								newPtHole.computeAngle();
+
+								// detach the points from the hole
+								holes.first = null;
+								break outer;
+							}
+						}
+					} while ((pContour = pContour.next) != contour.first);
+				}
+			} while ((pHole = pHole.next) != holes.first);
+
+			// free the hole
+			holes = freePointBag(holes);
+		}
+
+		// Step 3: Make sure we have enough space for the result
+		int numTriangles = contour.countPoints() - 2;
+		int neededSpace = numTriangles * 3 + 1; // for the null
+		if (result.length < neededSpace) {
+			result = (Vector2f[]) Array.newInstance(result.getClass()
+					.getComponentType(), neededSpace);
+		}
+
+		// Step 4: Extract the triangles
+		int idx = 0;
+		for (;;) {
+			Point pContour = contour.first;
+
+			if (pContour == null) {
+				break;
+			}
+			// Are there 2 or less points left ?
+			if (pContour.next == pContour.prev) {
+				break;
+			}
+
+			outer: do {
+				if (pContour.angle > 0) {
+					Point prev = pContour.prev;
+					Point next = pContour.next;
+
+					if (next.next == prev || prev.isInfront(next) && next.isInfront(prev)) {
+						if (!contour.doesIntersectSegment(prev.pt, next.pt)) {
+							result[idx++] = pContour.pt;
+							result[idx++] = next.pt;
+							result[idx++] = prev.pt;
+							break outer;
+						}
+					}
+				}
+			} while ((pContour = pContour.next) != contour.first);
+			
+			// remove the point - we do it in every case to prevent endless loop
+			Point prev = pContour.prev;
+			Point next = pContour.next;
+
+			contour.first = prev;
+			pContour.unlink();
+			freePoint(pContour);
+
+			next.computeAngle();
+			prev.computeAngle();
+		}
+
+		// Step 5: Append a null (see Collection.toArray)
+		result[idx] = null;
+
+		// Step 6: Free memory
+		contour.clear();
+
+		// Finished !
+		return result;
+	}
+
+	/**
+	 * Create a new point bag (or recycle an old one)
+	 * 
+	 * @return The new point bag
+	 */
+	private PointBag getPointBag() {
+		PointBag pb = nextFreePointBag;
+		if (pb != null) {
+			nextFreePointBag = pb.next;
+			pb.next = null;
+			return pb;
+		}
+		return new PointBag();
+	}
+
+	/**
+	 * Release a pooled bag
+	 * 
+	 * @param pb The bag to release
+	 * @return The next available bag
+	 */
+	private PointBag freePointBag(PointBag pb) {
+		PointBag next = pb.next;
+		pb.clear();
+		pb.next = nextFreePointBag;
+		nextFreePointBag = pb;
+		return next;
+	}
+
+	/**
+	 * Create or reuse a point
+	 * 
+	 * @param pt The point data to set
+	 * @return The new point
+	 */
+	private Point getPoint(Vector2f pt) {
+		Point p = nextFreePoint;
+		if (p != null) {
+			nextFreePoint = p.next;
+			// initialize new point to safe values
+			p.next = null;
+			p.prev = null;
+			p.pt = pt;
+			return p;
+		}
+		return new Point(pt);
+	}
+
+	/**
+	 * Release a point into the pool
+	 * 
+	 * @param p The point to release
+	 */
+	private void freePoint(Point p) {
+		p.next = nextFreePoint;
+		nextFreePoint = p;
+	}
+
+	/**
+	 * Release all points
+	 * 
+	 * @param head The head of the points bag
+	 */
+	private void freePoints(Point head) {
+		head.prev.next = nextFreePoint;
+		head.prev = null;
+		nextFreePoint = head;
+	}
+
+	/**
+	 * A single point being considered during triangulation
+	 *
+	 * @author Matthias Mann
+	 */
+	private static class Point {
+		/** The location of the point */
+		protected Vector2f pt;
+		/** The previous point in the contour */
+		protected Point prev;
+		/** The next point in the contour */
+		protected Point next;
+		/** The x component of the of the normal */
+		protected double nx;
+		/** The y component of the of the normal */
+		protected double ny;
+		/** The angle at this point in the path */
+		protected double angle;
+		/** The distance of this point from */
+		protected double dist;
+
+		/**
+		 * Create a new point
+		 * 
+		 * @param pt The points location
+		 */
+		public Point(Vector2f pt) {
+			this.pt = pt;
+		}
+
+		/**
+		 * Remove this point from it's contour
+		 */
+		public void unlink() {
+			prev.next = next;
+			next.prev = prev;
+			next = null;
+			prev = null;
+		}
+
+		/**
+		 * Insert a point before this one (see LinkedList)
+		 * 
+		 * @param p The point to insert
+		 */
+		public void insertBefore(Point p) {
+			prev.next = p;
+			p.prev = prev;
+			p.next = this;
+			prev = p;
+		}
+
+		/**
+		 * Insert a point after this one (see LinkedList)
+		 * 
+		 * @param p The point to insert
+		 */
+		public void insertAfter(Point p) {
+			next.prev = p;
+			p.prev = this;
+			p.next = next;
+			next = p;
+		}
+
+		/**
+		 * Java 5 hypot method
+		 * 
+		 * @param x The x component
+		 * @param y The y component
+		 * @return The hypotenuse
+		 */
+		private double hypot(double x, double y) {
+			return Math.sqrt(x*x + y*y);
+		}
+		
+		/**
+		 * Compute the angle at this point
+		 */
+		public void computeAngle() {
+			if (prev.pt.equals(pt)) {
+				pt.x += 0.01f;
+			}
+			double dx1 = pt.x - prev.pt.x;
+			double dy1 = pt.y - prev.pt.y;
+			double len1 = hypot(dx1, dy1);
+			dx1 /= len1;
+			dy1 /= len1;
+
+			if (next.pt.equals(pt)) {
+				pt.y += 0.01f;
+			}
+			double dx2 = next.pt.x - pt.x;
+			double dy2 = next.pt.y - pt.y;
+			double len2 = hypot(dx2, dy2);
+			dx2 /= len2;
+			dy2 /= len2;
+
+			double nx1 = -dy1;
+			double ny1 = dx1;
+
+			nx = (nx1 - dy2) * 0.5;
+			ny = (ny1 + dx2) * 0.5;
+
+			if (nx * nx + ny * ny < EPSILON) {
+				nx = dx1;
+				ny = dy2;
+				angle = 1; // TODO: nx1,ny1 and nx2,ny2 facing ?
+				if (dx1 * dx2 + dy1 * dy2 > 0) {
+					nx = -dx1;
+					ny = -dy1;
+				}
+			} else {
+				angle = nx * dx2 + ny * dy2;
+			}
+		}
+
+		/**
+		 * Get the angle of this point to another
+		 * 
+		 * @param p The other point
+		 * @return The angle between this point and another
+		 */
+		public double getAngle(Point p) {
+			double dx = p.pt.x - pt.x;
+			double dy = p.pt.y - pt.y;
+			double dlen = hypot(dx, dy);
+
+			return (nx * dx + ny * dy) / dlen;
+		}
+
+		/**
+		 * Check if this point is convave
+		 * 
+		 * @return True if this point remains concave
+		 */
+		public boolean isConcave() {
+			return angle < 0;
+		}
+
+		/**
+		 * Check if this point is infront of another
+		 * 
+		 * @param dx The other x
+		 * @param dy The other y
+		 * @return True if this point is infront (in the contour)
+		 */
+		public boolean isInfront(double dx, double dy) {
+			// no nead to normalize, amplitude does not metter for side
+			// detection
+			boolean sidePrev = ((prev.pt.y - pt.y) * dx + (pt.x - prev.pt.x)
+					* dy) >= 0;
+			boolean sideNext = ((pt.y - next.pt.y) * dx + (next.pt.x - pt.x)
+					* dy) >= 0;
+
+			return (angle < 0) ? (sidePrev | sideNext) : (sidePrev & sideNext);
+		}
+
+		/**
+		 * Check if this point is infront of another
+		 * 
+		 * @param p The other point
+		 * @return True if this point is infront (in the contour)
+		 */
+		public boolean isInfront(Point p) {
+			return isInfront(p.pt.x - pt.x, p.pt.y - pt.y);
+		}
+	}
+
+	/**
+	 * A bag/pool of point objects
+	 *
+	 * @author kevin
+	 */
+	protected class PointBag {
+		/** The first point in the bag - head of the list */
+		protected Point first;
+		/** The next bag in the list of bags */
+		protected PointBag next;
+
+		/**
+		 * Clear all the points from this bag
+		 */
+		public void clear() {
+			if (first != null) {
+				freePoints(first);
+				first = null;
+			}
+		}
+
+		/**
+		 * Add a point to the bag
+		 * 
+		 * @param p The point to add
+		 */
+		public void add(Point p) {
+			if (first != null) {
+				first.insertBefore(p);
+			} else {
+				first = p;
+				p.next = p;
+				p.prev = p;
+			}
+		}
+
+		/**
+		 * Compute the angles for the points in this bag
+		 */
+		public void computeAngles() {
+			if (first == null) {
+				return;
+			}
+
+			Point p = first;
+			do {
+				p.computeAngle();
+			} while ((p = p.next) != first);
+		}
+
+		/**
+		 * Check if the points in this bag form a path intersecting
+		 * with the specified path
+		 * 
+		 * @param v1 The start point of the segment
+		 * @param v2 The end point of the segment
+		 * @return True if points in this contour intersect with the segment
+		 */
+		public boolean doesIntersectSegment(Vector2f v1, Vector2f v2) {
+			double dxA = v2.x - v1.x;
+			double dyA = v2.y - v1.y;
+
+			for (Point p = first;;) {
+				Point n = p.next;
+				if (p.pt != v1 && n.pt != v1 && p.pt != v2 && n.pt != v2) {
+					double dxB = n.pt.x - p.pt.x;
+					double dyB = n.pt.y - p.pt.y;
+					double d = (dxA * dyB) - (dyA * dxB);
+
+					if (Math.abs(d) > EPSILON) {
+						double tmp1 = p.pt.x - v1.x;
+						double tmp2 = p.pt.y - v1.y;
+						double tA = (dyB * tmp1 - dxB * tmp2) / d;
+						double tB = (dyA * tmp1 - dxA * tmp2) / d;
+
+						if (tA >= 0 && tA <= 1 && tB >= 0 && tB <= 1) {
+							return true;
+						}
+					}
+				}
+
+				if (n == first) {
+					return false;
+				}
+				p = n;
+			}
+		}
+
+		/**
+		 * Get the number of points in the bag 
+		 * 
+		 * @return The number of points in the bag
+		 */
+		public int countPoints() {
+			if (first == null) {
+				return 0;
+			}
+
+			int count = 0;
+			Point p = first;
+			do {
+				++count;
+			} while ((p = p.next) != first);
+			return count;
+		}
+		
+		/**
+		 * Check if the point provided was contained
+		 * 
+		 * @param point The point provided
+		 * @return True if it's in the bag
+		 */
+		public boolean contains(Vector2f point) {
+			if (first == null) {
+				return false;
+			}
+			
+			if (first.prev.pt.equals(point)) {
+				return true;
+			}
+			if (first.pt.equals(point)) {
+				return true;
+			}
+			return false;
+		}
+	}
+
+	public boolean triangulate() {
+		Vector2f[] temp = triangulate(new Vector2f[0]);
+
+		for (int i = 0; i < temp.length; i++) {
+			if (temp[i] == null) {
+				break;
+			} else {
+				triangles.add(temp[i]);
+			}
+		}
+
+		return true;
+	}
+
+	/**
+	 * @see org.newdawn.slick.geom.Triangulator#getTriangleCount()
+	 */
+	public int getTriangleCount() {
+		return triangles.size() / 3;
+	}
+
+	/**
+	 * @see org.newdawn.slick.geom.Triangulator#getTrianglePoint(int, int)
+	 */
+	public float[] getTrianglePoint(int tri, int i) {
+		Vector2f pt = (Vector2f) triangles.get((tri * 3) + i);
+
+		return new float[] { pt.x, pt.y };
+	}
+
+}
diff --git a/ext/slick/src/org/newdawn/slick/geom/MorphShape.java b/ext/slick/src/org/newdawn/slick/geom/MorphShape.java
new file mode 100644
index 0000000..52b2d29
--- /dev/null
+++ b/ext/slick/src/org/newdawn/slick/geom/MorphShape.java
@@ -0,0 +1,193 @@
+package org.newdawn.slick.geom;
+
+import java.util.ArrayList;
+
+/**
+ * A shape that morphs between a set of other shapes
+ *  
+ * @author kevin
+ */
+public class MorphShape extends Shape {
+	/** The shapes to morph between */
+	private ArrayList shapes = new ArrayList();
+	/** The offset between the shapes */
+	private float offset;
+	
+	/** The current shape */
+	private Shape current;
+	/** The next shape */
+	private Shape next;
+	
+	/**
+	 * Create a new mighty morphin shape
+	 * 
+	 * @param base The base shape we're starting the morph from
+	 */
+	public MorphShape(Shape base) {
+		shapes.add(base);
+		float[] copy = base.points;
+		this.points = new float[copy.length];
+		
+		current = base;
+		next = base;
+	}
+
+	/**
+	 * Add a subsequent shape that we should morph too in order
+	 * 
+	 * @param shape The new shape that forms part of the morphing shape
+	 */
+	public void addShape(Shape shape) {
+		if (shape.points.length != points.length) {
+			throw new RuntimeException("Attempt to morph between two shapes with different vertex counts");
+		}
+		
+		Shape prev = (Shape) shapes.get(shapes.size()-1);
+		if (equalShapes(prev, shape)) {
+			shapes.add(prev);
+		} else {
+			shapes.add(shape);
+		}
+		
+		if (shapes.size() == 2) {
+			next = (Shape) shapes.get(1);
+		}
+	}
+	
+	/**
+	 * Check if the shape's points are all equal
+	 * 
+	 * @param a The first shape to compare
+ 	 * @param b The second shape to compare
+	 * @return True if the shapes are equal
+	 */
+	private boolean equalShapes(Shape a, Shape b) {
+		a.checkPoints();
+		b.checkPoints();
+		
+		for (int i=0;i 1) {
+			int index = shapes.indexOf(next);
+			if (index < 1) {
+				index = 0;
+			}
+			
+			int nframe = rational(index+1);
+			setFrame(index, nframe, offset);
+			offset -= 1;
+		} else {
+			pointsDirty = true;
+		}
+	}
+	
+	/**
+	 * Set the current frame
+	 * 
+	 * @param current The current frame
+	 */
+	public void setExternalFrame(Shape current) {
+		this.current = current;
+		next = (Shape) shapes.get(0);
+		offset = 0;
+	}
+	
+	/**
+	 * Get an index that is rational, i.e. fits inside this set of shapes
+	 * 
+	 * @param n The index to rationalize
+	 * @return The index rationalized
+	 */
+	private int rational(int n) {
+		while (n >= shapes.size()) {
+			n -= shapes.size();
+		}
+		while (n < 0) {
+			n += shapes.size();
+		}
+		
+		return n;
+	}
+	
+	/**
+	 * Set the frame to be represented 
+	 * 
+	 * @param a The index of the first shape
+	 * @param b The index of the second shape
+	 * @param offset The offset between the two shapes to represent
+	 */
+	private void setFrame(int a, int b, float offset) {
+		current = (Shape) shapes.get(a);
+		next = (Shape) shapes.get(b);
+		this.offset = offset;
+		pointsDirty = true;
+	}
+	
+	/**
+	 * @see MorphShape#createPoints()
+	 */
+	protected void createPoints() {
+		if (current == next) {
+			System.arraycopy(current.points,0,points,0,points.length);
+			return;
+		}
+		
+		float[] apoints = current.points;
+		float[] bpoints = next.points;
+		
+		for (int i=0;i (k = findEdge(i, j)))
+        {
+            throw new InternalException("Attempt to delete unknown edge");
+        } 
+        else
+        {
+            edges[k] = edges[--numEdges];
+            return;
+        }
+    }
+
+    /**
+     * Mark an edge as either a suspect or not
+     * 
+     * @param i The index of the first vert
+     * @param j The index of the second vert
+     * @param flag True if the edge is a suspect
+     * @throws InternalException Indicates the edge didn't exist
+     */
+    void markSuspect(int i, int j, boolean flag) throws InternalException
+    {
+        int k;
+        if(0 > (k = findEdge(i, j)))
+        {
+            throw new InternalException("Attempt to mark unknown edge");
+        } else
+        {
+            edges[k].suspect = flag;
+            return;
+        }
+    }
+
+    /**
+     * Choose the suspect to become part of the triangle
+     * 
+     * @return The edge selected
+     */
+    private Edge chooseSuspect()
+    {
+        for(int i = 0; i < numEdges; i++)
+        {
+            Edge edge = edges[i];
+            if(edge.suspect)
+            {
+                edge.suspect = false;
+                if(edge.t0 >= 0 && edge.t1 >= 0)
+                    return edge;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Factor rho.
+     * 
+     * @param f Factor 1
+     * @param f1 Factor 2
+     * @param f2 Factor 3
+     * @param f3 Factor 4
+     * @param f4 Factor 5
+     * @param f5 Factor 6
+     * @return The computation of rho
+     */
+    private static float rho(float f, float f1, float f2, float f3, float f4, float f5)
+    {
+        float f6 = f4 - f2;
+        float f7 = f5 - f3;
+        float f8 = f - f4;
+        float f9 = f1 - f5;
+        float f18 = f6 * f9 - f7 * f8;
+        if(f18 > 0.0F)
+        {
+            if(f18 < 1E-006F)
+                f18 = 1E-006F;
+            float f12 = f6 * f6;
+            float f13 = f7 * f7;
+            float f14 = f8 * f8;
+            float f15 = f9 * f9;
+            float f10 = f2 - f;
+            float f11 = f3 - f1;
+            float f16 = f10 * f10;
+            float f17 = f11 * f11;
+            return ((f12 + f13) * (f14 + f15) * (f16 + f17)) / (f18 * f18);
+        } else
+        {
+            return -1F;
+        }
+    }
+
+	/**
+	 * Check if the point P is inside the triangle defined by
+	 * the points A,B,C
+	 * 
+	 * @param f Point A x-coordinate
+	 * @param f1 Point A y-coordinate
+	 * @param f2 Point B x-coordinate
+	 * @param f3 Point B y-coordinate
+	 * @param f4 Point C x-coordinate
+	 * @param f5 Point C y-coordinate
+	 * @param f6 Point P x-coordinate
+	 * @param f7 Point P y-coordinate
+	 * @return True if the point specified is within the triangle
+	 */
+    private static boolean insideTriangle(float f, float f1, float f2, float f3, float f4, float f5, float f6, float f7)
+    {
+        float f8 = f4 - f2;
+        float f9 = f5 - f3;
+        float f10 = f - f4;
+        float f11 = f1 - f5;
+        float f12 = f2 - f;
+        float f13 = f3 - f1;
+        float f14 = f6 - f;
+        float f15 = f7 - f1;
+        float f16 = f6 - f2;
+        float f17 = f7 - f3;
+        float f18 = f6 - f4;
+        float f19 = f7 - f5;
+        float f22 = f8 * f17 - f9 * f16;
+        float f20 = f12 * f15 - f13 * f14;
+        float f21 = f10 * f19 - f11 * f18;
+        return f22 >= 0.0D && f21 >= 0.0D && f20 >= 0.0D;
+    }
+
+	/**
+	 * Cut a the contour and add a triangle into V to describe the 
+	 * location of the cut
+	 * 
+	 * @param i The index of the first point
+	 * @param j The index of the second point
+	 * @param k The index of the third point
+	 * @param l ?
+	 * @return True if a triangle was found
+	 */
+    private boolean snip(int i, int j, int k, int l)
+    {
+        float f = pointsX[V[i]];
+        float f1 = pointsY[V[i]];
+        float f2 = pointsX[V[j]];
+        float f3 = pointsY[V[j]];
+        float f4 = pointsX[V[k]];
+        float f5 = pointsY[V[k]];
+        if(1E-006F > (f2 - f) * (f5 - f1) - (f3 - f1) * (f4 - f))
+            return false;
+        for(int i1 = 0; i1 < l; i1++)
+            if(i1 != i && i1 != j && i1 != k)
+            {
+                float f6 = pointsX[V[i1]];
+                float f7 = pointsY[V[i1]];
+                if(insideTriangle(f, f1, f2, f3, f4, f5, f6, f7))
+                    return false;
+            }
+
+        return true;
+    }
+
+    /**
+     * Get the area defined by the points
+     * 
+     * @return The area defined by the points
+     */
+    private float area()
+    {
+        float f = 0.0F;
+        int i = numPoints - 1;
+        for(int j = 0; j < numPoints;)
+        {
+            f += pointsX[i] * pointsY[j] - pointsY[i] * pointsX[j];
+            i = j++;
+        }
+
+        return f * 0.5F;
+    }
+
+    /**
+     * Perform simple triangulation
+     * 
+     * @throws InternalException Indicates a polygon that can't be triangulated
+     */
+    public void basicTriangulation() throws InternalException
+    {
+        int i = numPoints;
+        if(i < 3)
+            return;
+        numEdges = 0;
+        numTriangles = 0;
+        V = new int[i];
+        
+        if(0.0D < area())
+        {
+            for(int k = 0; k < i; k++)
+                V[k] = k;
+
+        } else
+        {
+            for(int l = 0; l < i; l++)
+                V[l] = numPoints - 1 - l;
+
+        }
+        int k1 = 2 * i;
+        int i1 = i - 1;
+        while(i > 2) 
+        {
+            if(0 >= k1--) {
+                throw new InternalException("Bad polygon");
+            }
+            
+            int j = i1;
+            if(i <= j)
+                j = 0;
+            i1 = j + 1;
+            if(i <= i1)
+                i1 = 0;
+            int j1 = i1 + 1;
+            if(i <= j1)
+                j1 = 0;
+            if(snip(j, i1, j1, i))
+            {
+                int l1 = V[j];
+                int i2 = V[i1];
+                int j2 = V[j1];
+                if(numTriangles == triangles.length)
+                {
+                    Triangle atriangle[] = new Triangle[triangles.length * 2];
+                    System.arraycopy(triangles, 0, atriangle, 0, numTriangles);
+                    triangles = atriangle;
+                }
+                triangles[numTriangles] = new Triangle(l1, i2, j2);
+                addEdge(l1, i2, numTriangles);
+                addEdge(i2, j2, numTriangles);
+                addEdge(j2, l1, numTriangles);
+                numTriangles++;
+                int k2 = i1;
+                for(int l2 = i1 + 1; l2 < i; l2++)
+                {
+                    V[k2] = V[l2];
+                    k2++;
+                }
+
+                i--;
+                k1 = 2 * i;
+            }
+        }
+        V = null;
+    }
+
+    /**
+     * Optimize the triangulation by applying delauney rules
+     * 
+     * @throws InternalException Indicates an invalid polygon
+     */
+    private void optimize() throws InternalException
+    {
+        do
+        {
+            Edge edge;
+            if ((edge = chooseSuspect()) == null) {
+                break;
+            }
+            int i1 = edge.v0;
+            int k1 = edge.v1;
+            int i = edge.t0;
+            int j = edge.t1;
+            int j1 = -1;
+            int l1 = -1;
+            for (int k = 0; k < 3; k++)
+            {
+                int i2 = triangles[i].v[k];
+                if(i1 == i2 || k1 == i2) {
+                    continue;
+                }
+                l1 = i2;
+                break;
+            }
+
+            for (int l = 0; l < 3; l++)
+            {
+                int j2 = triangles[j].v[l];
+                if(i1 == j2 || k1 == j2) {
+                    continue;
+                }
+                j1 = j2;
+                break;
+            }
+
+            if(-1 == j1 || -1 == l1) {
+                throw new InternalException("can't find quad");
+            }
+            
+            float f = pointsX[i1];
+            float f1 = pointsY[i1];
+            float f2 = pointsX[j1];
+            float f3 = pointsY[j1];
+            float f4 = pointsX[k1];
+            float f5 = pointsY[k1];
+            float f6 = pointsX[l1];
+            float f7 = pointsY[l1];
+            float f8 = rho(f, f1, f2, f3, f4, f5);
+            float f9 = rho(f, f1, f4, f5, f6, f7);
+            float f10 = rho(f2, f3, f4, f5, f6, f7);
+            float f11 = rho(f2, f3, f6, f7, f, f1);
+            if(0.0F > f8 || 0.0F > f9) {
+                throw new InternalException("original triangles backwards");
+            }
+            if(0.0F <= f10 && 0.0F <= f11)
+            {
+                if(f8 > f9) {
+                    f8 = f9;
+                }
+                if(f10 > f11) {
+                    f10 = f11;
+                }
+                if(f8 > f10) {
+                    deleteEdge(i1, k1);
+                    triangles[i].v[0] = j1;
+                    triangles[i].v[1] = k1;
+                    triangles[i].v[2] = l1;
+                    triangles[j].v[0] = j1;
+                    triangles[j].v[1] = l1;
+                    triangles[j].v[2] = i1;
+                    addEdge(j1, k1, i);
+                    addEdge(k1, l1, i);
+                    addEdge(l1, j1, i);
+                    addEdge(l1, i1, j);
+                    addEdge(i1, j1, j);
+                    addEdge(j1, l1, j);
+                    markSuspect(j1, l1, false);
+                }
+            }
+        } while(true);
+    }
+
+    /**
+     * Upate the triangles
+     */
+    public boolean triangulate()
+    {
+        try
+        {
+            basicTriangulation();
+            //optimize();
+            return true;
+        }
+        catch (InternalException e)
+        {
+            numEdges = 0;
+        }
+        return false;
+    }
+
+    /** 
+     * Add a point to the polygon
+     */
+    public void addPolyPoint(float x, float y)
+    {
+    	for (int i=0;i