├── assets ├── images │ ├── hero.png │ ├── enemy.png │ ├── potion.png │ ├── tileset.png │ ├── marker_move.png │ └── marker_stop.png ├── sounds │ ├── hit.wav │ ├── step.wav │ └── levelup.wav └── data │ └── map.csv ├── source ├── AssetPaths.hx ├── Potion.hx ├── MenuState.hx ├── HUD.hx ├── CombatWindow.hx ├── Main.hx ├── Enemy.hx └── PlayState.hx ├── README.md └── Project.xml /assets/images/hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keyreal-code/HaxeFlixel-RPG/HEAD/assets/images/hero.png -------------------------------------------------------------------------------- /assets/sounds/hit.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keyreal-code/HaxeFlixel-RPG/HEAD/assets/sounds/hit.wav -------------------------------------------------------------------------------- /assets/sounds/step.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keyreal-code/HaxeFlixel-RPG/HEAD/assets/sounds/step.wav -------------------------------------------------------------------------------- /assets/images/enemy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keyreal-code/HaxeFlixel-RPG/HEAD/assets/images/enemy.png -------------------------------------------------------------------------------- /assets/images/potion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keyreal-code/HaxeFlixel-RPG/HEAD/assets/images/potion.png -------------------------------------------------------------------------------- /assets/images/tileset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keyreal-code/HaxeFlixel-RPG/HEAD/assets/images/tileset.png -------------------------------------------------------------------------------- /assets/sounds/levelup.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keyreal-code/HaxeFlixel-RPG/HEAD/assets/sounds/levelup.wav -------------------------------------------------------------------------------- /assets/images/marker_move.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keyreal-code/HaxeFlixel-RPG/HEAD/assets/images/marker_move.png -------------------------------------------------------------------------------- /assets/images/marker_stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keyreal-code/HaxeFlixel-RPG/HEAD/assets/images/marker_stop.png -------------------------------------------------------------------------------- /source/AssetPaths.hx: -------------------------------------------------------------------------------- 1 | package ; 2 | 3 | @:build(flixel.system.FlxAssets.buildFileReferences("assets", true)) 4 | class AssetPaths {} -------------------------------------------------------------------------------- /source/Potion.hx: -------------------------------------------------------------------------------- 1 | package ; 2 | import flixel.FlxSprite; 3 | 4 | class Potion extends FlxSprite 5 | { 6 | 7 | public function new() 8 | { 9 | super(); 10 | loadGraphic("assets/images/potion.png", false, 16, 16); 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | HaxeFlixel-RPG 2 | ============== 3 | 4 | An example RPG created using Haxe, OpenFL and HaxeFlixel. 5 | 6 | Read the full step-by-step tutorial series on my blog: http://www.haxecoder.com 7 | 8 | HaxeFlixel tutorial category: http://haxecoder.com/category.php?id=7 9 | -------------------------------------------------------------------------------- /source/MenuState.hx: -------------------------------------------------------------------------------- 1 | package ; 2 | 3 | import flixel.FlxG; 4 | import flixel.FlxSprite; 5 | import flixel.FlxState; 6 | import flixel.text.FlxText; 7 | import flixel.ui.FlxButton; 8 | import flixel.util.FlxMath; 9 | import flixel.util.FlxSave; 10 | 11 | /** 12 | * A FlxState which can be used for the game's menu. 13 | */ 14 | class MenuState extends FlxState 15 | { 16 | /** 17 | * Function that is called up when to state is created to set it up. 18 | */ 19 | override public function create():Void 20 | { 21 | super.create(); 22 | 23 | var init_x:Int = Math.floor(FlxG.width / 2 - 40); 24 | 25 | var btn_new = new FlxButton(init_x, 50, "New game", onNew); 26 | add(btn_new); 27 | 28 | var save:FlxSave = new FlxSave(); 29 | save.bind(PlayState.SAVE_NAME); 30 | 31 | if (save.data.hp!=null) { 32 | var btn_load = new FlxButton(init_x, 80, "Load", onLoad); 33 | add(btn_load); 34 | } 35 | } 36 | 37 | private function onNew():Void { 38 | var playState:PlayState = new PlayState(); 39 | FlxG.switchState(playState); 40 | playState.loadedGame = false; 41 | } 42 | 43 | private function onLoad():Void { 44 | var playState:PlayState = new PlayState(); 45 | FlxG.switchState(playState); 46 | playState.loadedGame = true; 47 | } 48 | 49 | /** 50 | * Function that is called when this state is destroyed - you might want to 51 | * consider setting all objects this state uses to null to help garbage collection. 52 | */ 53 | override public function destroy():Void 54 | { 55 | super.destroy(); 56 | } 57 | 58 | /** 59 | * Function that is called once every frame. 60 | */ 61 | override public function update():Void 62 | { 63 | super.update(); 64 | } 65 | } -------------------------------------------------------------------------------- /source/HUD.hx: -------------------------------------------------------------------------------- 1 | package ; 2 | import flixel.FlxG; 3 | import flixel.group.FlxSpriteGroup; 4 | import flixel.system.FlxSound; 5 | import flixel.text.FlxText; 6 | import flixel.ui.FlxBar; 7 | 8 | /** 9 | * RPG HUD. 10 | * @author Kirill Poletaev 11 | */ 12 | class HUD extends FlxSpriteGroup 13 | { 14 | private var healthDisplay:FlxText; 15 | private var levelDisplay:FlxText; 16 | private var expBar:FlxBar; 17 | 18 | public var hp:Int; 19 | public var maxHp:Int; 20 | public var exp:Int; 21 | public var maxExp:Int; 22 | public var level:Int; 23 | 24 | private var sfx_levelup:FlxSound; 25 | 26 | public function new() 27 | { 28 | super(); 29 | scrollFactor.x = 0; 30 | scrollFactor.y = 0; 31 | 32 | healthDisplay = new FlxText(2, 2); 33 | hp = 5; 34 | maxHp = 10; 35 | add(healthDisplay); 36 | 37 | levelDisplay = new FlxText(2, 12); 38 | level = 1; 39 | add(levelDisplay); 40 | 41 | maxExp = 10; 42 | exp = 0; 43 | expBar = new FlxBar(4, 25, FlxBar.FILL_LEFT_TO_RIGHT, 100, 4); 44 | expBar.createFilledBar(0xFF63460C, 0xFFE6AA2F); 45 | add(expBar); 46 | 47 | sfx_levelup = FlxG.sound.load("assets/sounds/levelup.wav"); 48 | } 49 | 50 | override public function update() { 51 | healthDisplay.text = "Health: " + hp + "/" + maxHp; 52 | levelDisplay.text = "Level: " + level; 53 | expBar.currentValue = exp; 54 | expBar.setRange(0, maxExp); 55 | } 56 | 57 | public function addHealth(num:Int):Void { 58 | hp += num; 59 | if (hp > maxHp) { 60 | hp = maxHp; 61 | } 62 | if (hp <= 0) { 63 | FlxG.switchState(new MenuState()); 64 | } 65 | } 66 | 67 | public function addExp(num:Int):Void { 68 | exp += num; 69 | while (exp > maxExp) { 70 | level++; 71 | exp -= maxExp; 72 | maxExp = Math.ceil(maxExp * 1.3); 73 | hp++; 74 | maxHp++; 75 | sfx_levelup.play(); 76 | } 77 | } 78 | 79 | public function getLevel():Int { 80 | return level; 81 | } 82 | 83 | } -------------------------------------------------------------------------------- /source/CombatWindow.hx: -------------------------------------------------------------------------------- 1 | package ; 2 | import flixel.FlxG; 3 | import flixel.FlxSprite; 4 | import flixel.group.FlxSpriteGroup; 5 | import flixel.system.FlxSound; 6 | import flixel.text.FlxText; 7 | import flixel.ui.FlxButton; 8 | import flixel.util.FlxColor; 9 | 10 | /** 11 | * Turn based combat window. 12 | * @author Kirill Poletaev 13 | */ 14 | class CombatWindow extends FlxSpriteGroup 15 | { 16 | private var btn_attack:FlxButton; 17 | private var btn_flee:FlxButton; 18 | private var bg:FlxSprite; 19 | private var txt:FlxText; 20 | 21 | private var playState:PlayState; 22 | private var enemy:Enemy; 23 | 24 | private var sfx_hit:FlxSound; 25 | 26 | public function new(playState:PlayState) 27 | { 28 | super(); 29 | this.playState = playState; 30 | this.scrollFactor.x = 0; 31 | this.scrollFactor.y = 0; 32 | 33 | x = FlxG.width / 2 - 100; 34 | y = FlxG.height / 2 - 40; 35 | 36 | bg = new FlxSprite(); 37 | add(bg); 38 | bg.makeGraphic(200, 80, 0xff222222); 39 | 40 | btn_attack = new FlxButton(5, 55, "Attack", onAttack); 41 | btn_flee = new FlxButton(115, 55, "Flee", onFlee); 42 | add(btn_attack); 43 | add(btn_flee); 44 | 45 | txt = new FlxText(5, 5, 190); 46 | add(txt); 47 | sfx_hit = FlxG.sound.load("assets/sounds/hit.wav"); 48 | } 49 | 50 | public function fight(enemy:Enemy) { 51 | this.enemy = enemy; 52 | txt.text = "A wild enemy appears!"; 53 | } 54 | 55 | private function onAttack() { 56 | sfx_hit.play(); 57 | var dmg:Int = playState.hud.getLevel(); 58 | enemy.health -= dmg; 59 | txt.text = "You hit the enemy, dealing " + dmg + " damage."; 60 | if (enemy.health > 0) { 61 | var enemyDmg:Int = Math.floor(Math.random()*2); 62 | txt.text += "\nThe enemy strikes, dealing " + enemyDmg + " damage."; 63 | playState.hud.addHealth( -enemyDmg); 64 | } else { 65 | playState.winCombat(enemy); 66 | playState.hud.addExp(6); 67 | } 68 | } 69 | 70 | private function onFlee() { 71 | playState.endCombat(enemy); 72 | } 73 | } -------------------------------------------------------------------------------- /source/Main.hx: -------------------------------------------------------------------------------- 1 | package ; 2 | 3 | import flash.display.Sprite; 4 | import flash.display.StageAlign; 5 | import flash.display.StageScaleMode; 6 | import flash.events.Event; 7 | import flash.Lib; 8 | import flixel.FlxGame; 9 | import flixel.FlxState; 10 | 11 | class Main extends Sprite 12 | { 13 | var gameWidth:Int = 320; // Width of the game in pixels (might be less / more in actual pixels depending on your zoom). 14 | var gameHeight:Int = 240; // Height of the game in pixels (might be less / more in actual pixels depending on your zoom). 15 | var initialState:Class = MenuState; // The FlxState the game starts with. 16 | var zoom:Float = 2; // If -1, zoom is automatically calculated to fit the window dimensions. 17 | var framerate:Int = 60; // How many frames per second the game should run at. 18 | var skipSplash:Bool = false; // Whether to skip the flixel splash screen that appears in release mode. 19 | var startFullscreen:Bool = false; // Whether to start the game in fullscreen on desktop targets 20 | 21 | // You can pretty much ignore everything from here on - your code should go in your states. 22 | 23 | public static function main():Void 24 | { 25 | Lib.current.addChild(new Main()); 26 | } 27 | 28 | public function new() 29 | { 30 | super(); 31 | 32 | if (stage != null) 33 | { 34 | init(); 35 | } 36 | else 37 | { 38 | addEventListener(Event.ADDED_TO_STAGE, init); 39 | } 40 | } 41 | 42 | private function init(?E:Event):Void 43 | { 44 | if (hasEventListener(Event.ADDED_TO_STAGE)) 45 | { 46 | removeEventListener(Event.ADDED_TO_STAGE, init); 47 | } 48 | 49 | setupGame(); 50 | } 51 | 52 | private function setupGame():Void 53 | { 54 | var stageWidth:Int = Lib.current.stage.stageWidth; 55 | var stageHeight:Int = Lib.current.stage.stageHeight; 56 | 57 | if (zoom == -1) 58 | { 59 | var ratioX:Float = stageWidth / gameWidth; 60 | var ratioY:Float = stageHeight / gameHeight; 61 | zoom = Math.min(ratioX, ratioY); 62 | gameWidth = Math.ceil(stageWidth / zoom); 63 | gameHeight = Math.ceil(stageHeight / zoom); 64 | } 65 | 66 | addChild(new FlxGame(gameWidth, gameHeight, initialState, zoom, framerate, framerate, skipSplash, startFullscreen)); 67 | } 68 | } -------------------------------------------------------------------------------- /source/Enemy.hx: -------------------------------------------------------------------------------- 1 | package ; 2 | import flixel.FlxG; 3 | import flixel.FlxSprite; 4 | import flixel.system.FlxSound; 5 | import flixel.tile.FlxTilemap; 6 | import flixel.util.FlxPath; 7 | import flixel.util.FlxPoint; 8 | 9 | /** 10 | * Enemy 11 | * @author Kirill Poletaev 12 | */ 13 | class Enemy extends FlxSprite 14 | { 15 | public var path:FlxPath; 16 | private var wandering:Bool; 17 | private var wanderTicks:Int; 18 | private var nodes:Array; 19 | private var tilemap:FlxTilemap; 20 | private var hero:FlxSprite; 21 | 22 | public function new(tilemap:FlxTilemap, hero:FlxSprite) 23 | { 24 | super(); 25 | this.tilemap = tilemap; 26 | this.hero = hero; 27 | 28 | loadGraphic("assets/images/enemy.png", true, PlayState.TILE_WIDTH, PlayState.TILE_HEIGHT); 29 | animation.add("down", [0, 1, 0, 2]); 30 | animation.add("up", [3, 4, 3, 5]); 31 | animation.add("right", [6, 7, 6, 8]); 32 | animation.add("left", [9, 10, 9, 11]); 33 | 34 | path = new FlxPath(); 35 | 36 | animation.play("down"); 37 | 38 | wandering = true; 39 | wanderTicks = Std.int(Math.random() * 300); 40 | } 41 | 42 | override public function update() { 43 | super.update(); 44 | 45 | if (wandering) { 46 | var startPoint:FlxPoint = FlxPoint.get(x + PlayState.TILE_WIDTH / 2, y + PlayState.TILE_HEIGHT / 2); 47 | var heroPoint:FlxPoint = FlxPoint.get(hero.x + PlayState.TILE_WIDTH / 2, hero.y + PlayState.TILE_HEIGHT / 2); 48 | 49 | if (hero.active && tilemap.ray(startPoint, heroPoint)) { 50 | var pathToHero:Array = tilemap.findPath(startPoint, heroPoint, false); 51 | if(pathToHero.length <= 5){ 52 | wanderTicks = 300; 53 | path.start(this, pathToHero); 54 | } 55 | } 56 | 57 | if (wanderTicks > 0) { 58 | wanderTicks--; 59 | } else { 60 | wanderTicks = Std.int(Math.random() * 300); 61 | while (nodes == null || nodes.length == 0) { 62 | var tileCoordY:Int = Std.int(startPoint.y / PlayState.TILE_WIDTH + Math.ceil(Math.random()*6) - 3); 63 | var tileCoordX:Int = Std.int(startPoint.x / PlayState.TILE_HEIGHT + Math.ceil(Math.random()*6) - 3); 64 | var endPoint = FlxPoint.get(tileCoordX * PlayState.TILE_WIDTH + PlayState.TILE_WIDTH / 2, tileCoordY * PlayState.TILE_HEIGHT + PlayState.TILE_HEIGHT / 2); 65 | nodes = tilemap.findPath(startPoint, endPoint); 66 | } 67 | path.start(this, nodes); 68 | } 69 | } 70 | 71 | if (!path.finished && path.nodes != null) { 72 | if (path.angle == 0 || path.angle == 45 || path.angle == -45) { 73 | animation.play("up"); 74 | } 75 | if (path.angle == 180 || path.angle == -135 || path.angle == 135) { 76 | animation.play("down"); 77 | } 78 | if (path.angle == 90) { 79 | animation.play("right"); 80 | } 81 | if (path.angle == -90) { 82 | animation.play("left"); 83 | } 84 | } else { 85 | animation.curAnim.curFrame = 0; 86 | animation.curAnim.stop(); 87 | nodes = null; 88 | } 89 | } 90 | 91 | } -------------------------------------------------------------------------------- /Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /assets/data/map.csv: -------------------------------------------------------------------------------- 1 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 2 | 1,2,2,2,2,2,2,2,2,1,2,2,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 3 | 1,2,2,2,2,2,2,2,2,1,2,2,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 4 | 1,2,2,1,1,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 5 | 1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0, 6 | 1,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0, 7 | 1,2,2,2,2,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0, 8 | 1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0, 9 | 1,2,2,2,2,2,2,2,2,2,1,2,2,2,1,0,0,0,0,0,0,0,1,2,2,2,2,2,2,2,2,2,1,1,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0, 10 | 1,2,1,2,2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,1,2,2,2,2,2,2,2,2,2,1,2,2,2,2,1,2,2,2,2,1,0,0,0,0,0,0,0, 11 | 1,2,1,2,2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,1,2,2,2,2,1,1,2,2,2,2,2,2,2,2,1,2,2,2,2,1,0,0,0,0,0,0,0, 12 | 1,1,1,1,1,1,1,1,1,1,2,2,1,1,1,0,0,0,0,0,0,0,1,2,2,2,1,1,2,2,2,2,2,2,2,2,1,1,2,2,2,2,1,0,0,0,0,0,0,0, 13 | 0,0,0,0,0,0,0,0,0,1,2,2,1,0,0,0,0,0,0,0,0,0,1,2,2,2,1,2,2,2,2,2,2,2,2,1,1,2,2,2,2,2,1,0,0,0,0,0,0,0, 14 | 0,0,0,0,0,0,0,0,0,1,2,2,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,2,2,2,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0, 15 | 0,0,0,0,0,0,0,0,0,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 16 | 0,0,0,0,0,0,0,0,0,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 17 | 0,0,0,0,0,1,1,1,1,1,2,2,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,2,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 18 | 0,0,0,0,0,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,1,2,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 19 | 0,0,0,0,0,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,2,2,2,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0, 20 | 0,0,0,0,0,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0, 21 | 0,0,0,0,0,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1, 22 | 0,0,0,0,0,1,1,1,1,1,2,2,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,1,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,1, 23 | 0,0,0,0,0,0,0,0,0,1,2,2,1,0,0,0,0,0,0,0,0,0,1,2,2,2,2,2,2,1,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,1, 24 | 0,0,0,0,0,0,0,0,0,1,2,2,1,0,0,0,0,0,0,0,0,0,1,2,2,2,2,2,2,1,2,2,2,2,2,1,2,2,2,2,2,1,1,1,1,1,1,1,1,1, 25 | 0,0,0,0,0,0,0,0,0,1,2,2,1,1,0,0,0,0,0,0,0,0,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0, 26 | 0,0,0,0,0,0,1,1,1,1,2,2,2,1,1,1,1,1,1,1,1,0,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0, 27 | 0,0,0,0,0,0,1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,2,1,2,1,2,2,2,2,1,2,1,2,1,2,1,2,1,2,1,0,0,0,0,0,0,0,0, 28 | 0,0,0,0,0,0,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0, 29 | 0,0,0,0,0,0,1,2,2,2,1,1,2,2,2,2,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0, 30 | 0,0,0,0,0,0,1,2,2,2,1,1,2,2,2,2,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0, 31 | 0,0,0,0,0,0,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0, 32 | 0,0,0,0,0,0,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0, 33 | 0,0,0,0,0,0,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0, 34 | 0,0,0,0,0,0,1,2,2,2,1,1,2,2,2,2,1,1,2,2,2,2,1,1,1,1,2,2,2,2,2,2,1,1,1,1,2,2,2,2,2,1,0,0,0,0,0,0,0,0, 35 | 0,0,0,0,0,0,1,2,2,2,1,1,2,2,2,2,1,1,2,2,2,2,1,1,1,1,2,2,2,2,2,2,1,1,1,1,2,2,2,2,2,1,0,0,0,0,0,0,0,0, 36 | 0,0,0,0,0,0,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0, 37 | 0,0,0,0,0,0,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0, 38 | 0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,2,2,2,2,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,2,2,2,2,1,1,1,0,0,0,0,0,0,0,0, 39 | 0,0,0,0,0,0,0,0,1,2,2,2,2,2,2,1,2,2,2,1,0,0,0,1,2,1,0,0,0,0,1,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0,0,0, 40 | 0,0,0,0,0,0,0,0,1,2,2,2,1,2,2,2,1,2,2,1,1,1,1,1,2,1,1,1,1,1,1,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0,0,0, 41 | 0,0,0,0,0,0,0,0,1,1,2,2,2,1,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0, 42 | 0,0,0,0,0,0,0,0,1,2,1,2,2,2,1,2,2,2,2,1,1,1,1,1,2,1,1,1,1,1,1,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0,0,0, 43 | 0,0,0,0,0,0,0,0,1,2,2,1,2,2,2,1,2,2,2,1,0,0,0,1,2,1,0,0,0,0,1,1,1,1,1,1,1,1,2,1,0,0,0,0,0,0,0,0,0,0, 44 | 0,0,0,0,0,0,0,0,1,2,2,2,2,2,2,2,1,2,2,1,1,1,1,1,2,1,1,1,1,1,1,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0,0,0, 45 | 0,0,0,0,0,0,0,0,1,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0,0,0, 46 | 0,0,0,0,0,0,0,0,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0, 47 | 0,0,0,0,0,0,0,0,0,0,0,1,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 48 | 0,0,0,0,0,0,0,0,0,0,0,1,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 49 | 0,0,0,0,0,0,0,0,0,0,0,1,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 50 | 0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 -------------------------------------------------------------------------------- /source/PlayState.hx: -------------------------------------------------------------------------------- 1 | package ; 2 | 3 | import flixel.effects.particles.FlxEmitter; 4 | import flixel.effects.particles.FlxParticle; 5 | import flixel.FlxCamera; 6 | import flixel.FlxG; 7 | import flixel.FlxObject; 8 | import flixel.FlxSprite; 9 | import flixel.FlxState; 10 | import flixel.group.FlxTypedGroup; 11 | import flixel.tile.FlxTilemap; 12 | import flixel.tweens.FlxEase; 13 | import flixel.tweens.FlxTween; 14 | import flixel.ui.FlxButton; 15 | import flixel.util.FlxColor; 16 | import flixel.util.FlxPath; 17 | import flixel.util.FlxPoint; 18 | import flixel.util.FlxSave; 19 | import openfl.Assets; 20 | 21 | enum PlayerAction { 22 | Walking; 23 | Combat; 24 | } 25 | 26 | /** 27 | * A FlxState which can be used for the actual gameplay. 28 | */ 29 | class PlayState extends FlxState 30 | { 31 | private var camera:FlxCamera; 32 | private var cameraFocus:FlxSprite; 33 | private var combatHide:FlxTween; 34 | private var combatWindow:CombatWindow; 35 | private var currentAction:PlayerAction; 36 | private var enemies:Array; 37 | private var movementMarker:FlxSprite; 38 | private var path:FlxPath; 39 | private var potions:FlxTypedGroup; 40 | private var saveButton:FlxButton; 41 | private var tileMap:FlxTilemap; 42 | private var particleEmitter:FlxEmitter; 43 | public static var CAMERA_SPEED:Int = 8; 44 | public static var LEVEL_HEIGHT:Int = 50; 45 | public static var LEVEL_WIDTH:Int = 50; 46 | public static var SAVE_NAME:String = "RPG_Save"; 47 | public static var TILE_HEIGHT:Int = 16; 48 | public static var TILE_WIDTH:Int = 16; 49 | public var hero:FlxSprite; 50 | public var hud:HUD; 51 | public var loadedGame:Bool; 52 | 53 | /** 54 | * Function that is called up when to state is created to set it up. 55 | */ 56 | override public function create():Void 57 | { 58 | super.create(); 59 | 60 | FlxG.worldBounds.width = TILE_WIDTH * LEVEL_WIDTH; 61 | FlxG.worldBounds.height = TILE_HEIGHT * LEVEL_HEIGHT; 62 | 63 | tileMap = new FlxTilemap(); 64 | tileMap.loadMap(Assets.getText("assets/data/map.csv"), "assets/images/tileset.png", TILE_WIDTH, TILE_HEIGHT, 0, 1); 65 | tileMap.setTileProperties(0, FlxObject.ANY); 66 | tileMap.setTileProperties(1, FlxObject.ANY); 67 | tileMap.setTileProperties(2, FlxObject.NONE); 68 | add(tileMap); 69 | 70 | cameraFocus = new FlxSprite(); 71 | cameraFocus.makeGraphic(1, 1, FlxColor.TRANSPARENT); 72 | add(cameraFocus); 73 | 74 | camera = FlxG.camera; 75 | camera.follow(cameraFocus, FlxCamera.STYLE_LOCKON); 76 | 77 | movementMarker = new FlxSprite(); 78 | movementMarker.visible = false; 79 | add(movementMarker); 80 | 81 | hero = new FlxSprite(TILE_WIDTH * 7, TILE_HEIGHT * 3); 82 | hero.loadGraphic("assets/images/hero.png", true, TILE_WIDTH, TILE_HEIGHT); 83 | hero.animation.add("down", [0, 1, 0, 2]); 84 | hero.animation.add("up", [3, 4, 3, 5]); 85 | hero.animation.add("right", [6, 7, 6, 8]); 86 | hero.animation.add("left", [9, 10, 9, 11]); 87 | add(hero); 88 | 89 | potions = new FlxTypedGroup(); 90 | add(potions); 91 | 92 | hero.animation.play("down"); 93 | path = new FlxPath(); 94 | 95 | enemies = new Array(); 96 | 97 | hud = new HUD(); 98 | 99 | if (loadedGame) { 100 | loadGame(); 101 | }else { 102 | newGame(); 103 | } 104 | 105 | particleEmitter = new FlxEmitter(0, 0); 106 | particleEmitter.setXSpeed( -8, 8); 107 | particleEmitter.setYSpeed( -8, 8); 108 | add(particleEmitter); 109 | 110 | add(hud); 111 | 112 | currentAction = Walking; 113 | hero.active = true; 114 | 115 | combatWindow = new CombatWindow(this); 116 | combatWindow.active = false; 117 | combatWindow.visible = false; 118 | add(combatWindow); 119 | 120 | saveButton = new FlxButton(FlxG.width - 80, 0, "Save", doSave); 121 | add(saveButton); 122 | } 123 | 124 | public function newGame():Void { 125 | spawnPotion(5, 5); 126 | spawnPotion(6, 5); 127 | spawnPotion(3, 10); 128 | spawnPotion(4, 10); 129 | spawnPotion(1, 10); 130 | addEnemy(10, 15); 131 | addEnemy(12, 10); 132 | addEnemy(15, 6); 133 | addEnemy(20, 6); 134 | addEnemy(12, 20); 135 | } 136 | 137 | public function loadGame():Void { 138 | var save:FlxSave = new FlxSave(); 139 | save.bind(SAVE_NAME); 140 | hud.hp = save.data.hp; 141 | hud.maxHp = save.data.maxHp; 142 | hud.exp = save.data.exp; 143 | hud.maxExp = save.data.maxExp; 144 | hud.level = save.data.level; 145 | var i:Int; 146 | if(save.data.enemies != null){ 147 | for (i in 0...save.data.enemies.length) { 148 | addEnemy(Math.floor(save.data.enemies[i].x / TILE_WIDTH), Math.floor(save.data.enemies[i].y / TILE_HEIGHT)); 149 | } 150 | } 151 | if(save.data.potions != null){ 152 | for (i in 0...save.data.potions.length) { 153 | spawnPotion(Math.floor(save.data.potions[i].x / TILE_WIDTH), Math.floor(save.data.potions[i].y / TILE_HEIGHT)); 154 | } 155 | } 156 | hero.x = save.data.heroX; 157 | hero.y = save.data.heroY; 158 | cameraFocus.x = save.data.cameraX; 159 | cameraFocus.y = save.data.cameraY; 160 | } 161 | 162 | private function doSave():Void { 163 | var save:FlxSave = new FlxSave(); 164 | 165 | // Delete all existing data 166 | save.bind(SAVE_NAME); 167 | save.erase(); 168 | 169 | // Write new data 170 | save.bind(SAVE_NAME); 171 | save.data.hp = hud.hp; 172 | save.data.maxHp = hud.maxHp; 173 | save.data.exp = hud.exp; 174 | save.data.maxExp = hud.maxExp; 175 | save.data.level = hud.level; 176 | var i:Int; 177 | save.data.enemies = new Array(); 178 | for (i in 0...enemies.length) { 179 | if(enemies[i].exists && enemies[i].active){ 180 | save.data.enemies.push({ x: enemies[i].x, y: enemies[i].y }); 181 | } 182 | } 183 | save.data.potions = new Array(); 184 | for (i in 0...potions.members.length) { 185 | if(potions.members[i].exists && potions.members[i].active){ 186 | save.data.potions.push({ x: potions.members[i].x, y: potions.members[i].y }); 187 | } 188 | } 189 | save.data.heroX = hero.x; 190 | save.data.heroY = hero.y; 191 | save.data.cameraX = cameraFocus.x; 192 | save.data.cameraY = cameraFocus.y; 193 | 194 | save.flush(); 195 | FlxG.switchState(new MenuState()); 196 | } 197 | 198 | private function spawnPotion(x:Int, y:Int):Void{ 199 | var potion:Potion = new Potion(); 200 | potion.x = x * TILE_WIDTH; 201 | potion.y = y * TILE_HEIGHT; 202 | potions.add(potion); 203 | } 204 | 205 | private function addEnemy(x:Int, y:Int):Void { 206 | var enemy:Enemy = new Enemy(tileMap, hero); 207 | enemy.x = x * TILE_WIDTH; 208 | enemy.y = y * TILE_HEIGHT; 209 | enemy.health = 5; 210 | enemies.push(enemy); 211 | add(enemy); 212 | } 213 | 214 | private function onPotionCollision(hero:FlxSprite, potion:Potion):Void { 215 | if (potion.exists && hero.exists) { 216 | potion.kill(); 217 | hud.addHealth(1); 218 | 219 | particleEmitter.x = potion.x + TILE_WIDTH / 2; 220 | particleEmitter.y = potion.y + TILE_HEIGHT / 2; 221 | var i:Int; 222 | for (i in 0...10) { 223 | var particle:FlxParticle = new FlxParticle(); 224 | particle.makeGraphic(2, 2, FlxColor.CYAN); 225 | particle.visible = false; 226 | particleEmitter.add(particle); 227 | } 228 | particleEmitter.start(true, 2, 0, 10, 1); 229 | } 230 | } 231 | 232 | private function onEnemyCollision(hero:FlxSprite, enemy:Enemy):Void { 233 | if (enemy.exists && hero.exists && hero.active && enemy.active) { 234 | hero.active = false; 235 | enemy.active = false; 236 | currentAction = Combat; 237 | startCombat(enemy); 238 | } 239 | } 240 | 241 | private function startCombat(enemy:Enemy):Void { 242 | combatWindow.active = true; 243 | combatWindow.visible = true; 244 | if (combatHide!=null && combatHide.active) { 245 | combatHide.cancel(); 246 | } 247 | FlxTween.tween(combatWindow, { y: FlxG.height / 2 - 40 }, 1, { type: FlxTween.ONESHOT, ease: FlxEase.quadOut } ); 248 | combatWindow.y = -200; 249 | combatWindow.fight(enemy); 250 | } 251 | 252 | public function winCombat(enemy:Enemy):Void { 253 | endCombat(enemy); 254 | } 255 | 256 | public function endCombat(enemy:Enemy):Void { 257 | enemy.kill(); 258 | combatHide = FlxTween.tween(combatWindow, {y: -200 }, 1, { type: FlxTween.ONESHOT, ease: FlxEase.quadIn, complete: hideCombat} ); 259 | hero.active = true; 260 | currentAction = Walking; 261 | } 262 | 263 | private function hideCombat(tween:FlxTween):Void { 264 | combatWindow.active = false; 265 | combatWindow.visible = false; 266 | } 267 | 268 | /** 269 | * Function that is called when this state is destroyed - you might want to 270 | * consider setting all objects this state uses to null to help garbage collection. 271 | */ 272 | override public function destroy():Void 273 | { 274 | super.destroy(); 275 | } 276 | 277 | /** 278 | * Function that is called once every frame. 279 | */ 280 | override public function update():Void 281 | { 282 | super.update(); 283 | 284 | // Save visibility 285 | saveButton.visible = currentAction == Walking; 286 | 287 | // Collisions 288 | FlxG.overlap(hero, potions, onPotionCollision); 289 | var i:Int; 290 | for (i in 0...enemies.length) { 291 | FlxG.overlap(hero, enemies[i], onEnemyCollision); 292 | } 293 | 294 | // Animation 295 | if (!path.finished && path.nodes!=null) { 296 | if (path.angle == 0 || path.angle == 45 || path.angle == -45) { 297 | hero.animation.play("up"); 298 | } 299 | if (path.angle == 180 || path.angle == -135 || path.angle == 135) { 300 | hero.animation.play("down"); 301 | } 302 | if (path.angle == 90) { 303 | hero.animation.play("right"); 304 | } 305 | if (path.angle == -90) { 306 | hero.animation.play("left"); 307 | } 308 | } else { 309 | hero.animation.curAnim.curFrame = 0; 310 | hero.animation.curAnim.stop(); 311 | } 312 | 313 | // Camera movement 314 | if (FlxG.keys.anyPressed(["DOWN", "S"])) { 315 | cameraFocus.y += CAMERA_SPEED; 316 | } 317 | if (FlxG.keys.anyPressed(["UP", "W"])) { 318 | cameraFocus.y -= CAMERA_SPEED; 319 | } 320 | if (FlxG.keys.anyPressed(["RIGHT", "D"])) { 321 | cameraFocus.x += CAMERA_SPEED; 322 | } 323 | if (FlxG.keys.anyPressed(["LEFT", "A"])) { 324 | cameraFocus.x -= CAMERA_SPEED; 325 | } 326 | 327 | // Camera bounds 328 | if (cameraFocus.x < FlxG.width / 2) { 329 | cameraFocus.x = FlxG.width / 2; 330 | } 331 | if (cameraFocus.x > LEVEL_WIDTH * TILE_WIDTH - FlxG.width / 2) { 332 | cameraFocus.x = LEVEL_WIDTH * TILE_WIDTH - FlxG.width / 2; 333 | } 334 | if (cameraFocus.y < FlxG.height / 2) { 335 | cameraFocus.y = FlxG.height / 2; 336 | } 337 | if (cameraFocus.y > LEVEL_HEIGHT * TILE_HEIGHT - FlxG.height / 2) { 338 | cameraFocus.y = LEVEL_HEIGHT * TILE_HEIGHT - FlxG.height / 2; 339 | } 340 | 341 | // Mouse clicks 342 | if (currentAction == Walking && FlxG.mouse.justReleased){ 343 | var tileCoordX:Int = Math.floor(FlxG.mouse.x / TILE_WIDTH); 344 | var tileCoordY:Int = Math.floor(FlxG.mouse.y / TILE_HEIGHT); 345 | 346 | movementMarker.visible = true; 347 | if (tileMap.getTile(tileCoordX, tileCoordY) == 2) { 348 | var nodes:Array = tileMap.findPath(FlxPoint.get(hero.x + TILE_WIDTH/2, hero.y + TILE_HEIGHT/2), FlxPoint.get(tileCoordX * TILE_WIDTH + TILE_WIDTH/2, tileCoordY * TILE_HEIGHT + TILE_HEIGHT/2)); 349 | if (nodes != null) { 350 | path.start(hero, nodes); 351 | movementMarker.loadGraphic(AssetPaths.marker_move__png, false, TILE_WIDTH, TILE_HEIGHT); 352 | }else { 353 | movementMarker.loadGraphic(AssetPaths.marker_stop__png, false, TILE_WIDTH, TILE_HEIGHT); 354 | } 355 | }else { 356 | movementMarker.loadGraphic(AssetPaths.marker_stop__png, false, TILE_WIDTH, TILE_HEIGHT); 357 | } 358 | movementMarker.setPosition(tileCoordX * TILE_WIDTH, tileCoordY * TILE_HEIGHT); 359 | } 360 | } 361 | } --------------------------------------------------------------------------------