// forked from takimo's 不思議のダンジョン自動生成 package { import flash.display.Bitmap; import flash.utils.IDataInput; import flash.events.Event; import flash.events.KeyboardEvent; import flash.text.TextField; import flash.text.TextFormat; import flash.geom.Rectangle; import flash.display.Sprite; import flash.geom.Point; import flash.geom.Rectangle; import flash.text.AntiAliasType; //参考:http://d.hatena.ne.jp/Gemma/20070816 [SWF(backgroundColor="#000000")] public class Rogue extends Sprite { private var _debug:TextField; private var _mapText:TextField; private var _status:TextField; private var _fontSize:uint = 12; private var _rectArray:Array; private var _dungeon:Array; private var _map:Array; private var _format:TextFormat; private var _commandManager:CommandManager; private var _user:User; private var _players:Array; private var _floor:Floor; private var _enemies:Array; public function Rogue() { super(); _debug = new TextField(); createTextField(_debug, 400, 40, 0, 0, 12, 0xffffff, 'h,j,k,lで移動。@がPlayer。アルファベットは敵'); _mapText = new TextField(); // game 用 //createTextField(_mapText, 300, 300, 100, 100, 20, 0xffffff); // debug 用 createTextField(_mapText, 400, 400, 0, 0, 10, 0xffffff); _status = new TextField(); createTextField(_status, 100, 400, 350, 0, 12, 0xffffff); _user = new User(); _user.symbol = "@"; _user.hitpoint = 10; _commandManager = new CommandManager(); _commandManager.addEventListener(CommandEvent.COMPLETE, commandManager_completeHandler); stage.addEventListener(KeyboardEvent.KEY_UP, stage_keyUpHandler); _floor = new Floor(this); _floor.addEventListener(DungeonEvent.NEXT_FLOOR, dungeon_nextfloorHandler); nextFloor(); renderMap(); _status.appendText("\n"); displayStatus(); } private function displayStatus():void { _status.text = ""; _status.appendText("Floor : " + String(_floor.count) + "\n"); _status.appendText("HP : " + String(_user.hitpoint) + "\n"); _status.appendText("LEVEL : " + String(_user.level) + "\n"); _status.appendText("EXP : " + String(_user.exp) + "\n"); } private function nextFloor():void { _floor.generate(); //render(floor.map.toString()); _enemies = new Array(); _floor.dungeon = _floor.map; _user.position = _floor.randomPosition(); generateEnemies(["A", "B", "C", "D"]); setEnemies(); _floor.dungeon[_user.position.y][_user.position.x] = _user; _map = generateMap(_floor.dungeon); } private function generateEnemies(enemySymbols:Array):void { var enemy:Enemy; for each(var enemySymbol:String in enemySymbols) { enemy = new Enemy(); enemy.position = _floor.randomPosition(); enemy.symbol = enemySymbol; enemy.hitpoint = 3; _enemies.push(enemy); } } private function setEnemies():void { for each(var enemy:Enemy in _enemies) { if(enemy.hitpoint > 0) _floor.dungeon[enemy.position.y][enemy.position.x] = enemy; else //debug(_floor.map[enemy.position.y][enemy.position.x]); _floor.dungeon[enemy.position.y][enemy.position.x] = _floor.map[enemy.position.y][enemy.position.x]; } } private function workEnemies():void { _enemies.map(moveEnemy); /** for each(var enemy:Enemy in _enemies) { } */ } private function moveEnemy(enemy:Enemy, index:int, array:Array):void { if(enemy.hitpoint > 0) { var position:Point = enemy.position.clone(); var way:uint = randomRange(0, 4); switch(way) { case 0: enemy.position = _floor.move(enemy, Command.MOVE_RIGHT); break; case 1: enemy.position = _floor.move(enemy, Command.MOVE_LEFT); break; case 2: enemy.position = _floor.move(enemy, Command.MOVE_UP); break; case 3: enemy.position = _floor.move(enemy, Command.MOVE_DOWN); break; default: break; } _floor.dungeon[enemy.position.y][enemy.position.x] = enemy; } else { //debug(_floor.map[enemy.position.y][enemy.position.x]); } } private function generateMap(map:Array, position:Point = null):Array { var dungeon:Array = new Array(); var tmp:Array; for each (var row:Array in map) { tmp = new Array(); for each (var column:* in row) { if (column is Wall) tmp.push(column.symbol); else if (column is Ground) tmp.push("."); else if (column is Road) tmp.push("#"); else if (column is Door) tmp.push(column.symbol); else if (column is Stairs) tmp.push(column.symbol); else if (column is User) tmp.push(column.symbol); else if (column is Enemy) tmp.push(column.symbol); else //debug(String(column)); tmp.push(" "); } dungeon.push(tmp); } return dungeon; } public function debug(message:String, isBreak:Boolean = true):void { if (isBreak) message += "\n"; //_debug.appendText(message); _debug.text = message; } private function render(message:String, isBreak:Boolean = true):void { if (isBreak) message += "\n"; _mapText.appendText(message); } private function renderMap():void { var rect:Rectangle = new Rectangle(0, 0, 60, 30); //var rect:Rectangle = new Rectangle(_user.position.x - 10 , _user.position.y - 5, 20, 10); render(""); _mapText.text = ""; for (var i:int = rect.top; i < rect.bottom; i++) { for (var j:int = rect.left; j < rect.right; j++) { if(i < 0 || j < 0 || i > _floor.height || j > _floor.width || !_map[i][j]) render(" ", false); else render(_map[i][j], false); } render('\n', false); } } private function createTextField(textField:TextField, width:int, height:int, x:int = 0, y:int = 0, fontsize:int=12, color:int = 0xffffff, text:String = " "):void { var format:TextFormat = new TextFormat(); format.font = '_等幅'; format.size = fontsize; format.color = color; textField.height = height; textField.width = width; textField.x = x; textField.y = y; //textField.border = true; textField.text = text; textField.setTextFormat(format); this.addChild(textField); } private function stage_keyUpHandler(event:KeyboardEvent):void { _commandManager.input(event.keyCode); //debug(String(event.keyCode)); } private function commandManager_completeHandler(event:CommandEvent):void { _user.position = _floor.move(_user, event.action); _floor.dungeon = new Array(); _floor.dungeon = _floor.map; setEnemies(); _floor.dungeon[_user.position.y][_user.position.x] = _user; _map = generateMap(_floor.dungeon); renderMap(); displayStatus(); workEnemies(); } private function dungeon_nextfloorHandler(event:DungeonEvent):void { nextFloor(); } private function randomRange(min:int, max:int):int { if(min > max) return 0; else if(min == max) return min; else return Math.floor(Math.random() * (max - min)) + min; } } } import flash.events.EventDispatcher; class CommandManager extends EventDispatcher { private var _command:String; public function CommandManager():void { _command = ""; } public function input(command:int):void { _command += String(command); switch(_command) { case "27": _command = ""; break; case "72": dispatch(Command.MOVE_LEFT); break; case "74": dispatch(Command.MOVE_DOWN); break; case "75": dispatch(Command.MOVE_UP); break; case "76": dispatch(Command.MOVE_RIGHT); break; default: dispatch("error"); } } private function dispatch(action:String):void { var event:CommandEvent = new CommandEvent(CommandEvent.COMPLETE); event.action = action; dispatchEvent(event); _command = ""; } } import flash.geom.Point; import flash.geom.Rectangle; class Dungeon { public function get floor():Floor { return _floor; } private var _floor:Floor; public function set floor(floor:Floor):void { _floor = floor; } public function get enemies():Array { return _enemies; } private var _enemies:Array; public function set enemies(enemies:Array):void { _enemies = enemies; } public function Dungeon():void { } public function generateFloor():void { } public function generateEnemies():void { } public function renderFloor():String { return ""; } public function setPlayer():void { } public function setItems(items:Array):void { } public function move():void { } } import flash.geom.Rectangle; import flash.utils.ByteArray; import flash.events.EventDispatcher; class Floor extends EventDispatcher { private var _dungeonWidth:uint = 60; private var _dungeonHeight:uint = 30; private var _margin:uint = 8; private var _self:*; public function get count():uint { return _count; } private var _count:uint; public function set count(count:uint):void { _count = count; } public function get width():uint { return _dungeonWidth; } public function get height():uint { return _dungeonHeight; } public function get rooms():Array { return _rooms; } private var _rooms:Array; public function set rooms(rooms:Array):void { _rooms = rooms; } public function get map():Array { var tmp:Array = new Array(); for each(var colmun:Array in _map) tmp.push(colmun.concat()); return tmp; //return _map; } private var _map:Array; public function set map(map:Array):void { _map = map; } public function get dungeon():Array { /** var tmp:Array = new Array(); for each(var colmun:Array in _dungeon) tmp.push(colmun.concat()); return tmp; */ return _dungeon; } private var _dungeon:Array; public function set dungeon(dungeon:Array):void { _dungeon = dungeon; } public function Floor(self:*):void { _self = self; _count = 0; } public function generate():void { var rect:Rectangle = new Rectangle(0, 0, _dungeonWidth - 1, _dungeonHeight - 1); _map = new Array(_dungeonHeight); for (var j:int=0; j<_dungeonHeight; j++) { _map[j] =new Array(_dungeonWidth); for(var i:int=0; i < _dungeonWidth; i++) { _map[j][i] = null; } } var compartments:Array = flatten(split(rect), []); _rooms = compartments.map(createRoom); for each (var room:Rectangle in _rooms) { setRoom(room); } createRoad(compartments, _rooms); setStairs(_rooms); _count++; } public function move(character:*, way:String):Point { var point:Point = character.position.clone(); switch(way) { case Command.MOVE_RIGHT: point.x++; break; case Command.MOVE_LEFT: point.x--; break; case Command.MOVE_UP: point.y--; break; case Command.MOVE_DOWN: point.y++; break; default: break; } var dungeon:* = _dungeon[point.y][point.x]; //_self.debug(dungeon.toString()); if ((dungeon is Ground) || (dungeon is Road) || (dungeon is Door)) { return point; } else if(!(character is Enemy)) { if ((dungeon is Enemy)) { dungeon.protec(); } else if (dungeon is Stairs) { var event:DungeonEvent = new DungeonEvent(DungeonEvent.NEXT_FLOOR); dispatchEvent(event); } return character.position; } else { if ((dungeon is User)) { dungeon.protec(); } return character.position; } } public function attack(enemy:Enemy):void { enemy.hitpoint--; } private function half(n:int):int { return int(n >> 1); } private function setRoad(rect:Rectangle):void { var min_j:int = Math.min(rect.top, rect.bottom); var max_j:int = Math.max(rect.top, rect.bottom); var min_i:int = Math.min(rect.left, rect.right); var max_i:int = Math.max(rect.left, rect.right); for (var j:int = min_j; j <= max_j; j++) for (var i:int = min_i; i <= max_i; i++) _map[j][i] = new Road(); } private function setRoom(rect:Rectangle):void { var min_j:int = Math.min(rect.top, rect.bottom); var max_j:int = Math.max(rect.top, rect.bottom); var min_i:int = Math.min(rect.left, rect.right); var max_i:int = Math.max(rect.left, rect.right); for (var j:int = min_j; j <= max_j; j++) for (var i:int = min_i; i <= max_i; i++) _map[j][i] = new Ground(); for (j = min_j; j <= max_j; j++) { _map[j][min_i] = new Wall(Wall.LEFT); _map[j][max_i] = new Wall(Wall.RIGHT); } for (i = min_i; i <= max_i; i++) { _map[min_j][i] = new Wall(Wall.TOP); _map[max_j][i] = new Wall(Wall.BOTTOM); } } private function setDoor(rect:Rectangle):void { var min_j:int = Math.min(rect.top, rect.bottom); var max_j:int = Math.max(rect.top, rect.bottom); var min_i:int = Math.min(rect.left, rect.right); var max_i:int = Math.max(rect.left, rect.right); for (var j:int = min_j; j <= max_j; j++) for (var i:int = min_i; i <= max_i; i++) _map[j][i] = new Door(); } private function setStairs(rects:Array):void { var position:Point = randomPosition(); _map[position.y][position.x] = new Stairs(); } private function randomRange(min:int, max:int):int { if(min > max) return 0; else if(min == max) return min; else return Math.floor(Math.random() * (max - min)) + min; } private function createRoad(compartments:Array, rooms:Array):void { var connectList:Array = new Array(); for (var i:int = 0; i < compartments.length - 1; i++) connectList.push([i, i + 1]); for (var k:int = 0; k < compartments.length; k++) for (var j:int = k + 1; j < compartments.length; j++) if (randomRange(0,5) == 0) if (compartments[k].left == compartments[j].right || compartments[k].right == compartments[j].left || compartments[k].top == compartments[j].bottom || compartments[k].bottom == compartments[j].top) connectList.push([k,j]); for each (var connect:Array in connectList) { var from:int = connect[0]; var to:int = connect[1]; var parentCompartment:Rectangle = compartments[from]; var childrenCompartment:Rectangle = compartments[to]; var parentRoom:Rectangle = rooms[from]; var childrenRoom:Rectangle = rooms[to]; if (parentCompartment.bottom == childrenCompartment.top) { // N 縦 var a:int = randomRange(parentRoom.left + 1, parentRoom.right - 1); var b:int = randomRange(childrenRoom.left + 1, childrenRoom.right - 1); setDoor(point2Rect(a, parentRoom.bottom, a, parentRoom.bottom)); setRoad(point2Rect(a, parentRoom.bottom + 1, a, parentCompartment.bottom)); setRoad(point2Rect(b, childrenRoom.top - 1, b, childrenCompartment.top)); setDoor(point2Rect(b, childrenRoom.top, b, childrenRoom.top)); setRoad(point2Rect(a, parentCompartment.bottom, b, childrenCompartment.top)); } else if (parentCompartment.right == childrenCompartment.left) { // Z 横 a = randomRange(parentRoom.top + 1, parentRoom.bottom - 1); b = randomRange(childrenRoom.top + 1, childrenRoom.bottom - 1); setDoor(point2Rect(parentRoom.right, a, parentRoom.right, a)); setRoad(point2Rect(parentRoom.right + 1, a, parentCompartment.right, a)); setRoad(point2Rect(childrenRoom.left - 1, b, childrenCompartment.left, b)); setDoor(point2Rect(childrenRoom.left, b, childrenRoom.left, b)); setRoad(point2Rect(parentCompartment.right, a, childrenCompartment.left, b)); } } } private function point2Rect(x0:int, y0:int, x1:int, y1:int):Rectangle { var rect:Rectangle = new Rectangle(); rect.topLeft = new Point(Number(x0), Number(y0)); rect.bottomRight = new Point(Number(x1), Number(y1)); return rect; } private function createRoom(rect:Rectangle, index:int=0, array:Array=null):Rectangle { var room:Rectangle = new Rectangle(); room.left = rect.left + randomRange(2, half(rect.right - rect.left) - 1); room.top = rect.top + randomRange(2, half(rect.bottom - rect.top) - 1); room.right = rect.right - randomRange(2, half(rect.right - rect.left) - 1); room.bottom = rect.bottom - randomRange(2, half(rect.bottom - rect.top) - 1); return room; } private function split(rect:Rectangle, index:int=0, array:Array=null):Array { if ((rect.right - rect.left < _margin * 2) || (rect.bottom - rect.top < _margin * 2)) { return [rect]; } var rect1:Rectangle = new Rectangle(); var rect2:Rectangle = new Rectangle(); // 縦(0) or 横(1) var direction:int = Math.floor(Math.random() * 2); //debug("direction : " + String(direction)); // 分割 var point:Point = new Point(); // 縦分割 if(direction) { point.x = randomRange(rect.left + _margin, rect.right - _margin); rect1.topLeft = rect.topLeft; rect1.bottomRight = new Point(point.x, rect.bottom); rect2.topLeft = new Point(point.x, rect.top); rect2.bottomRight = rect.bottomRight; } else { point.y =randomRange(rect.top + _margin, rect.bottom - _margin); rect1.topLeft = rect.topLeft; rect1.bottomRight = new Point(rect.right, point.y); rect2.topLeft = new Point(rect.x, point.y); rect2.bottomRight = rect.bottomRight; } return [rect1, rect2].map(split); } private function flatten(array:*, tmp:Array):Array { //debug("flatten"); for each (var item:* in array) { if(item as Array) tmp = flatten(item, tmp); if(item as Rectangle) tmp.push(item); } return tmp; } public function randomPosition():Point { var target:int = randomRange(0, _rooms.length); var rect:Rectangle = _rooms[target]; var point:Point = new Point(); point.x = randomRange(rect.left + 1, rect.right - 1); point.y = randomRange(rect.top + 1, rect.bottom - 1); return point; } public function clone(source:Object):* { var tmp:ByteArray = new ByteArray(); tmp.writeObject(source); tmp.position = 0; return(tmp.readObject()); } } import flash.events.Event; class CommandEvent extends Event { public static const COMPLETE:String = "complete"; /** * action */ public function get action():String { return _action; } private var _action:String; public function set action(action:String):void { _action = action; } public function CommandEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false):void { super(type, bubbles, cancelable); } public override function clone():Event { return new CommandEvent(type, bubbles, cancelable); } } class Command { public static const MOVE_LEFT:String = "move_left"; public static const MOVE_RIGHT:String = "move_right"; public static const MOVE_UP:String = "move_up"; public static const MOVE_DOWN:String = "move_down"; public function Command():void { } } import flash.events.Event; class DungeonEvent extends Event { public static const NEXT_FLOOR:String = "next_floor"; public function DungeonEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false):void { super(type, bubbles, cancelable); } } class DungeonObject { public function get symbol():String { return _symbol; } protected var _symbol:String; public function set symbol(symbol:String):void { _symbol = symbol; } public function DungeoObject():void { } } class Wall extends DungeonObject { public static const TOP:String = "top"; public static const BOTTOM:String = "bottom"; public static const LEFT:String = "left"; public static const RIGHT:String = "right"; public function get type():String { return _type; } private var _type:String; public function set type(type:String):void { _type = type; } override public function get symbol():String { if(_type == TOP || _type == BOTTOM) return "-"; else if(_type == LEFT || _type == RIGHT) return "|"; else return " "; } public function Wall(type:String):void { _type = type; } } class Road extends DungeonObject { public function Road():void { } } class Door extends DungeonObject { public function Door():void { _symbol = "+"; } } class Ground extends DungeonObject { public function Ground():void { } } import flash.geom.Point; class Character extends DungeonObject { public function get position():Point { return _position; } protected var _position:Point; public function set position(position:Point):void { _position = position; } public function get hitpoint():int { return _hitpoint; } protected var _hitpoint:int public function set hitpoint(hitpoint:int):void { _hitpoint = hitpoint; } public function get items():Array { return _items; } protected var _items:Array public function set items(items:Array):void { _items = items; } public function get level():int { return _level; } protected var _level:int public function get exp():int { return _exp; } protected var _exp:int; public function addExp(exp:int):void { _exp += exp; } public function Character():void { } public function protec():void { _hitpoint--; } } import flash.geom.Point; class User extends Character { public function User():void { _level = 1; _exp = 0; _items = [":", ")", "[", "!", "?", "="] } } import flash.geom.Point; class Enemy extends Character { public function Enemy():void { _level = 0; _exp = 0; _items = []; } } class Stairs extends DungeonObject { public function Stairs():void { _symbol = "%"; } } 不思議のダンジョン(ダンジョン自動生成&移動&戦闘)