RPG Test
移動/選択:カーソルキー
アクション:スペースキー
画像素材は First Seed Material 様より拝借
http://www.tekepon.net/fsm
/**
* Copyright hycro ( http://wonderfl.net/user/hycro )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/vq8H
*/
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.text.TextField;
import net.hires.debug.Stats;
[SWF(width=465, height=465, backgroundColor=0xFFFFFF, frameRate=30)]
public class RPGTest extends Sprite
{
private var _scene:GameScene;
private var _tf:TextField;
public function RPGTest()
{
Global.setup(stage);
_tf = new TextField;
_tf.text = "Loading...";
addChild(_tf);
// 静的情報の読み込み
LevelInfo.initialize(staticData);
Item.initialize(staticData);
Equipment.initialize(staticData);
// シーン情報の読み込み
_scene = new GameScene(SceneData);
_scene.addEventListener(Event.INIT, sceneInitHandler);
_scene.load();
}
private function sceneInitHandler(evt:Event):void
{
removeChild(_tf);
addChild(_scene.view);
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
//addChild(new Stats);
}
private function enterFrameHandler(evt:Event):void
{
_scene.update();
}
}
}
// ---------------------------------------------------------------------------
// シーン情報
const SceneData:XML =
<scene>
<!-- リソースファイル -->
<!-- 画像は First Seed Material 様(http://www.tekepon.net/fsm)で配付されている物を使用しています。別の用途での2次利用はご遠慮ください。念のため。-->
<resource root="http://hycro.crz.jp/wonderfl/rpg/">
<image id="1001" type="MapChip" url="img/map/TileA4.png"/>
<image id="2001" type="Object" url="img/obj/TileB.png"/>
<image id="3001" type="Charactor" url="img/chara/vx_chara01_a.png"/>
</resource>
<!-- マップチップの定義 -->
<mapchip>
<item code="1" name="Floor1" resourceid="1001" walkable="Yes" x="0" y="2"/>
<item code="2" name="Floor1" resourceid="1001" walkable="Yes" x="1" y="2"/>
<item code="3" name="Floor1" resourceid="1001" walkable="Yes" x="2" y="2"/>
<item code="4" name="Floor1" resourceid="1001" walkable="Yes" x="0" y="3"/>
<item code="5" name="Floor1" resourceid="1001" walkable="Yes" x="1" y="3"/>
<item code="6" name="Floor1" resourceid="1001" walkable="Yes" x="2" y="3"/>
<item code="7" name="Floor1" resourceid="1001" walkable="No" x="0" y="5"/>
<item code="8" name="Floor1" resourceid="1001" walkable="No" x="1" y="5"/>
<item code="9" name="Floor1" resourceid="1001" walkable="No" x="2" y="5"/>
<item code="0" name="Floor1" resourceid="1001" walkable="No" x="0" y="6"/>
<item code="t" name="Tree" resourceid="1001" walkable="No" x="9" y="5"/>
<item code="r" name="Tree" resourceid="1001" walkable="No" x="8" y="5"/>
<item code="y" name="Tree" resourceid="1001" walkable="No" x="10" y="5"/>
<item code="s" name="Tree" resourceid="1001" walkable="No" x="9" y="3"/>
<item code="a" name="Tree" resourceid="1001" walkable="No" x="8" y="3"/>
<item code="d" name="Tree" resourceid="1001" walkable="No" x="10" y="3"/>
<item code="x" name="Tree" resourceid="1001" walkable="No" x="9" y="7"/>
<item code="z" name="Tree" resourceid="1001" walkable="No" x="8" y="7"/>
<item code="c" name="Tree" resourceid="1001" walkable="No" x="10" y="7"/>
<item code="t" name="Tree" resourceid="1001" walkable="No" x="9" y="5"/>
<item code=" " name="Floor1" resourceid="1001" walkable="Yes" x="1" y="3"/>
</mapchip>
<!-- プレイヤー -->
<player resourceid="3001" posx="7" posy="9" direction="Front"/>
<!-- その他のキャラクター -->
<charactor name="村人A" resourceid="3001" resourcex="1" resourcey="1" posx="9" posy="5" type="Random" direction="Right">
<action id="1" type="Talk">「こんにちわ」</action>
</charactor>
<charactor name="村人B" resourceid="3001" resourcex="3" resourcey="1" posx="10" posy="10" type="Random" direction="Back">
<action id="1" type="Talk" next="2">「寒くなってきましたね」</action>
<action id="2" type="Talk" next="1">「カゼには気をつけてください」</action>
<action id="3" type="Talk">ここは実行されないはず</action>
</charactor>
<charactor name="村人C" resourceid="3001" resourcex="3" resourcey="0" posx="12" posy="4" type="Random" direction="Front">
<action id="1" type="Talk" next="2">「・・・」</action>
<action id="2" type="Message">返事がない。ただのモブキャラのようだ。</action>
</charactor>
<charactor name="村人D" resourceid="3001" resourcex="2" resourcey="0" posx="4" posy="14" type="Random" direction="Back">
<action id="1" type="Talk">
「あ
・
・
・
・
・
・
・
・
・
・
・
・
・
・
・
・
・
・
いえ、なんでもないです」
</action>
</charactor>
<charactor name="村人E" resourceid="3001" resourcex="0" resourcey="1" posx="2" posy="5" type="Random" direction="Left">
<action id="1" type="Talk">「先は長いです」</action>
</charactor>
<!-- オブジェクト -->
<object name="Box" type="Static" resourceid="2001" resourcex="1" resourcey="6" posx="7" posy="8" >
<action id="1" type="Check" chain="2">画面は開発中のものです。</action>
<action id="2" type="YesNo" yes="3">まだ続きがあります。読みますか?</action>
<action id="3" type="Message">製品とは異なる場合がありますのであらかじめご了承ください。</action>
</object>
<object name="Barrel" type="Static" resourceid="2001" resourcex="14" resourcey="1" posx="8" posy="8" >
<action id="1" type="Check" chain="2">樽だ。</action>
<action id="2" type="YesNo" yes="3" no="4">調べますか?</action>
<action id="3" type="GetItem" itemid="1" flagid="1" flagng="5">$(itemname)を見つけた。</action>
<action id="4" type="YesNo" yes="3" no="4">そうは言わずに調べてみませんか?</action>
<action id="5" type="Message">何も入っていない。</action>
</object>
<!-- マップ構成 -->
<map>
assssssssssssssd
rtttttttttttttty
zxxxxxxxxxxxxxxc
1222222222222223
4 6
4 6
4 6
4 6
4 6
4 6
4 6
4 6
4 6
4 6
4 6
4 6
4 6
4 6
7888888888888889
0000000000000000
0000000000000000
</map>
</scene>;
// 色々な情報(ほぼ未使用)
var staticData:XML =
<data>
<!-- レベル情報 -->
<level_info>
<info level="1" exp="0" maxhp="16" maxmp="0" power="5" stamina="4" speed="4" intellect="3" />
<info level="2" exp="10" maxhp="24" maxmp="6" power="8" stamina="9" speed="8" intellect="6" newMagic="1"/>
<info level="3" exp="20" maxhp="34" maxmp="9" power="11" stamina="12" speed="10" intellect="9" newMagic="2"/>
<info level="4" exp="30" maxhp="47" maxmp="17" power="16" stamina="19" speed="13" intellect="13" />
<info level="5" exp="40" maxhp="52" maxmp="23" power="19" stamina="21" speed="17" intellect="16" />
</level_info>
<!-- 魔法 -->
<magic id="1" name="ファイアボール" type="Attack" value="30" cost="2" target="Single" />
<magic id="2" name="ヒーリングα" type="Recover" value="30" cost="3" target="Single" />
<magic id="3" name="サンダーストーム" type="Attack" value="60" cost="8" target="All" />
<magic id="4" name="ヒーリングβ" type="Recover" value="80" cost="6" target="Single" />
<!-- アイテム -->
<item id="1" name="薬草" type="Recover" value="20" cost="5" />
<!-- 装備品 -->
<equip id="1" name="ロングソード" type="Weapon" attack="10" defense="0" cost="100" />
<equip id="2" name="革の帽子" type="Head" attack="0" defense="2" cost="30" />
<equip id="3" name="布の服" type="Body" attack="0" defense="5" cost="75" />
</data>;
// マップチップの大きさ
const SIZE:Number = 32;
const SIZE_HALF:Number = 16;
const CHARA_W:Number = 32;
const CHARA_H:Number = 48;
import caurina.transitions.Tweener;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.display.Sprite;
import flash.display.Stage;
import flash.errors.IllegalOperationError;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.KeyboardEvent;
import flash.events.TimerEvent;
import flash.filters.DropShadowFilter;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.net.URLRequest;
import flash.system.LoaderContext;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import flash.ui.Keyboard;
import flash.utils.Dictionary;
import flash.utils.Timer;
/**
* マップ
*/
class Map {
private var _data:Vector.<MapChip>;
private var _width:uint;
private var _height:uint;
public function Map(xml:XML = null){
if (xml) {
load(xml);
}
}
/**
* マップデータを読み込みます。
*/
public function load(sceneData:XML):void
{
_data = new Vector.<MapChip>;
// マップチップの定義を読み込む
MapChip.initialize(sceneData);
// 入力データのタブ文字を削除し、改行文字で区切る
var lines:Array = sceneData.map.toString().replace(/\t/g, "").split("\n");
var line:String;
// マップの幅を取得
_width = 0;
for each (line in lines) {
if (_width < line.length) {
_width = line.length;
}
}
// マップの高さを取得
_height = lines.length;
// マップを null で初期化
var mapLength:uint = _width * _height;
for (var i:uint = 0; i < mapLength; i++) {
_data.push(null);
}
// マップ構成の読み込み
for (var y:uint = 0; y < _height; y++) {
line = lines[y];
for (var x:uint = 0; x < line.length; x++) {
_data[x + _width * y] = MapChip.fromCode(line.charAt(x));
}
}
}
/**
* 指定位置のオブジェクトを取得します。
*/
public function getData(x:uint, y:uint):MapChip {
return _data[x + _width * y];
}
/**
* 指定位置のデータを設定します。
*/
public function setData(x:uint, y:uint, data:MapChip):void
{
_data[x + _width * y] = data;
}
// ------------------------------
// Getter / Setter
// ------------------------------
public function get width():uint
{
return _width;
}
public function get height():uint
{
return _height;
}
}
// ---------------------------------------------------------------------------
/**
* マップチップ
*/
class MapChip {
// 内部からのみコンストラクタを呼び出すために使用
private static const CONSTRUCT_KEY:Object = {};
// マップ定義XMLに記載される文字からマップチップへ変換するための連想配列
private static var chipDictionary:Dictionary;
// 定義されているマップチップのリスト
private static var chipList:Vector.<MapChip>;
private var _code:String; // マップ定義XMLに記載される文字
private var _name:String; // マップチップの名前(未使用)
private var _walkable:Boolean; // キャラクターが歩ける場所か
private var _resourceId:uint; // 使用する画像のID
private var _rect:Rectangle; // 画像から切り出す領域
/**
* コンストラクタ (外部からの呼び出しは不可)
*/
/*private*/ function MapChip(key:Object,
code:String,
name:String,
walkable:Boolean,
resourceId:uint,
x:uint,
y:uint)
{
if (key != CONSTRUCT_KEY) {
throw new IllegalOperationError("MapChip cannot be instantiated.");
}
_code = code;
_name = name;
_walkable = walkable;
_resourceId = resourceId;
_rect = new Rectangle(x*SIZE_HALF, y*SIZE_HALF, SIZE, SIZE);
}
/**
* シーン定義XMLを読み込み初期化します。
*
*/
public static function initialize(scene:XML):void
{
chipDictionary = new Dictionary();
chipList = new Vector.<MapChip>();
for each (var item:XML in scene.mapchip.item) {
var chip:MapChip = new MapChip(CONSTRUCT_KEY,
item.@code,
item.@name,
item.@walkable == "Yes",
item.@resourceid,
item.@x,
item.@y);
chipDictionary[(item.@code).toString()] = chip;
chipList.push(chip);
}
}
/**
* マップ定義の文字からマップチップを取得します。
*/
public static function fromCode(code:String):MapChip
{
return chipDictionary[code];
}
/**
* 定義されているマップチップのリストを取得します。
*/
public static function getAllChip():Vector.<MapChip>
{
return chipList;
}
// ------------------------------
// Getter / Setter
// ------------------------------
public function get name():String
{
return _name;
}
public function get resourceId():uint
{
return _resourceId;
}
public function get rect():Rectangle
{
return _rect;
}
public function get walkable():Boolean
{
return _walkable;
}
}
// ---------------------------------------------------------------------------
/**
* オブジェクト
*/
class GameObject extends EventDispatcher
{
protected var _name:String;
protected var _resourceId:uint;
protected var _resourceX:uint;
protected var _resourceY:uint;
protected var _x:uint;
protected var _y:uint;
protected var _rect:Rectangle;
private var _actionList:Vector.<GameAction>;
private var _nextActionId:uint;
/**
* コンストラクタ
*/
public function GameObject(name:String,
resourceId:uint,
resourceX:uint,
resourceY:uint,
x:uint,
y:uint)
{
_name = name;
_resourceId = resourceId;
_resourceX = resourceX;
_resourceY = resourceY;
_x = x;
_y = y;
_rect = new Rectangle(resourceX*SIZE, resourceY*SIZE, SIZE, SIZE);
_actionList = new Vector.<GameAction>();
_nextActionId = 1;
}
/**
* アクションを追加します。
*/
public function addAction(event:GameAction):void
{
_actionList.push(event);
}
/**
* 次のアクションを取得します。
*/
public function getNextAction():GameAction
{
if (_actionList.length == 0) {
return null;
}
return getAction(_nextActionId);
}
/**
* 次のアクションを設定します。
*/
public function setNextAction(id:uint):void
{
_nextActionId = id;
}
/**
* IDで指定されたアクションを取得します。
*/
public function getAction(id:uint):GameAction
{
for each (var action:GameAction in _actionList) {
if (action.id == id) {
return action;
}
}
return null;
}
// ------------------------------
// Getter / Setter
// ------------------------------
public function get x():uint
{
return _x;
}
public function get y():uint
{
return _y;
}
public function get rect():Rectangle
{
return _rect;
}
public function get resourceId():uint
{
return _resourceId;
}
}
// ---------------------------------------------------------------------------
/**
* キャラクター
*/
class Charactor extends GameObject
{
public static const TYPE_RANDOM:String = "Random";
public static const TYPE_STATIC:String = "Static";
public static const FRONT:String = "Front";
public static const BACK:String = "Back";
public static const LEFT:String = "Left";
public static const RIGHT:String = "Right";
public static const STATE_STOP:uint = 0;
public static const STATE_MOVE:uint = 1;
public static const STATE_IDLE:uint = 2;
public static const STATE_PAUSE:uint = 3;
protected var _idleTime:uint = 30;
private var _idleCount:int;
protected var _direction:String;
protected var _scene:GameScene;
protected var _type:String;
protected var _state:uint;
protected var _aniCounter:uint;
public function Charactor(scene:GameScene,
name:String,
resourceId:uint,
resourceX:uint,
resourceY:uint,
x:uint,
y:uint,
type:String=TYPE_RANDOM,
direction:String=FRONT)
{
super(name, resourceId, resourceX, resourceY, x, y);
_scene = scene;
_type = type;
_direction = direction;
_aniCounter = 0;
_rect = new Rectangle(_resourceX*3*CHARA_W, _resourceY*4*CHARA_H, CHARA_W, CHARA_H);
switch (_type) {
case TYPE_RANDOM:
_state = STATE_IDLE;
break;
case TYPE_STATIC:
_state = STATE_STOP;
break;
default:
_state = STATE_IDLE;
break;
}
_idleCount = _idleTime;
}
/**
* キャラクターの状態の更新。
* 毎フレームこの処理が呼ばれます。
*/
public final function update():void
{
switch (_state) {
case STATE_IDLE:
if (--_idleCount <= 0) {
nextState();
if (_state == STATE_MOVE) {
dispatchEvent(new Event("move"));
} else {
_idleCount = _idleTime;
}
}
break;
case STATE_MOVE:
break;
case STATE_PAUSE:
break;
case STATE_STOP:
return;
}
updateRect();
}
/**
* 次の状態を決定します。
*/
protected function nextState():void
{
switch (_type) {
case TYPE_STATIC:
break;
case TYPE_RANDOM:
nextStateRandom();
break;
}
}
/**
* 次の状態として、上下左右への移動またはアイドル状態のいずれかをランダムに選択します。
*/
private function nextStateRandom():void
{
var seed:Number = Math.random() * 5;
if (seed < 1) {
move(BACK);
} else if (seed < 2) {
move(FRONT);
} else if (seed < 3) {
move(LEFT);
} else if (seed < 4) {
move(RIGHT);
}
}
/**
* 移動します。
*/
protected function move(dir:String):void
{
switch (dir) {
case FRONT:
if (_scene.isWalkable(_x, _y+1)) {
_y++;
_state = STATE_MOVE;
}
_direction = FRONT;
break;
case BACK:
if (_scene.isWalkable(_x, _y-1)) {
_y--;
_state = STATE_MOVE;
}
_direction = BACK;
break;
case LEFT:
if (_scene.isWalkable(_x-1, _y)) {
_x--;
_state = STATE_MOVE;
}
_direction = LEFT;
break;
case RIGHT:
if (_scene.isWalkable(_x+1, _y)) {
_x++;
_state = STATE_MOVE;
}
_direction = RIGHT;
break;
}
}
/**
* 画像から切り出す範囲を更新します。
*/
protected function updateRect():void
{
const index:Array = [1, 0, 1, 2];
_aniCounter++;
var offsetX:uint = _resourceX * CHARA_W * 3;
var offsetY:uint = _resourceY * CHARA_H * 4;
var x:uint = index[(uint(_aniCounter / 10) % 4)] * CHARA_W + offsetX;
var y:uint;
switch (_direction) {
case FRONT: y = offsetY; break;
case LEFT: y = offsetY + CHARA_H; break;
case RIGHT: y = offsetY + 2 * CHARA_H; break;
case BACK: y = offsetY + 3 * CHARA_H; break;
}
if (_rect.x != x || _rect.y != y) {
_rect.x = x;
_rect.y = y;
dispatchEvent(new Event("update"));
}
}
/**
* アイドル状態にします。
*/
public function idle():void
{
_state = STATE_IDLE;
_idleCount = _idleTime;
}
// 一時停止する直前の状態
private var prevState:uint;
/**
* 一時停止します。
*/
public function pause():void
{
if (_state != STATE_PAUSE) {
prevState = _state;
_state = STATE_PAUSE;
}
}
/**
* 一時停止を解除します。
*/
public function restart():void
{
if (_state == STATE_PAUSE) {
_state = prevState;
}
}
/**
* 指定したキャラクターの方に向きを変えます。
*/
public function lookAt(target:Charactor):void
{
var distanceX:int = target.x - _x;
var distanceY:int = target.y - _y;
if (Math.abs(distanceX) < Math.abs(distanceY)) {
if (0 < distanceY) {
_direction = FRONT;
} else {
_direction = BACK;
}
} else if (Math.abs(distanceX) >= Math.abs(distanceY)) {
if (0 < distanceX) {
_direction = RIGHT;
} else {
_direction = LEFT;
}
}
updateRect();
}
}
// ---------------------------------------------------------------------------
/**
* プレイヤー
*/
class Player extends Charactor
{
// ステータス -------------
private var _status:LevelInfo;
private var _exp:int;
private var _hp:int;
private var _mp:int;
// 装備 -------------
private var _weapon:Equipment = Equipment.Empty;
private var _head:Equipment = Equipment.Empty;
private var _body:Equipment = Equipment.Empty;
private var _etc:Equipment = Equipment.Empty;
private var _itemList:Vector.<Item>;
private var _magicList:Vector.<Magic>;
public function Player(scene:GameScene, resourceId:uint, resourceX:uint, resourceY:uint, posX:uint, posY:uint)
{
super(scene, "主人公", resourceId, resourceX, resourceY, posX, posY);
_idleTime = 0;
_itemList = new Vector.<Item>();
_magicList = new Vector.<Magic>();
}
protected override function nextState():void
{
if (Global.isKeyPressed(Keyboard.UP)) {
move(Charactor.BACK);
} else if (Global.isKeyPressed(Keyboard.DOWN)) {
move(Charactor.FRONT);
} else if (Global.isKeyPressed(Keyboard.LEFT)) {
move(Charactor.LEFT);
} else if (Global.isKeyPressed(Keyboard.RIGHT)) {
move(Charactor.RIGHT);
}
}
/**
* プレイヤーの向いている方向の一つ先の位置を取得します。
*/
public function getActionPoint():Point
{
var point:Point = new Point(_x, _y);
switch (_direction) {
case BACK:
point.y--;
break;
case FRONT:
point.y++;
break;
case LEFT:
point.x--;
break;
case RIGHT:
point.x++;
break;
}
return point;
}
/**
* アイテムを追加します。
*/
public function addItem(item:Item):void
{
_itemList.push(item);
}
/**
* 経験値を追加します。
*
* @return レベルアップの有無
*/
public function addExp(value:int):Boolean
{
_exp += value;
var st:LevelInfo = LevelInfo.fromExp(_exp);
if (_status.level != st.level) {
_status = st;
if (_status.newMagic) {
_magicList.push(Magic.fromId(_status.newMagic));
}
return true;
}
return false;
}
public function get attack():int
{
return _status.power +
_weapon.attackPerformance +
_head.attackPerformance +
_body.attackPerformance +
_etc.attackPerformance;
}
public function get defense():int
{
return _status.stamina +
_weapon.defensePerformance +
_head.defensePerformance +
_body.defensePerformance +
_etc.defensePerformance;
}
}
// レベル情報
class LevelInfo
{
public var level:int;
public var maxHp:int;
public var maxMp:int;
public var power:int;
public var stamina:int;
public var speed:int;
public var intellect:int;
public var newMagic:int;
private var _exp:int;
private static var _instanceList:Vector.<LevelInfo>;
public static function initialize(data:XML):void
{
_instanceList = new Vector.<LevelInfo>;
for each (var info:XML in data.level_info) {
var levelInfo:LevelInfo = new LevelInfo;
levelInfo._exp = parseInt(info.@exp);
levelInfo.level = parseInt(info.@level);
levelInfo.maxHp = parseInt(info.@maxhp);
levelInfo.maxMp = parseInt(info.@maxmp);
levelInfo.power = parseInt(info.@power);
levelInfo.stamina = parseInt(info.@stamina);
levelInfo.speed = parseInt(info.@speed);
levelInfo.intellect = parseInt(info.@intellect);
levelInfo.newMagic = parseInt(info.@newMagic);
}
_instanceList.sort(function(a:LevelInfo, b:LevelInfo):int {
if (a.level < b.level) {
return -1;
} else if (a.level > b.level) {
return 1;
}
return 0;
});
}
public static function fromExp(exp:int):LevelInfo
{
for (var i:uint = 1; i < _instanceList.length; i++) {
if (exp < _instanceList[i]._exp) {
return _instanceList[i-1];
}
}
return _instanceList[i-1];
}
}
/**
* アイテム
*/
class Item
{
protected static const CONSTRUCT_KEY:Object = {};
private static var itemList:Dictionary;
public var name:String;
/*private*/ function Item(key:Object, name:String)
{
if (key != CONSTRUCT_KEY) {
throw new IllegalOperationError("Item cannot be instantiated.");
}
this.name = name;
}
public static function initialize(data:XML):void
{
itemList = new Dictionary();
for each (var itemData:XML in data.item) {
itemList[parseInt(itemData.@id)] = new Item(CONSTRUCT_KEY, itemData.@name);
}
}
public static function fromId(id:uint):Item
{
return itemList[id];
}
}
/**
* 装備品
*/
class Equipment extends Item
{
public static const Empty:Equipment = new Equipment(CONSTRUCT_KEY, "");
private static var equipDic:Dictionary;
protected var _attackPerformance:int;
protected var _defensePerformance:int;
public function Equipment(key:Object, name:String)
{
super(key, name);
}
public function get attackPerformance():int
{
return _attackPerformance;
}
public function get defensePerformance():int
{
return _defensePerformance;
}
public static function initialize(data:XML):void
{
equipDic = new Dictionary();
for each (var equipData:XML in data.equip) {
equipDic[parseInt(equipData.@id)] = new Item(CONSTRUCT_KEY, equipData.@name);
}
}
public static function fromId(id:uint):Equipment
{
return equipDic[id];
}
}
class Magic
{
protected static const CONSTRUCT_KEY:Object = {};
public static const TYPE_ATTACK:String = "Attack";
public static const TYPE_RECOVER:String = "Recover";
public static const TARGET_SINGLE:String = "Single";
public static const TARGET_ALL:String = "All";
private static var magicDic:Dictionary;
public var name:String;
public var type:String;
public var value:int;
public var cost:int;
public var target:String;
public function Magic(key:Object, name:String, type:String, value:int, cost:int, target:String)
{
if (key != CONSTRUCT_KEY) {
throw new IllegalOperationError("Item cannot be instantiated.");
}
this.name = name;
this.type = type;
this.value = value;
this.target = target;
}
public static function initialize(data:XML):void
{
magicDic = new Dictionary();
for each (var magicData:XML in data.magic) {
var magic:Magic = new Magic(
CONSTRUCT_KEY,
magicData.@name,
magicData.@type,
parseInt(magicData.@value),
parseInt(magicData.@cost),
magicData.@target
);
magicDic[parseInt(magicData.@id)] = magic;
}
}
public static function fromId(id:int):Magic
{
return magicDic[id];
}
}
// ---------------------------------------------------------------------------
class GameAction
{
public static const TYPE_TALK:String = "Talk";
public static const TYPE_CHECK:String = "Check";
public static const TYPE_YESNO:String = "YesNo";
public static const TYPE_GETITEM:String = "GetItem";
public static const TYPE_MESSAGE:String = "Message";
private var _id:uint;
private var _type:String;
private var _next:uint;
private var _chain:uint;
private var _value:String;
private var _yes:uint;
private var _no:uint;
private var _itemId:uint;
private var _flagId:uint;
private var _flagNg:uint;
public function GameAction(data:XML)
{
_id = parseInt(data.@id);
_type = data.@type;
_chain = parseInt(data.@chain);
_next = parseInt(data.@next);
_yes = parseInt(data.@yes);
_no = parseInt(data.@no);
_itemId = parseInt(data.@itemid);
_flagId = parseInt(data.@flagid);
_flagNg = parseInt(data.@flagng);
_value = data.toString();
}
public function get type():String
{
return _type;
}
public function get value():String
{
return _value;
}
public function get id():uint
{
return _id;
}
public function get chain():uint
{
return _chain;
}
public function get yes():uint
{
return _yes;
}
public function get no():uint
{
return _no;
}
public function get next():uint
{
return _next;
}
public function get itemId():uint
{
return _itemId;
}
public function get flagId():uint
{
return _flagId;
}
public function get flagNg():uint
{
return _flagNg;
}
}
/**
* フラグ管理
*/
class FlagManager
{
private static var instance:FlagManager;
private static const CONSTRUCT_KEY:Object = {};
private var _dic:Dictionary;
/*private*/ function FlagManager(key:Object)
{
if (key != CONSTRUCT_KEY) {
throw new IllegalOperationError("FlagManager cannot be instantiated.");
}
_dic = new Dictionary();
}
public function set(id:uint, value:Boolean=true):void
{
_dic[id] = value;
}
public function get(id:uint):Boolean
{
return (_dic[id] == undefined) ? false : _dic[id] as Boolean;
}
public static function getInstance():FlagManager
{
if (instance == null) {
instance = new FlagManager(CONSTRUCT_KEY);
}
return instance;
}
}
// ===========================================================================
// view
// ===========================================================================
/**
* 画像ファイル
*/
class ImageResource {
// 全インスタンスのリスト
private static var List:Vector.<ImageResource> = new Vector.<ImageResource>();
// IDからインスタンスを取得するための連想配列
private static var Dict:Dictionary = new Dictionary();
private var _loader:Loader;
private var _bmp:BitmapData;
private var _length:uint;
private var _id:uint;
private var _type:String;
private var _url:String;
private var _ready:Boolean;
public static function inititalize():void {
for each (var g:ImageResource in List) {
g.dispose();
}
List = new Vector.<ImageResource>();
Dict = new Dictionary();
}
public static function getResource(id:uint):ImageResource
{
return Dict[id];
}
public static function isAllReady():Boolean
{
for each (var g:ImageResource in List) {
if (!g.isReady()) {
return false;
}
}
return true;
}
public function ImageResource(id:uint, type:String, url:String, length:uint=30)
{
_id = id;
_type = type;
_url = url;
_length = length;
_ready = false;
List.push(this);
Dict[id] = this;
}
public function load():void
{
_loader= new Loader();
_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadCompleteHander);
_loader.load(new URLRequest(_url), new LoaderContext(true));
}
public function isReady():Boolean
{
return _ready;
}
private function loadCompleteHander(evt:Event):void
{
_bmp = new BitmapData(_loader.width, _loader.height, true, 0x00FFFFFF);
_bmp.draw(_loader);
_loader.unload();
_loader = null;
_ready = true;
}
private function dispose():void
{
if (_bmp) {
_bmp.dispose();
_bmp = null;
}
}
public function get bmp():BitmapData
{
return _bmp;
}
}
class GameObjectContainer extends Bitmap
{
private var _data:GameObject;
private var _bmp:BitmapData;
protected static const ORIGIN:Point = new Point(0, 0);
public function GameObjectContainer(data:GameObject)
{
super(new BitmapData(SIZE, SIZE, true, 0x00FFFFFF));
_data = data;
_bmp = ImageResource.getResource(_data.resourceId).bmp;
x = _data.x * SIZE;
y = _data.y * SIZE;
update();
_data.addEventListener("update", update);
}
private function update(event:Event=null):void
{
this.bitmapData.copyPixels(_bmp, _data.rect, ORIGIN);
}
}
// ---------------------------------------------------------------------------
/**
* キャラクター描画用コンテナ
*/
class CharactorContainer extends Bitmap
{
private const OffsetY:Number = -18;
private var _data:Charactor;
private var _bmp:BitmapData;
protected static const ORIGIN:Point = new Point(0, 0);
public function CharactorContainer(data:Charactor)
{
super(new BitmapData(CHARA_W, CHARA_H, true, 0x00FFFFFF));
_data = data;
_bmp = ImageResource.getResource(_data.resourceId).bmp;
x = _data.x * SIZE;
y = _data.y * SIZE + OffsetY;
_data.addEventListener("update", update);
_data.addEventListener("move", move);
}
public function update(evt:Event):void
{
this.bitmapData.copyPixels(_bmp, _data.rect, ORIGIN);
}
public function move(evt:Event):void
{
Tweener.addTween(this, {x:_data.x * SIZE,
y:_data.y * SIZE + OffsetY,
time:.35,
transition:"linear",
onComplete:function():void{_data.idle();}
});
}
}
// ---------------------------------------------------------------------------
/**
* 画面
*/
class SceneView extends Sprite {
private var _map:Map;
private var _mapLayer:Bitmap;
private var _mapLayerData:BitmapData;
private var _objectLayer:Sprite;
private var _messageArea:MessageArea;
private var _yesNoDialog:YesNoDialog;
public function SceneView(map:Map)
{
_map = map;
_mapLayerData = new BitmapData(map.width*SIZE, map.height*SIZE, false, 0x0);
// マップの描画
for (var y:uint = 0; y < map.height; y++) {
for (var x:uint = 0; x < map.width; x++) {
var chip:MapChip = map.getData(x, y);
if (chip) {
var img:ImageResource = ImageResource.getResource(chip.resourceId);
_mapLayerData.copyPixels(img.bmp,
chip.rect,
new Point(x*SIZE, y*SIZE));
}
}
}
_mapLayer = new Bitmap(_mapLayerData);
_objectLayer = new Sprite();
_messageArea = new MessageArea();
_messageArea.visible = false;
addChild(_mapLayer);
addChild(_objectLayer);
addChild(_messageArea);
}
public function sort():void
{
var num:uint = _objectLayer.numChildren;
for (var i:uint = 0; i < num-1; i++) {
for (var j:uint = i; j < num; j++) {
if (_objectLayer.getChildAt(i).y > _objectLayer.getChildAt(j).y) {
_objectLayer.swapChildrenAt(i, j);
}
}
}
}
public function addCharactor(c:Charactor):CharactorContainer
{
var container:CharactorContainer = new CharactorContainer(c);
_objectLayer.addChild(container);
return container;
}
public function addObject(obj:GameObject):GameObjectContainer
{
var container:GameObjectContainer = new GameObjectContainer(obj);
_objectLayer.addChild(container);
return container;
}
private var _messageComplete:Function;
public function showMessageArea(text:String, onComplete:Function):void
{
var o:Point = this.globalToLocal(new Point(0, 0));
_messageArea.x = o.x;
_messageArea.y = o.y + Global.stageHeight - _messageArea.height - 15;
_messageArea.visible = true;
_messageArea.text = text;
_messageComplete = onComplete;
}
public function scrollMessageArea():void
{
if (!_messageArea.scroll()) {
_messageArea.visible = false;
if (_messageComplete != null) {
_messageComplete();
_messageComplete = null;
}
}
}
public function hideMessageArea():void
{
_messageArea.visible = false;
}
private var _yesNoComplete:Function;
public function showYesNoDialog(yesText:String, noText:String, onComplete:Function):void
{
_yesNoDialog = new YesNoDialog(yesText, noText);
var o:Point = this.globalToLocal(new Point(0, 0));
_yesNoDialog.x = o.x + Global.stageWidth - _yesNoDialog.width - 20;
_yesNoDialog.y = o.y + Global.stageHeight - _messageArea.height - _yesNoDialog.height - 20 ;
addChild(_yesNoDialog);
_yesNoComplete = onComplete;
}
public function switchYesNo():void
{
if (_yesNoDialog) {
_yesNoDialog.switchValue();
}
}
public function finishYesNo():void
{
if (_yesNoDialog) {
var value:Boolean = _yesNoDialog.yes;
removeChild(_yesNoDialog);
_yesNoDialog = null;
if (_yesNoComplete != null) {
var tmp:Function = _yesNoComplete;
_yesNoComplete = null;
tmp(value);
}
}
}
}
// テキストフィールド
class YesNoDialog extends Sprite
{
private static const selectText:String = "▷";
private var _yesText:String;
private var _noText:String;
private var _tf:TextField;
private var _yes:Boolean;
public function YesNoDialog(yesText:String="はい", noText:String="いいえ")
{
var format:TextFormat = new TextFormat("_ゴシック", 14, 0xFFFFFF);
_tf = new TextField;
_tf.autoSize = TextFieldAutoSize.LEFT;
_tf.wordWrap = true;
_tf.defaultTextFormat = format;
_tf.selectable = false;
_tf.x = 15;
_tf.y = 10;
_tf.width = 100;
_tf.height = 40;
addChild(_tf);
graphics.lineStyle(3, 0xFFFFFF);
graphics.beginFill(0x000000, 0.5);
graphics.drawRoundRect(10, 6, 120, 50, 5, 5);
graphics.endFill();
this.filters = [new DropShadowFilter(4, 90, 0, 0.5, 8.0, 8.0, 1.0, 3)];
_yesText = yesText;
_noText = noText;
_yes = true;
update();
}
private function update():void
{
if (_yes) {
_tf.text = selectText + " " + _yesText + "\n" + " " + _noText;
} else {
_tf.text = " " + _yesText + "\n" + selectText + " " + _noText;
}
}
public function switchValue():void
{
_yes = !_yes;
update();
}
public function get yes():Boolean
{
return _yes;
}
}
// テキストフィールド
class MessageArea extends Sprite
{
private static const MAX_LINE:uint = 7;
private var _lines:Array;
private var _index:int;
private var _tf:TextField;
public function MessageArea()
{
var format:TextFormat = new TextFormat("_ゴシック", 14, 0xFFFFFF);
_tf = new TextField;
_tf.autoSize = TextFieldAutoSize.LEFT;
_tf.wordWrap = true;
_tf.defaultTextFormat = format;
_tf.selectable = false;
_tf.x = 15;
_tf.y = 10;
_tf.width = Global.stageWidth - 30;
_tf.height = Global.stageHeight/3 - 20;
addChild(_tf);
graphics.lineStyle(3, 0xFFFFFF);
graphics.beginFill(0x000000, 0.5);
graphics.drawRoundRect(10, 6, Global.stageWidth-20, Global.stageHeight/3-12, 5, 5);
graphics.endFill();
this.filters = [new DropShadowFilter(4, 90, 0, 0.5, 8.0, 8.0, 1.0, 3)];
}
public function set text(value:String):void
{
_lines = value.replace(/\t/g, "").split("\n");
_index = 0;
scroll();
}
public function scroll():Boolean
{
if (_lines.length <= _index) {
return false;
}
_tf.text = "";
var count:uint = 0;
while (count++ < MAX_LINE) {
if (_lines.length <= _index) {
break;
}
_tf.appendText(_lines[_index++] + "\n");
}
return true;
}
}
// ===========================================================================
// controllers
// ===========================================================================
/**
* シーン
*/
class GameScene extends EventDispatcher {
private var _data:XML;
private var _map:Map;
private var _objList:Vector.<GameObject>;
private var _charaList:Vector.<Charactor>;
private var _player:Player;
private var _playerContainer:CharactorContainer;
private var _view:SceneView;
// ロード完了したかどうかの確認用
private var _loadTimer:Timer;
/**
* コンストラクタ
*/
public function GameScene(data:XML)
{
_data = data;
_map = new Map();
_objList = new Vector.<GameObject>();
_charaList = new Vector.<Charactor>();
}
/**
* シーンデータを読み込みます。
*/
public function load():void
{
// リソースの読み込み
var root:String = (_data.resource.@root).toString();
for each (var image:XML in _data.resource.image) {
var ir:ImageResource = new ImageResource(parseInt(image.@id),
image.@type,
root + image.@url,
parseInt(image.@length));
ir.load();
}
// リソースの読み込み完了の監視
_loadTimer = new Timer(100);
_loadTimer.addEventListener(TimerEvent.TIMER, checkImageResource);
_loadTimer.start();
}
/**
* リソースの読み込みが完了したかどうかをチェックします
*/
private function checkImageResource(evt:TimerEvent):void
{
if (ImageResource.isAllReady()) {
_loadTimer.stop();
_loadTimer.removeEventListener(TimerEvent.TIMER, checkImageResource);
_loadTimer = null;
// マップデータの読み込み
_map.load(_data);
// ビューの作成
_view = new SceneView(_map);
// キャラクターの読み込み
loadObjectAndCharactor();
// スペースキーの押下を「アクション」に対応させる
Global.setKeyDownCallback(Keyboard.SPACE, doAction);
// 初期化完了イベントを送出
dispatchEvent(new Event(Event.INIT));
}
}
private function loadObjectAndCharactor():void
{
// オブジェクトの作成
for each (var objectData:XML in _data.object) {
var obj:GameObject = new GameObject(objectData.@name,
objectData.@resourceid,
objectData.@resourcex,
objectData.@resourcey,
objectData.@posx,
objectData.@posy);
for each (var actionData:XML in objectData.action) {
obj.addAction(new GameAction(actionData));
}
_objList.push(obj);
_view.addObject(obj);
}
// キャラクターの作成
for each (var charaData:XML in _data.charactor) {
var c:Charactor = new Charactor(this,
charaData.@name,
charaData.@resourceid,
charaData.@resourcex,
charaData.@resourcey,
charaData.@posx,
charaData.@posy,
charaData.@type,
charaData.@direction);
for each (var actionData2:XML in charaData.action) {
c.addAction(new GameAction(actionData2));
}
_charaList.push(c);
_view.addCharactor(c);
}
// プレイヤーの作成
if (_data.player) {
_player = new Player(this,
_data.player.@resourceid,
_data.player.@resourcex,
_data.player.@resourcey,
_data.player.@posx,
_data.player.@posy);
_charaList.push(_player);
_playerContainer = _view.addCharactor(_player);
}
}
/**
* シーンを更新します。
* このメソッドは毎フレーム呼ばれます。
*/
public function update():void
{
// キャラクターの状態を更新
for each (var chara:Charactor in _charaList) {
chara.update();
}
// 画面スクロール
if (_player != null) {
var x:Number = _playerContainer.x - Global.stageWidth /2 + SIZE_HALF;
var y:Number = _playerContainer.y - Global.stageHeight /2 + SIZE_HALF;
_view.x = Math.max(Math.min(-x, 0), -_map.width*SIZE+Global.stageWidth);
_view.y = Math.max(Math.min(-y, 0), -_map.height*SIZE+Global.stageHeight);
}
// キャラクターなどの重なり順をソート
_view.sort();
}
/**
* 指定した位置に移動できるかどうかを調べます。
*/
public function isWalkable(x:int, y:int):Boolean
{
// 範囲チェック
if (x < 0 || _map.width <= x ||
y < 0 || _map.height <= y) {
return false;
}
// マップを確認
var chip:MapChip = _map.getData(x, y);
if (chip == null || !chip.walkable) {
return false;
}
// オブジェクトを確認
for each (var obj:GameObject in _objList) {
if (x == obj.x && y == obj.y) {
return false;
}
}
// キャラクターを確認
for each (var chara:Charactor in _charaList) {
if (x == chara.x && y == chara.y) {
return false;
}
}
return true;
}
// プレイヤーの進行方向のオブジェクトに対してアクションを起こします
public function doAction():void
{
var ap:Point = _player.getActionPoint();
var action:GameAction;
var target:GameObject;
for each (var chara:Charactor in _charaList) {
if (chara != _player && chara.x == ap.x && chara.y == ap.y) {
action = chara.getNextAction();
target = chara;
break;
}
}
if (action == null) {
for each (var obj:GameObject in _objList) {
if (obj.x == ap.x && obj.y == ap.y) {
action = obj.getNextAction();
target = obj;
break;
}
}
}
if (action) {
new ActionExecutor(target, action, this).execute();
}
}
// ------------------------------
// Getter / Setter
// ------------------------------
public function get view():SceneView
{
return _view;
}
public function get player():Player
{
return _player;
}
}
class ActionExecutor
{
private var _target:GameObject;
private var _action:GameAction;
private var _scene:GameScene;
public function ActionExecutor(target:GameObject, action:GameAction, scene:GameScene)
{
_target = target;
_action = action;
_scene = scene;
}
public function execute():void
{
if (_target is Charactor) {
Charactor(_target).pause();
}
_scene.player.pause();
switch (_action.type) {
case GameAction.TYPE_TALK:
if (_target is Charactor) {
Charactor(_target).lookAt(_scene.player);
}
case GameAction.TYPE_CHECK:
case GameAction.TYPE_MESSAGE:
_scene.view.showMessageArea(
_action.value,
this.onActionComplete
);
Global.setKeyDownCallback(Keyboard.SPACE, _scene.view.scrollMessageArea);
break;
case GameAction.TYPE_YESNO:
_scene.view.showMessageArea(
_action.value,
null
);
_scene.view.showYesNoDialog(
"はい",
"いいえ",
this.yesNoComplete
);
Global.setKeyDownCallback(Keyboard.UP, _scene.view.switchYesNo);
Global.setKeyDownCallback(Keyboard.DOWN, _scene.view.switchYesNo);
Global.setKeyDownCallback(Keyboard.SPACE, _scene.view.finishYesNo);
break;
case GameAction.TYPE_GETITEM:
var flagId:uint = _action.flagId;
if (flagId && !FlagManager.getInstance().get(flagId)) {
_scene.view.showMessageArea(
_action.value.replace("$(itemname)", Item.fromId(_action.itemId).name),
onActionComplete
);
Global.setKeyDownCallback(Keyboard.SPACE, _scene.view.scrollMessageArea);
FlagManager.getInstance().set(flagId);
} else {
executeChainedAction(_action.flagNg);
}
break;
}
}
private function onActionComplete():void
{
if (_target is Charactor) {
Charactor(_target).restart();
}
_scene.player.restart();
Global.setKeyDownCallback(Keyboard.SPACE, _scene.doAction);
// 次に実行するアクションの設定
setNextAction();
// アクションチェインの実行
executeChainedAction(_action.chain);
}
private function yesNoComplete(value:Boolean):void
{
if (_target is Charactor) {
Charactor(_target).restart();
}
_scene.player.restart();
_scene.view.hideMessageArea();
Global.setKeyDownCallback(Keyboard.UP, null);
Global.setKeyDownCallback(Keyboard.DOWN, null);
Global.setKeyDownCallback(Keyboard.SPACE, _scene.doAction);
// 次に実行するアクションの設定
setNextAction();
// アクションチェインの実行
var chain:uint = value ? _action.yes : _action.no;
executeChainedAction(chain);
}
private function executeChainedAction(id:uint):void
{
if (id) {
var chainedAction:GameAction = _target.getAction(id);
if (chainedAction) {
new ActionExecutor(_target, chainedAction, _scene).execute();
}
}
}
private function setNextAction():void
{
var next:uint = _action.next;
if (next) {
_target.setNextAction(next);
}
}
}
class Global
{
private static var _keyState:Dictionary;
private static var _keyCallback:Dictionary;
private static var _stage:Stage;
public static function setup(stage:Stage):void
{
_stage = stage;
_stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
_stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
_keyState = new Dictionary();
_keyCallback = new Dictionary(true);
}
public static function dispose():void
{
_stage.removeEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
_stage.removeEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
}
public static function isKeyPressed(keyCode:uint):Boolean
{
if (_keyState[keyCode] == true) {
return true;
}
return false;
}
public static function setKeyDownCallback(keyCode:uint, callback:Function):void
{
_keyCallback[keyCode] = callback;
}
public static function clearKeyDownCallback():void
{
_keyCallback = new Dictionary(true);
}
private static function keyDownHandler(evt:KeyboardEvent):void
{
_keyState[evt.keyCode] = true;
var callback:Function = _keyCallback[evt.keyCode] as Function;
if (callback != null) {
callback();
}
}
private static function keyUpHandler(evt:KeyboardEvent):void
{
_keyState[evt.keyCode] = false;
}
public static function get stageWidth():Number
{
return _stage.stageWidth;
}
public static function get stageHeight():Number
{
return _stage.stageHeight;
}
}