2013-09-02



// forked from o8que's 某魔法少女のオンラインゲームのインキュベータテスト
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.events.TimerEvent;
import flash.geom.Rectangle;
import flash.utils.Timer;
import net.user1.logger.Logger;
import net.user1.reactor.Reactor;
import net.wonderfl.utils.WonderflAPI;
/* import ore.orelib.assets.Artist;
import ore.orelib.commons.Assets;
import ore.orelib.commons.GeomPool;
import ore.orelib.commons.Input;
import ore.orelib.commons.Preloader;
import ore.orelib.commons.TileSheet;
import ore.orelib.commons.UnionStats;
import ore.orelib.data.Const;
import ore.orelib.scenes.IScene;
import ore.orelib.scenes.TitleScene;
*/
[SWF(width = "465", height = "465", frameRate = "30", backgroundColor = "0x000000")]
public class Main extends Sprite {
private var _reactor:Reactor;
private var _synchronizer:Timer;
private var _api:WonderflAPI;
private var _scene:IScene;
private var _timestamp:Number;

public function Main() {
// Reactorの初期設定
_reactor = new Reactor();
_reactor.getConnectionMonitor().setHeartbeatFrequency(1000);
_reactor.getConnectionMonitor().setConnectionTimeout(5000);
_reactor.getConnectionMonitor().setAutoReconnectFrequency(5000);
_reactor.getLog().setLevel(Logger.FATAL);
//_reactor.getLog().setLevel(Logger.DEBUG);

var stats:UnionStats = new UnionStats(_reactor);
stats.y = 465 - stats.height;
stats.alpha = 0.8;
stage.addChild(stats);

// Reactor接続、各アセット読み込み
var preloader:Preloader = new Preloader(this);
preloader.addReactorConnectionRequest(_reactor);
preloader.addLoaderRequest(Const.IMAGE_URL);
preloader.addFontLoaderRequest(Const.FONT);
preloader.addEventListener(Event.COMPLETE, initialize);
preloader.load();
}

private function initialize(event:Event):void {
var preloader:Preloader = event.currentTarget as Preloader;
preloader.removeEventListener(event.type, arguments.callee);

// 画像アセットの登録
var sheet:TileSheet = new TileSheet(preloader.loaders[Const.IMAGE_URL]);
Assets.images["characters"] = sheet.blit(new Rectangle(0, 0, 16, 16), 3, 6);
Assets.images["weapons"] = sheet.blit(new Rectangle(48, 0, 32, 24), 3, 4);
Assets.images["background"] = Artist.createBackground();
Assets.images["actorShadow"] = Artist.createActorShadow();
Assets.images["muzzleFlashes"] = Artist.createMuzzleFlashes(10);
Assets.images["bloods"] = Artist.createBloods(10);

// 静的クラスの初期化
Input.setup(stage);
GeomPool.setup(5);

// サーバーとの同期
synchronize();
_synchronizer = new Timer(30000, 0);
_synchronizer.addEventListener(TimerEvent.TIMER, synchronize);
_synchronizer.start();

_api = new WonderflAPI(root.loaderInfo.parameters);
_scene = new TitleScene(this);
_timestamp = _reactor.getServer().getServerTime();

addEventListener(Event.ENTER_FRAME, update);
}

private function synchronize(event:TimerEvent = null):void {
if (_reactor.isReady()) { _reactor.getServer().syncTime(); }
}

private function update(event:Event):void {
var serverTime:Number = _reactor.getServer().getServerTime();

_scene.update(serverTime, Math.max(0, serverTime - _timestamp));
_timestamp = serverTime;

Input.record();
}

public function changeScene(scene:IScene):void { _scene = scene; }
public function get reactor():Reactor { return _reactor; }
public function get api():WonderflAPI { return _api; }
}
}
//package ore.orelib.scenes {
import flash.events.IEventDispatcher;

//public
interface IScene extends IEventDispatcher {
function update(serverTime:Number, elapsedTime:int):void;
}
//}
//package ore.orelib.scenes {
import com.bit101.components.PushButton;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.filters.BevelFilter;
import flash.text.TextField;
import flash.text.TextFieldType;
import flash.text.TextFormat;
import flash.text.TextFormatAlign;
import net.user1.reactor.ReactorEvent;
import net.user1.reactor.Room;
import net.user1.reactor.RoomEvent;
import net.user1.reactor.RoomManagerEvent;
import net.user1.reactor.UpdateLevels;
/* import ore.orelib.commons.TextBuilder;
import ore.orelib.data.Const;
import ore.orelib.data.SaveData;
*/
//public
class TitleScene extends Sprite implements IScene {
private var _main:Main;
private var _nameInput:TextField;
private var _buttons:Vector.<PushButton>;

private static const NUM_ROOMS:int = 1;

public function TitleScene(main:Main) {
_main = main;
_main.addChild(this);

addChild(
new TextBuilder().align(TextBuilder.CENTER).autoSize()
.font(Const.FONT, 0, 400).fontColor(0xFFFFFF).fontSize(50)
.pos(0, 100).size(465, 132).build("全ほ連")
);

addChild(_nameInput = createNameInput());

_buttons = new Vector.<PushButton>(NUM_ROOMS, true);
for (var i:int = 0; i < NUM_ROOMS; i++) {
_buttons[i] = new PushButton(this, 182, 310 + 30 * i, "join (0)", startPlaying);
}

_main.reactor.addEventListener(ReactorEvent.READY, enableToJoin);
_main.reactor.addEventListener(ReactorEvent.CLOSE, disableToJoin);
(_main.reactor.isReady()) ? enableToJoin() : disableToJoin();
}

private function createNameInput():TextField {
var result:TextField = new TextField();
result.x = 142; result.y = 270;
result.width = 180; result.height = 20;
result.defaultTextFormat = new TextFormat("_sans", 14, 0x000000, null, null, null, null, null, TextFormatAlign.CENTER);
result.background = true; result.backgroundColor = 0xFFFFFF;
result.filters = [new BevelFilter(1, 225, 0xC0C0C0, 1, 0x404040, 1, 1, 1)];
result.maxChars = 12;
result.selectable = true;
result.type = TextFieldType.INPUT;
result.text = SaveData.instance.player.name || "魔法少女" + _main.reactor.self().getClientID() + "番";
return result;
}

private function startPlaying(event:MouseEvent):void {
SaveData.instance.player.name = _nameInput.text || "魔法少女" + _main.reactor.self().getClientID() + "番";

// 各リスナの削除
_main.reactor.removeEventListener(ReactorEvent.READY, enableToJoin);
_main.reactor.removeEventListener(ReactorEvent.CLOSE, disableToJoin);
_main.reactor.getRoomManager().stopWatchingForRooms(String(_main.api.appID) + ".game");
_main.reactor.getRoomManager().removeEventListener(RoomManagerEvent.ROOM_ADDED, roomAddedHandler);
for (var i:int = 0; i < NUM_ROOMS; i++) {
var room:Room = _main.reactor.getRoomManager().getRoom(String(_main.api.appID) + ".game." + i);
if (room) { room.removeEventListener(RoomEvent.OCCUPANT_COUNT, roomClientCountHandler); }
}

_main.removeChild(this);
_main.changeScene(new PlayingScene(_main, _buttons.indexOf(event.currentTarget as PushButton)));
}

/** サーバーと正常に接続されている場合に、ゲーム参加を可能する */
private function enableToJoin(event:ReactorEvent = null):void {
_main.reactor.getRoomManager().watchForRooms(String(_main.api.appID) + ".game");
_main.reactor.getRoomManager().addEventListener(RoomManagerEvent.ROOM_ADDED, roomAddedHandler);

for (var i:int = 0; i < NUM_ROOMS; i++) {
_buttons[i].enabled = true;
_buttons[i].label = "join (0)";
}
}

/** 新しい部屋が作成されたら監視する */
private function roomAddedHandler(event:RoomManagerEvent):void {
var room:Room = event.getRoom();
var roomID:int = int(room.getSimpleRoomID());
if (roomID >= NUM_ROOMS) { return; }

// 部屋の参加人数のみを監視する
var updateLevels:UpdateLevels = new UpdateLevels();
updateLevels.clearAll();
updateLevels.occupantCount = true;
room.addEventListener(RoomEvent.OCCUPANT_COUNT, roomClientCountHandler, false, 0, true);
room.observe(String(_main.api.apiKey), updateLevels);
}

/** 参加者人数が変わったら更新する */
private function roomClientCountHandler(event:RoomEvent):void {
for (var i:int = 0; i < NUM_ROOMS; i++) {
var room:Room = _main.reactor.getRoomManager().getRoom(String(_main.api.appID) + ".game." + i);
var numClients:int = (room) ? room.getNumOccupants() : 0;
_buttons[i].label = "join (" + numClients + ")";
}
}

/** サーバーとの接続が切れた場合に、ゲーム参加不能にする */
private function disableToJoin(event:ReactorEvent = null):void {
for (var i:int = 0; i < NUM_ROOMS; i++) {
_buttons[i].enabled = false;
_buttons[i].label = "connecting...";
}
}

public function update(serverTime:Number, elapsedTime:int):void { }
}
//}
//package ore.orelib.scenes {
import com.bit101.components.PushButton;
import flash.display.Sprite;
import flash.events.MouseEvent;
import net.user1.reactor.Room;
import net.user1.reactor.RoomSettings;
import net.user1.reactor.UpdateLevels;
/* import ore.orelib.actors.EffectManager;
import ore.orelib.actors.EnemyManager;
import ore.orelib.actors.FriendManager;
import ore.orelib.assets.PlayingView;
import ore.orelib.data.Const;
import ore.orelib.logic.Host;
import ore.orelib.logic.Player;
*/
//public
class PlayingScene extends Sprite implements IScene {
private var _main:Main;
private var _room:Room;
private var _view:PlayingView;

private var _player:Player;
private var _host:Host;
private var _friends:FriendManager;
private var _enemies:EnemyManager;
private var _effects:EffectManager;

private var _isPlaying:Boolean;

public function PlayingScene(main:Main, roomNo:int) {
_main = main;
_main.addChild(this);

var roomSettings:RoomSettings = new RoomSettings();
roomSettings.password = String(_main.api.apiKey);
_room = _main.reactor.getRoomManager().createRoom(String(_main.api.appID) + ".game." + roomNo, roomSettings);

addChild(_view = new PlayingView());
_friends = new FriendManager(_room);
_enemies = new EnemyManager(_room);
_effects = new EffectManager(_view);

_player = new Player(_room, _main.reactor.self());
_host = new Host(_room, _main.reactor);

_view.setupHUD(_player);
addChild(_view.createChatWindow(String(_main.api.appID) + ".chat." + roomNo, _main.reactor));

stage.focus = null;
_room.join(String(_main.api.apiKey), new UpdateLevels());
_room.stopObserving();
_isPlaying = true;
}

public function update(serverTime:Number, elapsedTime:int):void {
if (!_isPlaying) { return; }

_player.update(elapsedTime, _enemies, (stage.focus == null));
_host.update(serverTime, elapsedTime);
_friends.update(elapsedTime);
_enemies.update(serverTime, elapsedTime, _host.isHost);
_effects.update();

_view.update(_player, _host);

// 接続が切れるか、QBの契約ノルマが達成されたらゲームオーバー
if (!_main.reactor.isReady() || _host.numContracts >= Const.FIELD_QB_QUOTA) { onGameover(); }
}

private function onGameover():void {
_isPlaying = false;
_room.leave();

_player.removeEventListeners();
_host.removeEventListeners();
_view.removeEventListeners();
_friends.removeEventListeners();
_enemies.removeEventListeners();
_effects.removeEventListeners();

_view.setupGameoverDisplay(new PushButton(null, 182, 300, "leave", leaveWorld), _main.reactor.isReady());
}

private function leaveWorld(event:MouseEvent):void {
_main.removeChild(this);
_main.changeScene(new TitleScene(_main));
}
}
//}
//package ore.orelib.logic {
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.ui.Keyboard;
import net.user1.reactor.Attribute;
import net.user1.reactor.AttributeEvent;
import net.user1.reactor.IClient;
import net.user1.reactor.Room;
/* import ore.orelib.actors.Enemy;
import ore.orelib.actors.EnemyManager;
import ore.orelib.actors.Friend;
import ore.orelib.commons.EventManager;
import ore.orelib.commons.GeomPool;
import ore.orelib.commons.Input;
import ore.orelib.data.Const;
import ore.orelib.data.PlayerData;
import ore.orelib.data.PlayerStatus;
import ore.orelib.data.SaveData;
import ore.orelib.data.WeaponSmith;
import ore.orelib.events.ActorEvent;
import ore.orelib.events.EffectEvent;
*/
//public
class Player {
private var _room:Room;
private var _self:IClient;
// プレイヤーのステータス・装備
private var _status:PlayerStatus;
private var _equip:PlayerEquipment;
private var _hp:int;
private var _invincibleCount:int;
private var _recoverCount:int;
private var _recoverTime:int;
// プレイヤーのビュー
private var _view:Friend;
private var _playerBounds:Rectangle;
private var _posSendCount:int;
// 撃退数
private var _killCount:int;

public static const BOUNDS_HALF_WIDTH:int = 10;
public static const BOUNDS_HEIGHT:int = 28;

public function Player(room:Room, self:IClient) {
_room = room;
_self = self;

var playerData:PlayerData = SaveData.instance.player;
var primary:Weapon = WeaponSmith.createWeaponFromData(SaveData.instance.inventory[playerData.primaryWeaponIndex]);
var secondary:Weapon = WeaponSmith.createWeaponFromData(SaveData.instance.inventory[playerData.secondaryWeaponIndex]);

_status = new PlayerStatus(
Math.min(Math.max(10, playerData.status.str + primary.strBonus + secondary.strBonus), 200),
Math.min(Math.max(10, playerData.status.vit + primary.vitBonus + secondary.vitBonus), 200),
Math.min(Math.max(10, playerData.status.dex + primary.dexBonus + secondary.dexBonus), 200),
Math.min(Math.max(10, playerData.status.luc + primary.lucBonus + secondary.lucBonus), 200)
);
_equip = new PlayerEquipment(primary, secondary);
_hp = _status.vit;
_invincibleCount = Const.PLAYER_INVINCIBLE_TIME_ON_RECOVER;
_recoverCount = 0;
_recoverTime = Const.PLAYER_RECOVER_TIME * _status.vit / 100;

_view = new Friend(
playerData.name,
Const.FIELD_SIZE,
Const.FIELD_SIZE * Math.random(),
Const.PLAYER_SPEED * _status.dex / 100,
_hp,
_status.vit,
playerData.characterID,
primary.id
);
_self.setAttribute(Friend.ATTR_NAME, playerData.name);
_self.setAttribute(Friend.ATTR_X, _view.x.toFixed(0));
_self.setAttribute(Friend.ATTR_Y, _view.y.toFixed(0));
_self.setAttribute(Friend.ATTR_SPEED, _view.speed.toFixed(0));
_self.setAttribute(Friend.ATTR_HP, _hp.toString());
_self.setAttribute(Friend.ATTR_MAXHP, _status.vit.toString());
_self.setAttribute(Friend.ATTR_CHARACTER, playerData.characterID.toString());
_self.setAttribute(Friend.ATTR_WEAPON, primary.id.toString());
EventManager.instance.dispatchEvent(new ActorEvent(ActorEvent.ADD, _view, _view.overlay));
_playerBounds = new Rectangle(
_view.x - Player.BOUNDS_HALF_WIDTH,
_view.y - Player.BOUNDS_HEIGHT,
Player.BOUNDS_HALF_WIDTH * 2,
Player.BOUNDS_HEIGHT
);
_posSendCount = Const.PLAYER_POS_SEND_INTERVAL;

_killCount = 0;
_room.addEventListener(AttributeEvent.UPDATE, onKillEnemy);
}

private function onKillEnemy(event:AttributeEvent):void {
var attr:Attribute = event.getChangedAttr();
var idAndName:Array = attr.name.split(".");
if (idAndName[1] != Enemy.ATTR_HP) { return; }

if (int(attr.value) <= 0) {
_room.deleteAttribute(idAndName[0] + "." + Enemy.ATTR_STATUS);
_room.deleteAttribute(idAndName[0] + "." + Enemy.ATTR_HP);
_room.deleteAttribute(idAndName[0] + "." + Enemy.ATTR_SPAWN_X);

if (int(attr.oldValue) > 0) {
_killCount++;
}
}
}

private function changeHp(value:int):void {
if (value <= 0) {
_invincibleCount = _recoverTime;
_recoverCount = _recoverTime;
} else if (value < _hp) {
_invincibleCount = Const.PLAYER_INVINCIBLE_TIME_ON_DAMAGED;
}

_hp = Math.max(0, value);
_view.changeHp(_hp);
_self.setAttribute(Friend.ATTR_HP, _hp.toString());
}

public function update(elapsedTime:int, enemies:EnemyManager, isControllable:Boolean):void {
_equip.update(elapsedTime);

if (_invincibleCount > 0) { _invincibleCount -= elapsedTime; }
if (_recoverCount > 0) {
_recoverCount -= elapsedTime;
if (_recoverCount <= 0) {
changeHp(_status.vit);
_invincibleCount = Const.PLAYER_INVINCIBLE_TIME_ON_RECOVER;
}
}

if (isControllable) { processInput(elapsedTime, enemies); }

_view.update(elapsedTime);
_posSendCount -= elapsedTime;
if (_posSendCount < 0) {
_posSendCount += Const.PLAYER_POS_SEND_INTERVAL;
_self.setAttribute(Friend.ATTR_X, _view.x.toFixed(0));
_self.setAttribute(Friend.ATTR_Y, _view.y.toFixed(0));
}

if (_invincibleCount <= 0) {
var collidingEnemies:Vector.<Enemy> = enemies.acquireCollidingEnemies(_playerBounds);
if (collidingEnemies.length > 0) {
changeHp(_hp - collidingEnemies[0].str);
}
}
}

private function processInput(elapsedTime:int, enemies:EnemyManager):void {
if (_recoverCount > 0) { return; }
move(elapsedTime);
if (_equip.isSwapping()) { return; }
if (Input.key.isPressed(Input.C)) {
_equip.swap();
_view.swapWeapon(_equip.currentWeapon.id);
_self.setAttribute(Friend.ATTR_WEAPON, _equip.currentWeapon.id.toString());
return;
}
if (_equip.isReloading()) { return; }
if (Input.key.isPressed(Input.X) && !_equip.isAmmoFull()) {
_equip.reload(_status);
return;
}
if (Input.key.isDown(Input.Z)) {
if (_equip.isAmmoEmpty()) {
_equip.reload(_status);
} else if (_equip.canAttack()) {
var range:int = _equip.attack(_room, _view.x, _view.y, _status, enemies);
_view.attack(_equip.currentWeapon.id, range);
_room.sendMessage(Friend.MESSAGE_ATTACK, false, null, _equip.currentWeapon.id.toString(), range.toString());
EventManager.instance.dispatchEvent(new EffectEvent(
EffectEvent.ATTACK_WEAPON,
_view.x, _view.y,
{ id: _equip.currentWeapon.id, range: range }
));
}
}
}

private function move(elapsedTime:int):void {
var delta:Number = _view.speed * elapsedTime / 1000;
var velocity:Point = GeomPool.point(0, 0);

if (Input.key.isDown(Keyboard.LEFT)) { velocity.x -= delta; }
if (Input.key.isDown(Keyboard.RIGHT)) { velocity.x += delta; }
if (Input.key.isDown(Keyboard.UP)) { velocity.y -= delta; }
if (Input.key.isDown(Keyboard.DOWN)) { velocity.y += delta; }

if (velocity.x || velocity.y) {
velocity.normalize(delta);
_view.moveTo(
Math.min(Math.max(0, _view.x + velocity.x), Const.FIELD_SIZE),
Math.min(Math.max(0, _view.y + velocity.y), Const.FIELD_SIZE)
);
_playerBounds.x = _view.x - Player.BOUNDS_HALF_WIDTH;
_playerBounds.y = _view.y - Player.BOUNDS_HEIGHT;
}
}

public function removeEventListeners():void {
_room.removeEventListener(AttributeEvent.UPDATE, onKillEnemy);
}

public function get characterID():int { return int(_self.getAttribute(Friend.ATTR_CHARACTER)); }
public function get hp():int { return _hp; }
public function get maxHp():int { return _status.vit; }
public function get hpRate():Number { return _hp / _status.vit; }
public function get ammo():int { return _equip.currentWeaponAmmo; }
public function get maxAmmo():int { return _equip.currentWeapon.maxAmmo; }
public function get hpGaugeRate():Number {
return (_recoverCount > 0) ? 1 - (_recoverCount / _recoverTime) : _hp / _status.vit;
}
public function get ammoGaugeRate():Number { return _equip.ammoGaugeRate; }
}
//}
//package ore.orelib.logic {
import net.user1.reactor.Room;
/* import ore.orelib.actors.EnemyManager;
import ore.orelib.actors.Friend;
import ore.orelib.data.Const;
import ore.orelib.data.PlayerStatus;
*/
//public
class PlayerEquipment {
private var _currentWeapon:Weapon;
private var _currentWeaponAmmo:int;
private var _theOtherWeapon:Weapon;
private var _theOtherWeaponAmmo:int;

private var _attackCount:int;
private var _reloadCount:int;
private var _swapCount:int;

private var _reloadTime:int;

public function PlayerEquipment(primary:Weapon, secondary:Weapon) {
_currentWeapon = primary;
_currentWeaponAmmo = primary.maxAmmo;
_theOtherWeapon = secondary;
_theOtherWeaponAmmo = secondary.maxAmmo;

_attackCount = _reloadCount = _swapCount = 0;
_reloadTime = 1;
}

public function update(elapsedTime:int):void {
if (_attackCount > 0) {
_attackCount -= elapsedTime;
}

if (_reloadCount > 0) {
_reloadCount -= elapsedTime;
if (_reloadCount <= 0) { _currentWeaponAmmo = _currentWeapon.maxAmmo; }
}

if (_swapCount > 0) {
_swapCount -= elapsedTime;
}
}

public function attack(room:Room, x:int, y:int, status:PlayerStatus, enemies:EnemyManager):int {
// if(isAmmoEmpty()) reload()
// if(canAttack() && !isReloading() && !isSwapping())
var range:int = _currentWeapon.attack(room, x, y, status, enemies);
_currentWeaponAmmo--;
_attackCount = _currentWeapon.attackRate;
return range;
}

public function reload(status:PlayerStatus):void {
// if(!isAmmoFull() && !isReloading() && !isSwapping())
_attackCount = 0;
_reloadCount = _reloadTime = _currentWeapon.reloadRate * 100 / status.dex;
}

public function swap():void {
// if(!isSwapping())
var tempWeapon:Weapon = _currentWeapon;
var tempWeaponAmmo:int = _currentWeaponAmmo;
_currentWeapon = _theOtherWeapon;
_currentWeaponAmmo = _theOtherWeaponAmmo;
_theOtherWeapon = tempWeapon;
_theOtherWeaponAmmo = tempWeaponAmmo;

_attackCount = _reloadCount = 0;
_swapCount = Const.PLAYER_SWAP_TIME;
}

public function isAmmoEmpty():Boolean { return _currentWeaponAmmo <= 0; }
public function isAmmoFull():Boolean { return _currentWeaponAmmo == _currentWeapon.maxAmmo; }
public function canAttack():Boolean { return _attackCount <= 0; }
public function isReloading():Boolean { return _reloadCount > 0; }
public function isSwapping():Boolean { return _swapCount > 0; }

public function get currentWeapon():Weapon { return _currentWeapon; }
public function get currentWeaponAmmo():int { return _currentWeaponAmmo; }
public function get ammoGaugeRate():Number {
return (_reloadCount > 0) ? 1 - (_reloadCount / _reloadTime) : _currentWeaponAmmo / _currentWeapon.maxAmmo;
}
}
//}
//package ore.orelib.logic {
import flash.geom.Rectangle;
import net.user1.reactor.Room;
/* import ore.orelib.actors.Enemy;
import ore.orelib.actors.EnemyManager;
import ore.orelib.data.Const;
import ore.orelib.data.PlayerStatus;
import ore.orelib.data.WeaponSmith;
*/
//public
class Weapon {
private var _id:int;
private var _name:String;
private var _buffs:String;

private var _minDamage:int;
private var _randDamage:int;
private var _attackRate:int;
private var _reloadRate:int;
private var _ammo:int;
private var _criticalRate:int;
private var _knockback:int;
private var _bonus:PlayerStatus;

private var _range:int;
private var _penetration:int;

public function Weapon(
id:int, name:String, buffs:String,
minDamage:int, randDamage:int,
attackRate:int, reloadRate:int, ammo:int,
criticalRate:int, knockback:int, bonus:PlayerStatus,
range:int, penetration:int
) {
_id = id;
_name = name;
_buffs = buffs;
_minDamage = minDamage;
_randDamage = randDamage;
_attackRate = attackRate;
_reloadRate = reloadRate;
_ammo = ammo;
_criticalRate = criticalRate;
_knockback = knockback;
_bonus = bonus;
_range = range;
_penetration = penetration;
}

public function attack(room:Room, x:int, y:int, status:PlayerStatus, enemies:EnemyManager):int {
var attackType:int = WeaponSmith.acquireAttackTypeOf(_id);
var bounds:Rectangle;
var collidingEnemies:Vector.<Enemy>;
var i:int, enemy:Enemy, collidingEnemiesLength:int;
var range:int = 0;

switch(attackType) {
case Const.ATTACKTYPE_SHOOTING:
{
bounds = new Rectangle(x - 508, y - 16, 500, 1);
collidingEnemies = enemies.acquireCollidingEnemies(bounds);
collidingEnemiesLength = collidingEnemies.length;
range = 500;
for (i = 0; i < collidingEnemiesLength; i++) {
enemy = collidingEnemies[i];
// 敵との距離に応じてダメージを減衰させる
var distX:int = x - 8 - collidingEnemies[i].x;
var distMultiplier:Number = (distX < _range) ? 1
: (distX < _range * 2) ? 1 - 0.9 * (distX - _range) / _range
: 0.1;
collidingEnemies[i].damaged(room, calculateDamage(status, distMultiplier), _knockback);
// 貫通しなかったら終了
if (i >= _penetration) { range = distX; break; }
}
break;
}

case Const.ATTACKTYPE_EXPLOSIVE:
{
bounds = new Rectangle(x - 508, y - 16, 500, 1);
collidingEnemies = enemies.acquireCollidingEnemies(bounds);
range = 500;
if (collidingEnemies.length == 0) { break; }
// 一番先頭の敵の頭を爆心地にする
enemy = collidingEnemies[0];
var halfRange:Number = _range / 2;
bounds = new Rectangle(enemy.x + 16 - halfRange, y - 16 - halfRange, _range, _range);
collidingEnemies = enemies.acquireCollidingEnemies(bounds);
collidingEnemiesLength = collidingEnemies.length;
range = (x - 8) - (enemy.x + 16);
for (i = 0; i < collidingEnemiesLength; i++) {
collidingEnemies[i].damaged(room, calculateDamage(status), _knockback);
}
break;
}

case Const.ATTACKTYPE_MELEE:
{
bounds = new Rectangle(x - 8 - _range, y - 40, _range, 40);
collidingEnemies = enemies.acquireCollidingEnemies(bounds);
collidingEnemiesLength = collidingEnemies.length;
range = _range;
for (i = 0; i < collidingEnemiesLength; i++) {
collidingEnemies[i].damaged(room, calculateDamage(status), _knockback);
}
break;
}

default: { break; }
}

return range;
}

private function calculateDamage(status:PlayerStatus, distMultiplier:Number = 1):int {
var baseDamage:int = _minDamage + int(_randDamage * distMultiplier * Math.random());
var crits:Boolean = (int(100 * Math.random()) < _criticalRate * (status.luc / 100));
return baseDamage * (status.str / 100) * ((crits) ? 10 : 1);
}

public function get id():int { return _id; }
public function get name():String { return _name; }
public function get buffs():String { return _buffs; }
public function get minDamage():int { return _minDamage; }
public function get randDamage():int { return _randDamage; }
public function get maxDamage():int { return _minDamage + _randDamage; }
public function get attackRate():int { return _attackRate; }
public function get reloadRate():int { return _reloadRate; }
public function get maxAmmo():int { return _ammo; }
public function get criticalRate():int { return _criticalRate; }
public function get knockback():int { return _knockback; }
public function get strBonus():int { return _bonus.str; }
public function get vitBonus():int { return _bonus.vit; }
public function get dexBonus():int { return _bonus.dex; }
public function get lucBonus():int { return _bonus.luc; }
public function get range():int { return _range; }
public function get penetration():int { return _penetration; }
}
//}
//package ore.orelib.logic {
import net.user1.reactor.Attribute;
import net.user1.reactor.AttributeEvent;
import net.user1.reactor.Reactor;
import net.user1.reactor.Room;
import net.user1.reactor.RoomEvent;
import net.user1.reactor.RoomManagerEvent;
import net.user1.reactor.Status;
/* import ore.orelib.actors.Enemy;
import ore.orelib.data.Const;
*/
//public
class Host {
private var _room:Room;
private var _reactor:Reactor;
private var _startTime:Number;
private var _playTime:int;
private var _spawnTimeCount:int;
private var _spawnUniqueCount:int;

public static const ATTR_HOST:String = "h";
public static const ATTR_HOST_CANDIDACY:String = "i";
public static const ATTR_START_TIME:String = "t";
public static const ATTR_NUM_CONTRACTS:String = "c";

public function Host(room:Room, reactor:Reactor) {
_room = room;
_reactor = reactor;
_startTime = _playTime = 0;
_spawnTimeCount = _spawnUniqueCount = 0;

_reactor.getRoomManager().addEventListener(RoomManagerEvent.CREATE_ROOM_RESULT, initialize);
_room.addEventListener(RoomEvent.REMOVE_OCCUPANT, removeOccupantHandler);
_room.addEventListener(AttributeEvent.UPDATE, attributeUpdateHandler);
_room.addEventListener(AttributeEvent.DELETE, attributeDeleteHandler);
}

private function initialize(event:RoomManagerEvent):void {
if (event.getRoomID() != _room.getRoomID()) { return; }
_reactor.getRoomManager().removeEventListener(RoomManagerEvent.CREATE_ROOM_RESULT, initialize);
if(event.getStatus() != Status.SUCCESS) { return; }

_room.setAttribute(Host.ATTR_HOST, _reactor.self().getClientID());
_room.setAttribute(Host.ATTR_START_TIME, _reactor.getServer().getServerTime().toString());
_room.setAttribute(Host.ATTR_NUM_CONTRACTS, "0");
}

private function removeOccupantHandler(event:RoomEvent):void {
/**
* 1. 退室者が出た際、ホストがいないなら、ホストに立候補する
* 2. 立候補には退室者IDを送信する
* 3. attributeUpdateHandlerで、最初に立候補したのが自分なら自分がホストになる
*/
if (!_room.clientIsInRoom(_room.getAttribute(Host.ATTR_HOST))) {
_room.setAttribute(Host.ATTR_HOST_CANDIDACY, event.getClientID());
}
}

private function attributeUpdateHandler(event:AttributeEvent):void {
var attr:Attribute = event.getChangedAttr();

switch(attr.name) {
case Host.ATTR_START_TIME:
{
_startTime = Number(attr.value);
break;
}

// 最初に立候補したのが自分なら自分がホストになる
case Host.ATTR_HOST_CANDIDACY:
{
if (attr.byClient && attr.byClient.isSelf()) {
_room.setAttribute(Host.ATTR_HOST, _reactor.self().getClientID());
}
break;
}

default: { break; }
}
}

private function attributeDeleteHandler(event:AttributeEvent):void {
// 自分がホストでないなら終了
if (!isHost) { return; }

var attr:Attribute = event.getChangedAttr();
var idAndProp:Array = attr.name.split(".");
// HPがある状態で削除(契約成立)なら、契約数を増やす
if (idAndProp[1] == Enemy.ATTR_HP && int(attr.oldValue) > 0) {
_room.setAttribute(Host.ATTR_NUM_CONTRACTS, "%v+1", true, false, true);
}
}

public function update(serverTime:Number, elapsedTime:int):void {
if (_startTime > 0) { _playTime = serverTime - _startTime; }
// 自分がホストでないなら終了
if (!isHost) { return; }

// 敵の生成
_spawnTimeCount += elapsedTime;
var spawnTime:int = Math.max(50, 1500 - 150 * wave);
if (_spawnTimeCount > spawnTime) {
_spawnTimeCount -= spawnTime;
var enemyID:String = _reactor.self().getClientID() + ("000" + _spawnUniqueCount).substr(-3);
_spawnUniqueCount++;
Enemy.spawn(_room, enemyID, 100, 10, 40, serverTime);
}
}

public function removeEventListeners():void {
_room.removeEventListener(RoomEvent.REMOVE_OCCUPANT, removeOccupantHandler);
_room.removeEventListener(AttributeEvent.UPDATE, attributeUpdateHandler);
_room.removeEventListener(AttributeEvent.DELETE, attributeDeleteHandler);
}

public function get isHost():Boolean { return _room.getAttribute(Host.ATTR_HOST) == _reactor.self().getClientID(); }
public function get wave():int { return (_startTime > 0) ? int(_playTime / Const.FIELD_WAVE_TIME) + 1 : 1; }
public function get numContracts():int { return int(_room.getAttribute(Host.ATTR_NUM_CONTRACTS)); }
}
//}
//package ore.orelib.actors {
import flash.display.BitmapData;

//public
interface IActor {
function draw(target:BitmapData):void;
function get depth():Number;
}
//}
//package ore.orelib.actors {
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.filters.GlowFilter;
import flash.geom.ColorTransform;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.text.TextField;
/* import ore.orelib.anim.ArmAnim;
import ore.orelib.anim.BodyAnim;
import ore.orelib.anim.WeaponAnim;
import ore.orelib.commons.Assets;
import ore.orelib.commons.EventManager;
import ore.orelib.commons.GeomPool;
import ore.orelib.commons.TextBuilder;
import ore.orelib.data.Const;
import ore.orelib.data.WeaponSmith;
import ore.orelib.events.EffectEvent;
*/
//public
class Friend implements IActor {
// 移動関連
private var _currentPosition:Point;
private var _destPosition:Point;
private var _speed:int;
private var _velocityPerMs:Point;
// 描画関連
private var _nameLabel:TextField;
private var _hp:int;
private var _maxHp:int;
private var _body:BodyAnim;
private var _arm:ArmAnim;
private var _weapon:WeaponAnim;
private var _worldTrans:Matrix;
private var _effectColorOffset:int;

public static const ATTR_NAME:String = "n";
public static const ATTR_X:String = "x";
public static const ATTR_Y:String = "y";
public static const ATTR_SPEED:String = "s";
public static const ATTR_HP:String = "h";
public static const ATTR_MAXHP:String = "m";
public static const ATTR_CHARACTER:String = "c";
public static const ATTR_WEAPON:String = "w";
public static const MESSAGE_ATTACK:String = "a";

public function Friend(
name:String, x:Number, y:Number, speed:int,
hp:int, maxHp:int, characterID:int, weaponID:int
) {
_currentPosition = new Point(x, y);
_destPosition = new Point(x, y);
_speed = speed;
_velocityPerMs = new Point(0, 0);

_nameLabel = new TextBuilder()
.align(TextBuilder.CENTER)
.filters([new GlowFilter(0x000000, 1, 2, 2, 8)])
.font(Const.FONT, -400, 400).fontColor(0xFFFFFF).fontSize(10)
.size(140, 20).build(name);
_hp = hp;
_maxHp = maxHp;
_body = new BodyAnim(characterID);
if (_hp <= 0) { _body.transition(BodyAnim.RECOVER); }
_arm = new ArmAnim();
_weapon = new WeaponAnim(weaponID);
_worldTrans = new Matrix();
_effectColorOffset = 0;
}

/** 目的位置を更新する */
public function moveTo(x:Number, y:Number):void {
_destPosition.x = x;
_destPosition.y = y;

var angle:Number = Math.atan2(_destPosition.y - _currentPosition.y, _destPosition.x - _currentPosition.x);
_velocityPerMs.x = _speed * Math.cos(angle) / 1000;
_velocityPerMs.y = _speed * Math.sin(angle) / 1000;
}

/** 攻撃モーションを行う */
public function attack(weaponID:int, range:int = 0):void {
var attackTypeID:int = WeaponSmith.acquireAttackTypeOf(weaponID);
_arm.transition((attackTypeID == Const.ATTACKTYPE_MELEE) ? ArmAnim.SWING : ArmAnim.RECOIL);
}

/** 武器変更モーションを行う */
public function swapWeapon(weaponID:int):void {
_arm.transition(ArmAnim.SWAP);
_weapon.transition(weaponID);
}

/** 残りHPに応じた更新を行う */
public function changeHp(value:int):void {
if (value <= 0) {
_body.transition(BodyAnim.RECOVER);
// 休憩エフェクトはここ
} else if (value < _hp) {
_effectColorOffset = 300;
EventManager.instance.dispatchEvent(new EffectEvent(
EffectEvent.DAMAGED_ACTOR,
_currentPosition.x,
_currentPosition.y,
{ num: _hp - value, isEnemy: false }
));
} else if (value > _hp) {
_body.transition(BodyAnim.IDLE);
// 復帰エフェクトはここ
}

_hp = value;
}

public function update(elapsedTime:int):void {
if (_hp > 0) {
if (!_currentPosition.equals(_destPosition)) {
var diff:Point = GeomPool.point(_destPosition.x - _currentPosition.x, _destPosition.y - _currentPosition.y);
var delta:Point = GeomPool.point(_velocityPerMs.x * elapsedTime, _velocityPerMs.y * elapsedTime);

if ((diff.x * diff.x + diff.y * diff.y) < (delta.x * delta.x + delta.y * delta.y)) {
_currentPosition.x = _destPosition.x;
_currentPosition.y = _destPosition.y;
}else {
_currentPosition.x += delta.x;
_currentPosition.y += delta.y;
}

_body.transition(BodyAnim.RUN);
}else {
_body.transition(BodyAnim.IDLE);
}
}

_worldTrans.tx = int(_currentPosition.x + Const.FIELD_OFFSET_X);
_worldTrans.ty = int(_currentPosition.y + Const.FIELD_OFFSET_Y);
_nameLabel.x = _worldTrans.tx - 70;
_nameLabel.y = _worldTrans.ty - 50;
_body.update(elapsedTime, _worldTrans);
_arm.update(elapsedTime, _body.worldTrans);
_weapon.update(elapsedTime, _arm.worldTrans);
_effectColorOffset = Math.max(0, _effectColorOffset - 60);
}

public function draw(target:BitmapData):void {
var effect:ColorTransform = GeomPool.colorTransform(1, 1, 1, 1, _effectColorOffset, _effectColorOffset, _effectColorOffset);
target.draw(Assets.images["actorShadow"], GeomPool.matrix(1, 0, 0, 1, _worldTrans.tx - 14, _worldTrans.ty - 4));
if (_hp > 0) {
_weapon.draw(target, effect);
_arm.draw(target, effect);
}
_body.draw(target, effect);
target.fillRect(GeomPool.rectangle(_worldTrans.tx - 16, _worldTrans.ty - 34, 32, 1), 0xFFCC0000);
target.fillRect(GeomPool.rectangle(_worldTrans.tx - 16, _worldTrans.ty - 34, 32 * _hp / _maxHp, 1), 0xFF00FF00);
}

public function get x():Number { return _currentPosition.x; }
public function get y():Number { return _currentPosition.y; }
public function get speed():int { return _speed; }
public function get overlay():DisplayObject { return _nameLabel; }
public function get depth():Number { return _currentPosition.y; }
}
//}
//package ore.orelib.actors {
import flash.utils.Dictionary;
import net.user1.reactor.Attribute;
import net.user1.reactor.IClient;
import net.user1.reactor.Room;
import net.user1.reactor.RoomEvent;
/* import ore.orelib.commons.EventManager;
import ore.orelib.events.ActorEvent;
import ore.orelib.events.EffectEvent;
*/
//public
class FriendManager {
private var _room:Room;
private var _friendList:Dictionary;

public function FriendManager(room:Room) {
_room = room;
_friendList = new Dictionary();

_room.addEventListener(RoomEvent.ADD_OCCUPANT, addOccupantHandler);
_room.addEventListener(RoomEvent.REMOVE_OCCUPANT, removeOccupantHandler);
_room.addEventListener(RoomEvent.UPDATE_CLIENT_ATTRIBUTE, updateClientAttrHandler);
_room.addMessageListener(Friend.MESSAGE_ATTACK, attackFriendHandler);
}

/** プレイヤーが入室した際の処理 */
private function addOccupantHandler(event:RoomEvent):void {
var client:IClient = event.getClient();
if (client.isSelf()) { return; }

var friend:Friend = new Friend(
client.getAttribute(Friend.ATTR_NAME),
Number(client.getAttribute(Friend.ATTR_X)),
Number(client.getAttribute(Friend.ATTR_Y)),
int(client.getAttribute(Friend.ATTR_SPEED)),
int(client.getAttribute(Friend.ATTR_HP)),
int(client.getAttribute(Friend.ATTR_MAXHP)),
int(client.getAttribute(Friend.ATTR_CHARACTER)),
int(client.getAttribute(Friend.ATTR_WEAPON))
);
_friendList[client.getClientID()] = friend;
EventManager.instance.dispatchEvent(new ActorEvent(ActorEvent.ADD, friend, friend.overlay));
}

/** プレイヤーが退室した際の処理 */
private function removeOccupantHandler(event:RoomEvent):void {
var friend:Friend = _friendList[event.getClientID()];
delete _friendList[event.getClientID()];
EventManager.instance.dispatchEvent(new ActorEvent(ActorEvent.REMOVE, friend, friend.overlay));
}

/** プレイヤーの属性が更新された際の処理 */
private function updateClientAttrHandler(event:RoomEvent):void {
var client:IClient = event.getClient();
if (client.isSelf()) { return; }

var friend:Friend = _friendList[client.getClientID()];
var attr:Attribute = event.getChangedAttr();
switch(attr.name) {
// 位置の更新
case Friend.ATTR_X:
case Friend.ATTR_Y:
{
friend.moveTo(
Number(client.getAttribute(Friend.ATTR_X)),
Number(client.getAttribute(Friend.ATTR_Y))
);
break;
}

// 残りHPの更新
case Friend.ATTR_HP:
{
friend.changeHp(int(attr.value));
break;
}

// 使用武器の変更
case Friend.ATTR_WEAPON:
{
friend.swapWeapon(int(attr.value));
break;
}

default: { break; }
}
}

/** プレイヤーが攻撃を行った際の処理 */
private function attackFriendHandler(from:IClient, weaponID:String, range:String):void {
var friend:Friend = _friendList[from.getClientID()];
friend.attack(int(weaponID), int(range));
EventManager.instance.dispatchEvent(new EffectEvent(
EffectEvent.ATTACK_WEAPON,
friend.x, friend.y,
{ id: int(weaponID), range: int(range) }
));
}

public function update(elapsedTime:int):void {
for each(var friend:Friend in _friendList) {
friend.update(elapsedTime);
}
}

public function removeEventListeners():void {
_room.removeEventListener(RoomEvent.ADD_OCCUPANT, addOccupantHandler);
_room.removeEventListener(RoomEvent.REMOVE_OCCUPANT, removeOccupantHandler);
_room.removeEventListener(RoomEvent.UPDATE_CLIENT_ATTRIBUTE, updateClientAttrHandler);
_room.removeMessageListener(Friend.MESSAGE_ATTACK, attackFriendHandler);
}
}
//}
//package ore.orelib.actors {
import flash.display.BitmapData;
import flash.geom.ColorTransform;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import net.user1.reactor.Room;
/* import ore.orelib.anim.BodyAnim;
import ore.orelib.commons.Assets;
import ore.orelib.commons.EventManager;
import ore.orelib.commons.GeomPool;
import ore.orelib.data.Const;
import ore.orelib.events.EffectEvent;
*/
//public
class Enemy implements IActor {
private var _id:String;
private var _hp:int;
private var _maxHp:int;
private var _str:int;
// 移動関連
private var _currentPosition:Point;
private var _vx:int;
private var _spawnPosition:Point;
private var _spawnTime:Number;
private var _bounds:Rectangle;
// 描画関連
private var _body:BodyAnim;
private var _worldTrans:Matrix;
private var _effectColorOffset:int;

public static const BOUNDS_HALF_WIDTH:int = 14;
public static const BOUNDS_HEIGHT:int = 20;

public static const ATTR_STATUS:String = "s"; // maxHp,str,spawnY,vx,spawnTime
public static const ATTR_HP:String = "h";
public static const ATTR_SPAWN_X:String = "x";

public static function spawn(room:Room, id:String, hp:int, str:int, vx:int, time:Number):void {
room.setAttribute(id + "." + Enemy.ATTR_HP, hp.toString());
room.setAttribute(id + "." + Enemy.ATTR_SPAWN_X, Const.FIELD_LFET_BOUND.toString());
room.setAttribute(id + "." + Enemy.ATTR_STATUS,
hp.toString() + "," +
str.toString() + "," +
int(Const.FIELD_SIZE * Math.random()) + "," +
vx.toString() + "," +
time.toString()
);
}

public function Enemy(id:String, status:String, hp:String, spawnX:String) {
var statusArray:Array = status.split(",");
_id = id;
_hp = (hp) ? int(hp) : int.MAX_VALUE;
_maxHp = statusArray[0];
_str = statusArray[1];
_currentPosition = new Point(int(spawnX), statusArray[2]);
_vx = statusArray[3];
_spawnPosition = new Point(int(spawnX), statusArray[2]);
_spawnTime = statusArray[4];
_bounds = new Rectangle(
_currentPosition.x - Enemy.BOUNDS_HALF_WIDTH,
_currentPosition.y - Enemy.BOUNDS_HEIGHT,
Enemy.BOUNDS_HALF_WIDTH * 2,
Enemy.BOUNDS_HEIGHT
);
_body = new BodyAnim(Const.CHARACTER_QB);
_body.transition(BodyAnim.RUN);
_worldTrans = new Matrix();
_worldTrans.ty = int(_currentPosition.y + Const.FIELD_OFFSET_Y);
_effectColorOffset = 0;
}

/** HP変更,ノックバック: プレイヤーが直接ダメージを与える際に呼ぶ */
public function damaged(room:Room, amount:int, knockback:int):void {
_effectColorOffset = 300;
EventManager.instance.dispatchEvent(new EffectEvent(EffectEvent.DAMAGED_ACTOR,
_currentPosition.x,
_currentPosition.y,
{ num: amount, isEnemy: true }
));
room.setAttribute(_id + "." + Enemy.ATTR_HP, "%v-" + amount, true, false, true);

_hp -= amount;
if (knockback > 0) {
_spawnPosition.x -= knockback;
room.setAttribute(_id + "." + Enemy.ATTR_SPAWN_X, "%v-" + knockback, true, false, true);
}
}

/** HP変更: サーバーから更新メッセージを受け取った際に呼ぶ */
public function changeHp(value:int, oldValue:int, bySelf:Boolean):void {
if (_hp != int.MAX_VALUE && !bySelf) {
_effectColorOffset = 300;
EventManager.instance.dispatchEvent(new EffectEvent(EffectEvent.DAMAGED_ACTOR,
_currentPosition.x,
_currentPosition.y,
{ num: oldValue - value, isEnemy: true }
));
}
if (value < _hp) { _hp = value; }
}

/** ノックバック: サーバーからの更新メッセージを受け取った際に呼ぶ */
public function changeSpawnX(spawnX:int):void {
_spawnPosition.x = spawnX;
}

public function update(serverTime:Number, elapsedTime:int):void {
var lifeTime:Number = serverTime - _spawnTime;
_currentPosition.x = _spawnPosition.x + _vx * lifeTime / 1000;
_worldTrans.tx = int(_currentPosition.x + Const.FIELD_OFFSET_X);
_bounds.x = _currentPosition.x - Enemy.BOUNDS_HALF_WIDTH;
_bounds.y = _currentPosition.y - Enemy.BOUNDS_HEIGHT;
_body.update(elapsedTime, _worldTrans);
_effectColorOffset = Math.max(0, _effectColorOffset - 60);
}

public function draw(target:BitmapData):void {
var effect:ColorTransform = GeomPool.colorTransform(1, 1, 1, 1, _effectColorOffset, _effectColorOffset, _effectColorOffset);
target.draw(Assets.images["actorShadow"], GeomPool.matrix(1, 0, 0, 1, _worldTrans.tx - 14, _worldTrans.ty - 4));
_body.draw(target, effect);
var hpRate:Number = Math.min(Math.max(0, _hp), _maxHp) / _maxHp;
target.fillRect(GeomPool.rectangle(_worldTrans.tx - 16, _worldTrans.ty - 28, 32, 1), 0xFFCC0000);
target.fillRect(GeomPool.rectangle(_worldTrans.tx - 16, _worldTrans.ty - 28, 32 * hpRate, 1), 0xFF00FF00);
}

public function get id():String { return _id; }
public function get isAlive():Boolean { return _hp > 0; }
public function get str():int { return _str; }
public function get x():Number { return _currentPosition.x; }
public function get y():Number { return _currentPosition.y; }
public function get bounds():Rectangle { return _bounds; }
public function get depth():Number { return _currentPosition.y; }
}
//}
//package ore.orelib.actors {
import flash.geom.Rectangle;
import flash.utils.Dictionary;
import net.user1.reactor.Attribute;
import net.user1.reactor.AttributeEvent;
import net.user1.reactor.Room;
/* import ore.orelib.commons.EventManager;
import ore.orelib.data.Const;
import ore.orelib.events.ActorEvent;
*/
//public
class EnemyManager {
private var _room:Room;
private var _enemyList:Dictionary;

public function EnemyManager(room:Room) {
_room = room;
_enemyList = new Dictionary();

_room.addEventListener(AttributeEvent.UPDATE, attributeUpdateHandler);
_room.addEventListener(AttributeEvent.DELETE, attributeDeleteHandler);
}

private function attributeUpdateHandler(event:AttributeEvent):void {
var attr:Attribute = event.getChangedAttr();
var idAndName:Array = attr.name.split(".");
var enemy:Enemy;
switch(idAndName[1]) {
// 敵の出現
case Enemy.ATTR_STATUS:
{
enemy = new Enemy(
idAndName[0],
attr.value,
_room.getAttribute(idAndName[0] + "." + Enemy.ATTR_HP),
_room.getAttribute(idAndName[0] + "." + Enemy.ATTR_SPAWN_X)
);
_enemyList[idAndName[0]] = enemy;
EventManager.instance.dispatchEvent(new ActorEvent(ActorEvent.ADD, enemy));
break;
}

// HPの変化(HP0でサーバーから削除は、撃破したクライアントのPlayerクラスで行う)
case Enemy.ATTR_HP:
{
enemy = _enemyList[idAndName[0]];
var bySelf:Boolean = (attr.byClient && attr.byClient.isSelf());
if (enemy) { enemy.changeHp(int(attr.value), int(attr.oldValue), bySelf); }
break;
}

// ノックバック
case Enemy.ATTR_SPAWN_X:
{
enemy = _enemyList[idAndName[0]];
if (enemy) { enemy.changeSpawnX(int(attr.value)); }
break;
}

default: { break; }
}
}

private function attributeDeleteHandler(event:AttributeEvent):void {
var idAndName:Array = event.getChangedAttr().name.split(".");
if (idAndName[1] != Enemy.ATTR_STATUS) { return; }

var enemy:Enemy = _enemyList[idAndName[0]];
if (enemy) { deleteEnemy(enemy); }
}

private function deleteEnemy(enemy:Enemy):void {
delete _enemyList[enemy.id];
EventManager.instance.dispatchEvent(new ActorEvent(ActorEvent.REMOVE, enemy));
}

public function update(serverTime:Number, elapsedTime:int, isHost:Boolean):void {
for each(var enemy:Enemy in _enemyList) {
// 撃破されていたら削除して次へ
if (!enemy.isAlive) {
deleteEnemy(enemy);
// 死亡エフェクトはここ
continue;
}

enemy.update(serverTime, elapsedTime);

// 自分がホストで、敵が画面右端に到達していたらサーバーから削除
if (isHost && enemy.x > Const.FIELD_RIGHT_BOUND) {
deleteEnemy(enemy);
_room.deleteAttribute(enemy.id + "." + Enemy.ATTR_STATUS);
_room.deleteAttribute(enemy.id + "." + Enemy.ATTR_HP);
_room.deleteAttribute(enemy.id + "." + Enemy.ATTR_SPAWN_X);
}
}
}

/** 引数で与えた境界と衝突している敵のリストを取得する */
public function acquireCollidingEnemies(bounds:Rectangle):Vector.<Enemy> {
var result:Vector.<Enemy> = new Vector.<Enemy>();

for each(var enemy:Enemy in _enemyList) {
var enemyBounds:Rectangle = enemy.bounds;
if (
bounds.top < enemyBounds.bottom &&
bounds.bottom > enemyBounds.top &&
bounds.left < enemyBounds.right &&
bounds.right > enemyBounds.left
) {
result.push(enemy);
}
}
result.sort(compareXOfEnemy);

return result;
}

private function compareXOfEnemy(a:Enemy, b:Enemy):Number {
return (a.x < b.x) ? 1 : -1; // xが大きい順に並べる
}

public function removeEventListeners():void {
_room.removeEventListener(AttributeEvent.UPDATE, attributeUpdateHandler);
_room.removeEventListener(AttributeEvent.DELETE, attributeDeleteHandler);
}
}
//}
//package ore.orelib.actors {
import flash.display.DisplayObject;
import flash.filters.GlowFilter;
import flash.text.TextField;
/* import ore.orelib.commons.TextBuilder;
import ore.orelib.data.Const;
*/
//public
class PopUp {
private var _text:TextField;
private var _frameCount:int;

public function PopUp() {
_text =
new TextBuilder().align(TextBuilder.CENTER)
.filters([new GlowFilter(0x440000, 1, 2, 2, 8)])
.font(Const.FONT, -400, 400).fontSize(12)
.size(50, 20).build("0");
_frameCount = 0;
}

public function initialize(x:Number, y:Number, num:int, isEnemy:Boolean):void {
_text.x = x - 25 + Const.FIELD_OFFSET_X;
_text.y = y - 20 + Const.FIELD_OFFSET_Y;
_text.text = num.toString();
_text.textColor = (isEnemy) ? 0xFFCC44 : 0xFF8888;
_text.alpha = 1;
_frameCount = 0;
}

public function update():void {
_frameCount++;
_text.y -= 3;
_text.alpha -= 0.05;
}

public function get overlay():DisplayObject { return _text; }
public function get exists():Boolean { return _frameCount <= 10; }
}
//}
//package ore.orelib.actors {
import flash.display.BitmapData;
import flash.display.GradientType;
import flash.display.Graphics;
import flash.display.Shape;
import flash.geom.Matrix;
/* import ore.orelib.assets.PlayingView;
import ore.orelib.commons.Assets;
import ore.orelib.commons.EventManager;
import ore.orelib.commons.GeomPool;
import ore.orelib.data.Const;
import ore.orelib.data.WeaponSmith;
import ore.orelib.events.EffectEvent;
*/
//public
class EffectManager {
private var _view:PlayingView
private var _shape:Shape;
private var _popUpList:Vector.<PopUp>;
private var _popUpPool:Vector.<PopUp>;

public function EffectManager(view:PlayingView) {
_view = view;
_shape = new Shape();
_popUpList = new Vector.<PopUp>();
_popUpPool = new Vector.<PopUp>();

EventManager.instance.addEventListener(EffectEvent.ATTACK_WEAPON, attackWeaponHandler);
EventManager.instance.addEventListener(EffectEvent.DAMAGED_ACTOR, damagedActorHandler);
}

private function attackWeaponHandler(event:EffectEvent):void {
var attackType:int = WeaponSmith.acquireAttackTypeO

Show more