はてなハイク STAR FIGHTERS
by
lieutar
2010-12-30 [2010/12/30 15:12:55]
はてなハイクで実行すると、星を打ちあう簡単な対戦型シューティングが初まる。カーソルキーで移動、Zで星発射。ESCで終了
@@ -5,6 +5,7 @@
* @license MIT License
* @require
*/
+//
(function(){
@@ -17,28 +18,66 @@
];
var CSS_DIALOG = [
- 'z-index: 131072',
- 'opacity: 0.9',
- 'display: table',
- 'padding: 20px',,
- 'font-size: 40px',
- 'font-weight: 900',
- 'background: #C99',
- 'color: #FFF',
- '-moz-border-radius: 20px',
- '-webkit-border-radius: 20px',
- 'border-radius: 20px',
- 'position: fixed'
- ].join(";\n");
+ 'z-index: 131072;',
+ 'opacity: 0.9;',
+ 'display: table;',
+ 'padding: 20px;',
+ 'font-size: 40px;',
+ 'font-weight: 900;',
+ 'background: #C99;',
+ 'color: #FFF;',
+ '-moz-border-radius: 20px;',
+ '-webkit-border-radius: 20px;',
+ 'border-radius: 20px;',
+ 'position: fixed;'
+ ].join("\n");
var CSS_NAMEPLATE= [
- 'background: #EEE',
- 'color: #339',
- 'font-size: 12px',
- '-moz-border-radius: 5px',
- '-webkit-border-radius: 5px',
- 'border-radius: 5px'
- ].join(";\n");
+ 'background: #EEE;',
+ 'color: #339;',
+ 'font-size: 12px;',
+ '-moz-border-radius: 5px;',
+ '-webkit-border-radius: 5px;',
+ 'border-radius: 5px;'
+ ].join("\n");
+
+ var CSS_TITLE = [
+ 'position: fixed;',
+ 'z-index: 65536;',
+ 'background: #FFF;',
+ 'border: solid #363 3px;',
+ 'color: #669;',
+ 'padding: 20px;',
+ 'font-size: 20px;',
+ 'font-weight: 900;',
+ '-moz-border-radius: 10px;',
+ '-webkit-border-radius: 10px;',
+ 'border-radius: 10px;'
+ ].join("\n");
+
+ var CSS_TITLE2 = [
+ 'font-size: 12px;'
+ ].join(" ");
+
+ var CSS_KBD = [
+ 'display: inline-block;',
+ 'background: #CCC;',
+ 'min-width: 1.2em;',
+ 'height: 1.2em;',
+ 'padding: 1px;',
+ 'border: solid #EEE 1px;',
+ 'border-color: #EEE #999 #999 #EEE;',
+ 'margin: 1px;'
+ ].join(" ");
+
+ var HTML_TITLE = [
+ '<img src="http://h.hatena.ne.jp/images/haiku_logo.gif">',
+ '<div style="">HATENA STAR FIGHTERS</div>',
+ '<div style="'+CSS_TITLE2+'"',
+ ' >PRESS <kbd style="'+CSS_KBD+'">Z</kbd> KEY to START</div>',
+ '<div style="'+CSS_TITLE2+'"',
+ ' >PRESS <kbd style="'+CSS_KBD+'">ESC</kbd> KEY to EXIT</div>'
+ ].join("\n");
////////////////////////////////////////////////////////////
@@ -152,13 +191,16 @@
}
);
-
var ShotKeyManager = defclass(
{
name: 'ShotKeyManager',
base: AbstractKeyManager
},
function(SUPER, Class){
+ this.pushKey = function(k){
+ SUPER.releaseKey.apply(this, [k]);
+ this._runHooks();
+ };
this.releaseKey = function(k){
SUPER.releaseKey.apply(this, [k]);
this._runHooks();
@@ -252,17 +294,13 @@
name: 'Game'
},
function(SUPER, Class){
-
- this.initialize = function(){
- this.allElems = {};
- this.elemDic = {};
- this.timer = null;
- this.runnning = false;
- this._initializer = null;
-
+ this.initialize = function(opt){
+ this.currentState = null;
+ this._states = {};
+ this._end = false;
var akman = new ArrowKeyManager();
- var skman = new ShotKeyManager();
-
+ var skman = new ShotKeyManager();
+ var self = this;
this.listeners = [
[ document,
'keypress',
@@ -274,19 +312,22 @@
e.preventDefault();
// console.log(e.keyCode);
switch(e.keyCode){
- case 37: //LEFT
+ case 27: // ESC
+ self.end();
+ break;
+ case 37: // LEFT
akman.pushKey('w');
break;
- case 38: //UP
+ case 38: // UP
akman.pushKey('n');
break;
- case 39: //RIGHT
+ case 39: // RIGHT
akman.pushKey('e');
break;
- case 40: //DOWN
+ case 40: // DOWN
akman.pushKey('s');
break;
- case 90:
+ case 90: // Z
skman.pushKey(1);
}
}, true ],
@@ -295,16 +336,16 @@
e.stopPropagation();
e.preventDefault();
switch(e.keyCode){
- case 37: //LEFT
+ case 37: // LEFT
akman.releaseKey('w');
break;
- case 38: //UP
+ case 38: // UP
akman.releaseKey('n');
break;
- case 39: //RIGHT
+ case 39: // RIGHT
akman.releaseKey('e');
break;
- case 40: //DOWN
+ case 40: // DOWN
akman.releaseKey('s');
break;
case 90: // Z
@@ -317,10 +358,6 @@
this.initEventListeners();
};
- this.setInitializer = function(cb){
- this._initializer = cb;
- };
-
this.initEventListeners = function(){
this.listeners.forEach(
function(spec){
@@ -337,6 +374,104 @@
);
};
+ this.registerState = function(key, state){
+ this._states[key] = state;
+ };
+
+ this.unregisterState = function(){
+ };
+
+ this.gotoState = function(key){
+ if(this._end) return;
+ if(this.currentState){
+ this.currentState.end();
+ }
+ this.currentState = this._states[key];
+ if(this.currentState){
+ this.currentState.start();
+ } else {
+ this.end();
+ }
+ };
+
+ this.end = function(){
+ this._end = true;
+ if(!this.currentState) return;
+ this.removeEventListeners();
+ this.currentState.end();
+ };
+
+ }) // end of defclass
+ ); // end of new
+
+ ////////////////////////////////////////////////////////////
+ var GameState = defclass(
+ {
+ base: Object,
+ name: 'GameState'
+ },
+ function(SUPER, Class){
+ this.initialize = function(){
+ };
+ this.start = function(){
+ };
+ this.end = function(){
+ };
+ }
+ );
+
+ var GameTitleState = defclass(
+ {
+ base: GameState,
+ name: 'GameTitleState'
+ },
+ function(SUPER, Class){
+ this.initialize = function(){
+ };
+
+ this.start = function(){
+ var div = document.createElement('div');
+ div.style.cssText = CSS_TITLE;
+ div.innerHTML = HTML_TITLE;
+ document.body.appendChild(div);
+ div.style.top = (window.innerHeight - div.clientHeight) / 2 + 'px';
+ div.style.left = (window.innerWidth - div.clientWidth) / 2 + 'px';
+ this._dialog = div;
+ var self = this;
+ this._shotHook = function(){
+ Game.gotoState('play');
+ };
+ Game.shotKeyManager.addHook(this._shotHook);
+ };
+
+ this.end = function(){
+ Game.shotKeyManager.deleteHook(this._shotHook);
+ this._dialog.parentNode.removeChild(this._dialog);
+ };
+ }
+ );
+
+ var GamePlayingState = defclass(
+ {
+ base: GameState,
+ name: 'GamePlayingState'
+ },
+ function(SUPER, Class){
+
+
+ this.initialize = function(){
+ this.allElems = {};
+ this.elemDic = {};
+ this.timer = null;
+ this.running = false;
+ this._initializer = null;
+ };
+
+ this.setInitializer = function(cb){
+ this._initializer = cb;
+ };
+
+
this.getElementsByClass = function(cls){
var elemDic = this.elemDic;
var slot = elemDic[cls._name || ''] || {};
@@ -405,12 +540,13 @@
};
this.end = function(){
- this.runnnig = false;
- this.removeEventListeners();
+ if(!this.running) return;
+ this.running = false;
var allElems = this.allElems;
for(var f in allElems){
allElems[f].dispose();
}
+ Game.gotoState('title');
};
this.reset = function(){
@@ -423,17 +559,25 @@
};
this.start = function(){
+ if(Game._end) return;
var self = this;
this._initializer();
- this.runnning = true;
+ this.running = true;
(function(){
- if(!self.runnning) return;
+ if(!self.running) return;
self.iter();
self.timer = setTimeout(arguments.callee, 10);
})();
};
- }) // end of defclass
- ); // end of new
+ }
+ );
+
+
+
+ ////////////////////////////////////////////////////////////
+ var titlebox = function(msg){
+ return div;
+ };
var dialog = function(msg){
var div = document.createElement('div');
@@ -455,11 +599,12 @@
setTimeout(
function(){
div.parentNode.removeChild(div);
- Game.end();
var gameover = dialog('GAME OVER');
setTimeout(
function(){
+ StateLock = false;
gameover.parentNode.removeChild(gameover);
+ Game.currentState.end();
}, 3000
);
}, 3000);
@@ -473,7 +618,7 @@
function(){
div.parentNode.removeChild(div);
StateLock = false;
- Game.reset();
+ Game.currentState.reset();
}, 3000);
};
@@ -507,7 +652,7 @@
this._pdy = -1;
this._elem = this.makeElem();
this.initElem(this._elem);
- Game.registerElement(this);
+ Game.currentState.registerElement(this);
this.redraw();
};
@@ -590,7 +735,7 @@
//
this.dispose = function(){
this.disposeElements();
- Game.unregisterElement(this);
+ Game.currentState.unregisterElement(this);
for(var f in this){
if('function' == typeof this[f]){
this[f] = function(){};
@@ -1022,7 +1167,8 @@
this.getPlayersDirection = function(){
var elem = this.elem;
- var player = (Game.getElementsByClass(Player) || [])[0] || null;
+ var player = (Game.currentState.getElementsByClass(Player) ||
+ [])[0] || null;
if(!player) return this.getRandomDirection();
var dx = player._x - elem._x;
var dy = player._y - elem._y;
@@ -1140,7 +1286,15 @@
new Enemy(enemy_id);
};
- Game.setInitializer(newgame);
- Game.start();
+
+ var titleState = new GameTitleState();
+ Game.registerState('title', titleState);
+
+ var gamePlayState = new GamePlayingState();
+ gamePlayState.setInitializer(newgame);
+ Game.registerState('play', gamePlayState);
+
+ //debugger;
+ Game.gotoState('title');
})();
/*
* @title はてなハイク STAR FIGHTERS
* @description はてなハイクで実行すると、星を打ちあう簡単な対戦型シューティングが初まる。カーソルキーで移動、Zで星発射。ESCで終了
* @include http://h.hatena.ne.jp/*
* @license MIT License
* @require
*/
//
(function(){
var STARS = [
'http://s.hatena.ne.jp/images/star.gif',
'http://s.hatena.ne.jp/images/star-green.gif',
'http://s.hatena.ne.jp/images/star-red.gif',
'http://s.hatena.ne.jp/images/star-blue.gif',
'http://s.hatena.ne.jp/images/star-purple.gif'
];
var CSS_DIALOG = [
'z-index: 131072;',
'opacity: 0.9;',
'display: table;',
'padding: 20px;',
'font-size: 40px;',
'font-weight: 900;',
'background: #C99;',
'color: #FFF;',
'-moz-border-radius: 20px;',
'-webkit-border-radius: 20px;',
'border-radius: 20px;',
'position: fixed;'
].join("\n");
var CSS_NAMEPLATE= [
'background: #EEE;',
'color: #339;',
'font-size: 12px;',
'-moz-border-radius: 5px;',
'-webkit-border-radius: 5px;',
'border-radius: 5px;'
].join("\n");
var CSS_TITLE = [
'position: fixed;',
'z-index: 65536;',
'background: #FFF;',
'border: solid #363 3px;',
'color: #669;',
'padding: 20px;',
'font-size: 20px;',
'font-weight: 900;',
'-moz-border-radius: 10px;',
'-webkit-border-radius: 10px;',
'border-radius: 10px;'
].join("\n");
var CSS_TITLE2 = [
'font-size: 12px;'
].join(" ");
var CSS_KBD = [
'display: inline-block;',
'background: #CCC;',
'min-width: 1.2em;',
'height: 1.2em;',
'padding: 1px;',
'border: solid #EEE 1px;',
'border-color: #EEE #999 #999 #EEE;',
'margin: 1px;'
].join(" ");
var HTML_TITLE = [
'<img src="http://h.hatena.ne.jp/images/haiku_logo.gif">',
'<div style="">HATENA STAR FIGHTERS</div>',
'<div style="'+CSS_TITLE2+'"',
' >PRESS <kbd style="'+CSS_KBD+'">Z</kbd> KEY to START</div>',
'<div style="'+CSS_TITLE2+'"',
' >PRESS <kbd style="'+CSS_KBD+'">ESC</kbd> KEY to EXIT</div>'
].join("\n");
////////////////////////////////////////////////////////////
if(! 'forEach' in Array.prototype){
Array.prototype.forEach = function(cb){
for(var i=0,l=this.length;i<l;i++){
cb(this[i],i);
}
};
}
if(! 'map' in Array.prototype){
Array.prototype.map = function(cb){
var R = [];
for(var i=0,l=this.length;i<l;i++){
R.push(cb(this[i],i));
}
return R;
};
}
////////////////////////////////////////////////////////////
var defclass = function(spec, initializer){
var base = spec.base;
var klass = function(){};
klass.prototype = base.prototype;
var proto = new klass;
if(!('initialize' in proto)) proto.initialize = function(){};
var R = function(){
this.constructor = arguments.callee;
this.initialize.apply(this,arguments);
};
R.prototype = proto;
R._name = spec.name;
R._base = base;
R.getName = function(){ return this._name; };
R.getBaseClasses = function(){
var R = [this];
if('getBaseClasses' in base){
R.push.apply(R, base.getBaseClasses());
} else {
R.push(base);
}
return R;
};
initializer.apply(
proto,
[
base.prototype,
R
]
);
return R;
};
////////////////////////////////////////////////////////////
var AbstractKeyManager = defclass(
{
name: 'AbstractKeyManager',
base: Object
},
function(SUPER, Class){
this.initialize = function(){
this._hooks = [];
this._lastKey = null;
this._lastEvent = null;
};
this.addHook = function(hook){
this._hooks.push(hook);
};
this.deleteHook = function(hook){
var hooks = this._hooks;
var found = null;
for(var i=0,l=hooks.length;i<l;i++){
if(hook === hooks[i]){
found = i;
break;
}
}
if( found !== null ){
hooks.splice( found, 1 );
}
};
this._runHooks = function(){
var self = this;
this._hooks.forEach(
function(hook){
hook.apply(self, []);
}
);
};
this.pushKey = function(k){
this._lastKey = k;
this._lastEvent = 'push';
};
this.releaseKey = function(k){
this._lastKey = k;
this._lastEvent = 'release';
};
}
);
var ShotKeyManager = defclass(
{
name: 'ShotKeyManager',
base: AbstractKeyManager
},
function(SUPER, Class){
this.pushKey = function(k){
SUPER.releaseKey.apply(this, [k]);
this._runHooks();
};
this.releaseKey = function(k){
SUPER.releaseKey.apply(this, [k]);
this._runHooks();
};
}
);
var ArrowKeyManager = defclass(
{
name: 'ArrowKeyManager',
base: AbstractKeyManager
},
function(SUPER, Class){
this.initialize = function(){
SUPER.initialize.apply(this,[]);
this.pressed = {n:0,s:0,w:0,e:0};
};
this.pushKey = function(k){
var pressed = this.pressed;
switch(k){
case 'n':
pressed.n = (pressed.s > 0) ? 2 : 1;
break;
case 's':
pressed.s = (pressed.n > 0) ? 2 : 1;
break;
case 'w':
pressed.w = (pressed.e > 0) ? 2 : 1;
break;
case 'e':
pressed.e = (pressed.w > 0) ? 2 : 1;
break;
}
this._runHooks();
};
this.releaseKey = (
function(){
var timer = null;
return function(k){
var pressed = this.pressed;
switch(k){
case 'n':
pressed.n = 0;
break;
case 's':
pressed.s = 0;
break;
case 'w':
pressed.w = 0;
break;
case 'e':
pressed.e = 0;
}
var self = this;
if(timer){
clearTimeout(timer);
timer = null;
}
timer = setTimeout(
function(){
self._runHooks();
timer = null;
},
50
);
};
}
)();
this.getCurrentDirection = function(){
var pressed = this.pressed;
var x = 0;
var y = 0;
if(pressed.s != pressed.n) y = pressed.s > pressed.n ? 1 : -1;
if(pressed.w != pressed.e) x = pressed.e > pressed.w ? 1 : -1;
return {x:x, y:y};
};
}
);
////////////////////////////////////////////////////////////
var Game = new (
defclass(
{
base: Object,
name: 'Game'
},
function(SUPER, Class){
this.initialize = function(opt){
this.currentState = null;
this._states = {};
this._end = false;
var akman = new ArrowKeyManager();
var skman = new ShotKeyManager();
var self = this;
this.listeners = [
[ document,
'keypress',
function(e){ e.preventDefault();},
false ],
[ window,
'keydown', function(e){
e.stopPropagation();
e.preventDefault();
// console.log(e.keyCode);
switch(e.keyCode){
case 27: // ESC
self.end();
break;
case 37: // LEFT
akman.pushKey('w');
break;
case 38: // UP
akman.pushKey('n');
break;
case 39: // RIGHT
akman.pushKey('e');
break;
case 40: // DOWN
akman.pushKey('s');
break;
case 90: // Z
skman.pushKey(1);
}
}, true ],
[ window,
'keyup', function(e){
e.stopPropagation();
e.preventDefault();
switch(e.keyCode){
case 37: // LEFT
akman.releaseKey('w');
break;
case 38: // UP
akman.releaseKey('n');
break;
case 39: // RIGHT
akman.releaseKey('e');
break;
case 40: // DOWN
akman.releaseKey('s');
break;
case 90: // Z
skman.releaseKey(1);
}
}, true]
];
this.arrowKeyManager = akman;
this.shotKeyManager = skman;
this.initEventListeners();
};
this.initEventListeners = function(){
this.listeners.forEach(
function(spec){
spec[0].addEventListener(spec[1],spec[2],spec[3]);
}
);
};
this.removeEventListeners = function(){
this.listeners.forEach(
function(spec){
spec[0].removeEventListener(spec[1],spec[2],spec[3]);
}
);
};
this.registerState = function(key, state){
this._states[key] = state;
};
this.unregisterState = function(){
};
this.gotoState = function(key){
if(this._end) return;
if(this.currentState){
this.currentState.end();
}
this.currentState = this._states[key];
if(this.currentState){
this.currentState.start();
} else {
this.end();
}
};
this.end = function(){
this._end = true;
if(!this.currentState) return;
this.removeEventListeners();
this.currentState.end();
};
}) // end of defclass
); // end of new
////////////////////////////////////////////////////////////
var GameState = defclass(
{
base: Object,
name: 'GameState'
},
function(SUPER, Class){
this.initialize = function(){
};
this.start = function(){
};
this.end = function(){
};
}
);
var GameTitleState = defclass(
{
base: GameState,
name: 'GameTitleState'
},
function(SUPER, Class){
this.initialize = function(){
};
this.start = function(){
var div = document.createElement('div');
div.style.cssText = CSS_TITLE;
div.innerHTML = HTML_TITLE;
document.body.appendChild(div);
div.style.top = (window.innerHeight - div.clientHeight) / 2 + 'px';
div.style.left = (window.innerWidth - div.clientWidth) / 2 + 'px';
this._dialog = div;
var self = this;
this._shotHook = function(){
Game.gotoState('play');
};
Game.shotKeyManager.addHook(this._shotHook);
};
this.end = function(){
Game.shotKeyManager.deleteHook(this._shotHook);
this._dialog.parentNode.removeChild(this._dialog);
};
}
);
var GamePlayingState = defclass(
{
base: GameState,
name: 'GamePlayingState'
},
function(SUPER, Class){
this.initialize = function(){
this.allElems = {};
this.elemDic = {};
this.timer = null;
this.running = false;
this._initializer = null;
};
this.setInitializer = function(cb){
this._initializer = cb;
};
this.getElementsByClass = function(cls){
var elemDic = this.elemDic;
var slot = elemDic[cls._name || ''] || {};
var R = [];
for(var f in slot) R.push(slot[f]);
return R;
};
this.registerElement = function(elem){
var self = this;
var elemDic = this.elemDic;
this.allElems[elem.key] = elem;
elem.constructor.getBaseClasses().forEach(
function(cl){
if(!cl._name) return;
(elemDic[cl._name] || (elemDic[cl._name] = {}))[elem.key] = elem;
}
);
};
this.unregisterElement = function(elem){
delete this.allElems[elem.key];
var elemDic = this.elemDic;
elem.constructor.getBaseClasses().forEach(
function(cl){
if(!(cl._name && elemDic[cl._name])) return;
delete elemDic[cl._name][elem.key];
}
);
};
this.iter = function(){
var allElems = this.allElems;
var elemDic = this.elemDic;
var i,l, ff;
var elem;
for(f in allElems){
allElems[f].step();
}
var checked = {};
for(f in allElems){
elem = allElems[f];
var tclasses = elem.getTargetClasses();
l = tclasses.length;
if(l < 1) continue;
var chslot = (checked[f] || (checked[f] = {}));
for(i=0,l=tclasses.length;i<l;i++){
var edslot = elemDic[tclasses[i]];
for(ff in edslot){
if(chslot[ff]) continue;
chslot[ff] = true;
var target = edslot[ff];
if(!elem.checkHit(target)) continue;
(checked[target.key] ||
(checked[target.key] = {}))[f] = true;
elem.onHit(target);
target.onHit(elem);
}
}
}
for(f in allElems){
allElems[f].redraw();
}
};
this.end = function(){
if(!this.running) return;
this.running = false;
var allElems = this.allElems;
for(var f in allElems){
allElems[f].dispose();
}
Game.gotoState('title');
};
this.reset = function(){
var allElems = this.allElems;
for(var f in allElems){
allElems[f].dispose();
}
if(this.timer) clearTimeout(this.timer);
this.start();
};
this.start = function(){
if(Game._end) return;
var self = this;
this._initializer();
this.running = true;
(function(){
if(!self.running) return;
self.iter();
self.timer = setTimeout(arguments.callee, 10);
})();
};
}
);
////////////////////////////////////////////////////////////
var titlebox = function(msg){
return div;
};
var dialog = function(msg){
var div = document.createElement('div');
div.appendChild(document.createTextNode(msg));
div.style.cssText = CSS_DIALOG;
document.body.appendChild(div);
div.style.left = (( window.innerWidth - div.clientWidth ) / 2 )+'px';
div.style.top = (( window.innerHeight - div.clientHeight ) / 2 )+'px';
return div;
};
var StateLock = false;
var endgame = function(msg){
if(StateLock) return;
StateLock = true;
var div = dialog(msg);
setTimeout(
function(){
div.parentNode.removeChild(div);
var gameover = dialog('GAME OVER');
setTimeout(
function(){
StateLock = false;
gameover.parentNode.removeChild(gameover);
Game.currentState.end();
}, 3000
);
}, 3000);
};
var nextgame = function(msg){
if(StateLock) return;
StateLock = true;
var div = dialog(msg);
setTimeout(
function(){
div.parentNode.removeChild(div);
StateLock = false;
Game.currentState.reset();
}, 3000);
};
////////////////////////////////////////////////////////////
var genImage = function(url , container){
var img = document.createElement('img');
img.src = url;
return img;
};
var GameElement = defclass(
{
base: Object,
name: 'GameElement'
},
function(SUPER, Class){
var counter = 0;
this.initialize = function(x,y){
this.key = 'elem_'+ counter++;
this._x = x;
this._y = y;
this._pre_x = NaN;
this._pre_y = NaN;
this._speed = 1;
this._dx = 0;
this._dy = 0;
this._pdx = 0;
this._pdy = -1;
this._elem = this.makeElem();
this.initElem(this._elem);
Game.currentState.registerElement(this);
this.redraw();
};
this.makeElem = function(){
var div = document.createElement('div');
div.style.border = 'solid black 2px';
return div;
};
this.initElem = function(elem){
document.body.appendChild(elem);
elem.style.zIndex = 65536;
elem.style.position = 'fixed';
};
this.setSpeed = function(s){
this._speed = s;
};
this.setPosition = function(x,y){
this._x = x;
this._y = y;
};
this.getPosition = function(){
return {
x: this._x ,
y: this._y
};
};
this.getPreviousPosition = function(){
return {
x: this._pre_x,
y: this._pre_y
};
};
this.getSize = function(){
var elem = this._elem;
return {
width: elem.clientWidth,
height: elem.clientHeight
};
};
this.getHitAreaSize = function(){
return this.getSize();
};
this.getSpeed = function(){
return this._speed;
};
this.setDirection = function(x, y){
this._dx = x;
this._dy = y;
};
this.getDirection = function(){
return { x:this._dx, y:this._dy };
};
this.setShotDirection = function(x,y){
this._pdx = x;
this._pdy = y;
};
this.getShotDirection = function(){
return { x:this._pdx, y:this._pdy };
};
this.step = function(){
this._pre_x = this._x;
this._pre_y = this._y;
this._x = this._x + this._dx * this._speed;
this._y = this._y + this._dy * this._speed;
};
//
this.dispose = function(){
this.disposeElements();
Game.currentState.unregisterElement(this);
for(var f in this){
if('function' == typeof this[f]){
this[f] = function(){};
}
}
};
this.disposeElements = function(){
var img = this._elem;
if(img.parentNode) img.parentNode.removeChild(img);
};
// フレームごとの計算結果を元に
this.redraw = function(){
if(this._dispose){
this.dispose();
return;
}
var sx = this._x;
var sy = this._y;
var elem = this._elem;
elem.style.top = (sy - elem.clientHeight / 2) + 'px';
elem.style.left = (sx - elem.clientWidth / 2) + 'px';
};
// 当たり判定の対象となるクラス群
this.getTargetClasses = function(){
return [];
};
this.getHitRectangle = function(){
var size = this.getHitAreaSize();
var w = size.width;
var h = size.height;
var sx = this._x;
var sy = this._y;
var x0 = sx - w / 2;
var y0 = sy - h / 2;
return {
x1: x0,
y1: y0,
x2: x0 + w,
y2: y0 + h
};
};
// elem との当り判定を書く
// TODO すりぬけない方法について学習する
this.checkHit = function(elem){
var sr = this.getHitRectangle();
var er = elem.getHitRectangle();
var x0 = sr.x1;
var x1 = sr.x2;
var x2 = er.x1;
var x3 = er.x2;
var y0 = sr.y1;
var y1 = sr.y2;
var y2 = er.y1;
var y3 = er.y2;
return (x0 < x3 && x2 < x1 && y0 < y3 && y2 < y1);
};
// elem 衝突時の振舞い
this.onHit = function(elem){
};
this.onScreen = function(){
var sx = this._x;
var sy = this._y;
return (sx >= 0 && sx < window.innerWidth &&
sy >= 0 && sy < window.innerHeight);
};
this.backToScreen = function(){
var sx = this._x;
var sy = this._y;
if(sx < 0){
this._x = 0;
} else if (sx >= window.innerWidth){
this._x = window.innerWidth - 1;
}
if(sy < 0){
this._y = 0;
} else if (sy >= window.innerHeight){
this._y = window.innerHeight - 1;
}
};
this.pacmanWarp = function(){
var sx = this._x;
var sy = this._y;
};
}
);
var Explode = defclass(
{
base: GameElement,
name: 'Explode'
},
function(SUPER, Class){
this.initialize = function(x, y){
SUPER.initialize.apply(this, [x, y]);
this._frame = 0;
this._stars = null;
};
this.makeElem = function(){
var div = document.createElement('div');
div.style.width = '100px';
div.style.height = '100px';
var stars = [];
this.stars = stars;
for(var i =0; i < 16; i++){
var img = genImage(STARS[i % STARS.length]);
img.style.position = 'absolute';
img.width = 24;
img.height = 24;
stars.push(img);
div.appendChild( img );
}
return div;
};
this.step = function(){
this._frame++;
if(this._frame > 40){
this.dispose();
}
};
this.redraw = function(){
SUPER.redraw.apply(this, []);
var imgs = this.stars;
var frame = this._frame;
var sx = this._x;
var sy = this._y;
for(var i = 0;i<16;i++){
var rad = i / 16 * 2 * Math.PI;
var x = (Math.cos(rad)) * frame;
var y = (Math.sin(rad)) * frame;;
imgs[i].style.top = (50 + y) + 'px';
imgs[i].style.left = (50 + x) + 'px';
}
};
}
);
////////////////////////////////////////////////////////////
var Bullet = defclass(
{
base: GameElement,
name: 'Bullet'
},
function(SUPER, Class){
this.initialize = function(x,y){
SUPER.initialize.apply(this,[x, y]);
this.setDirection(0,-1);
this.setSpeed(10);
};
this.step = function(){
SUPER.step.apply(this, []);
if(!this.onScreen()) this._dispose = true;
};
}
);
var EBullet = defclass(
{
base: Bullet,
name: 'EBullet'
},
function(SUPER, Class){
this.makeElem = function(){
return genImage('http://s.hatena.ne.jp/images/star.gif');
};
}
);
var PBullet = defclass(
{
base: Bullet,
name: 'PBullet'
},
function(SUPER, Class){
this.makeElem = function(){
return genImage('http://s.hatena.ne.jp/images/star-green.gif');
};
}
);
////////////////////////////////////////////////////////////
var Rock = defclass(
{
base: GameElement,
name: 'Rock'
},
function(SUPER, Class){
this.makeElem = function(){
var img = genImage('http://h.hatena.ne.jp/favicon.ico');
img.width = 32;
img.height = 32;
return img;
};
this.getTargetClasses = function(){
return ['Bullet'];
};
this.onHit = function(elem){
if(elem instanceof Bullet){
var dir = elem.getDirection();
var dx = dir.x;
var dy = dir.y;
var pp = elem.getPreviousPosition();
var sp = this.getPosition();
var w = sp.x - pp.x;
var h = sp.y - pp.y;
var r = w == 0 ? (h < 0 ? -Infinity : Infinity) : h / w;
if ((w > 0 && r > 1) || (w < 0 && r < -1)){// from north
dy = - dy;
} else if(w > 0 && r < 1 && r >= -1){ // from west
dx = -dx;
} else if((w > 0 && r < -1) || (w < 0 && r >= 1)){// from sourh
dy = -dy;
} else { // from east
dx = -dx;
}
elem.setDirection(dx, dy);
}
};
}
);
////////////////////////////////////////////////////////////
var Motion;
var RandomMotion;
var User = defclass(
{
base: GameElement,
name: 'User'
}, function(SUPER,Class){
this.initialize = function(name){
this.name = name;
SUPER.initialize.apply(this,[0,0]);
this._defaultSpeed = 5;
this.setDirection(0, -1);
this.setSpeed(0);
};
this.makeElem = function(){
var name = this.name;
var div = document.createElement('div');
var img = genImage(
'http://www.st-hatena.com/users/' + name.substring(0,2) +
'/' + name + '/profile.gif'
);
div.appendChild(img);
var label = document.createElement('div');
div.appendChild(label);
label.innerHTML = 'id:' + name;
label.style.cssText = CSS_NAMEPLATE;
return div;
};
this.getHitAreaSize = function(){
var size = this.getSize();
return {
width: Math.floor(size.width / 3),
height: Math.floor(size.height / 3)
};
};
this.step = function(){
SUPER.step.apply(this, []);
if(!this.onScreen()){
this.backToScreen();
}
};
this.explode = function(){
new Explode(this._x, this._y);
this.dispose();
};
}
);
var Enemy = defclass(
{
base: User,
name: 'Enemy'
},
function(SUPER, Class){
this.initialize = function(name){
SUPER.initialize.apply(this,[name]);
var img = this._elem;
this._x = Math.floor((window.innerWidth - img.clientWidth ) / 2
);
this._y = img.clientHeight;
this._motion = new RandomMotion( this );
};
this.setMotion = function(motion){
this._motion = motion;
};
this.step = function(){
this._motion.step();
SUPER.step.apply(this, []);
};
this.getTargetClasses = function(){
return ['PBullet'];
};
this.onHit = function(elem){
if(elem instanceof PBullet){
this.explode();
nextgame('You Win!!');
}
};
}
);
var Player = defclass(
{
base: User,
name: 'Player'
}, function(SUPER, Class){
this.initialize = function(name){
SUPER.initialize.apply(this,[name]);
var self = this;
var img = this._elem;
this._arrowHook = function(){
var dir = this.getCurrentDirection();
self.setDirection(dir.x, dir.y);
};
Game.arrowKeyManager.addHook( this._arrowHook );
this._shotHook = function(){ self.shot(); };
Game.shotKeyManager.addHook( this._shotHook );
this._x =
Math.floor((window.innerWidth - img.clientWidth ) / 2
);
this._y =
(window.innerHeight - img.clientHeight - 10);
};
this.dispose = function(){
Game.arrowKeyManager.deleteHook( this._arrowHook );
Game.shotKeyManager.deleteHook( this._shothook );
SUPER.dispose.apply(this, []);
};
this.getTargetClasses = function(){
return ['Enemy', 'EBullet', 'Rock'];
};
this.onHit = function(elem){
if(elem instanceof Enemy){
this.explode();
elem.explode();
endgame('Draw');
}
if(elem instanceof EBullet){
this.explode();
endgame('You Lose');
}
};
this.shot = function(){
var pos = this.getPosition();
var bullet = new PBullet(pos.x, pos.y);
var dir = this.getDirection();
bullet.setDirection(dir.x, dir.y);
bullet.setSpeed(this._defaultSpeed + 2);
};
this.setDirection = function(x, y){
if(x == 0 && y == 0){
this.setSpeed(0);
} else {
this.setSpeed(this._defaultSpeed);
SUPER.setDirection.apply(this, [x,y]);
}
};
}
);
////////////////////////////////////////////////////////////
Motion = defclass(
{
base: Object,
name: 'Motion'
},
function(SUPER, Class){
this.initialize = function(elem){
this.elem = elem;
};
this.step = function(){
};
}
);
RandomMotion = defclass(
{
base: Motion,
name: 'RandomMotion'
},
function(SUPER, Class){
this.initialize = function(elem){
SUPER.initialize.apply(this, [elem]);
this._turn_at = null;
this._iter = 0;
this._changeDir();
};
this.getPlayersDirection = function(){
var elem = this.elem;
var player = (Game.currentState.getElementsByClass(Player) ||
[])[0] || null;
if(!player) return this.getRandomDirection();
var dx = player._x - elem._x;
var dy = player._y - elem._y;
var ddx, ddy;
if(Math.abs(dx) > Math.abs(dy)){
ddx = dx < 0 ? -1 : 1;
if(dy == 0){
ddy = 0;
} else {
ddy = dy / Math.abs(dx);
}
}else{
ddy = dy < 0 ? -1 : 1;
if(dx == 0){
ddx = 0;
} else{
ddx = dx / Math.abs(dy);
}
}
return {x:ddx, y:ddy};
};
this.getRandomDirection = function(){
return { x: Math.random() * 2 - 1,
y: Math.random() * 2 - 1 };
};
this._changeDir = function(){
this._turn_at = 50;
var elem = this.elem;
var dir = ((this._iter++ > 3 &&
Math.random() * 3 < 1)
? this.getPlayersDirection()
: this.getRandomDirection());
elem.setDirection(dir.x, dir.y);
elem.setSpeed(Math.random() * 10);
};
this.step = function(){
if(this._iter > 10){
switch(this._turn_at){
case 40:
this.shot(this.getPlayersDirection(), 10);
break;
case 30:
this.shot(this.getRandomDirection(), 10);
break;
case 20:
this.shot(this.getPlayersDirection(), 10);
break;
case 10:
this.shot(this.getRandomDirection(), 10);
break;
case 0:
this.shot(this.elem.getDirection(), this.elem.getSpeed() + 10);
break;
}
} else {
switch(this._turn_at){
case 40:
case 30:
case 20:
case 10:
case 0:
this.shot(this.elem.getDirection(), this.elem.getSpeed() + 10);
break;
}
}
if(this._turn_at-- < 0){
this._changeDir();
}
};
this.shot = function(dir, speed){
var pos = this.elem.getPosition();
var bullet = new EBullet(pos.x, pos.y);
bullet.setDirection(dir.x, dir.y);
bullet.setSpeed(speed);
};
}
);
////////////////////////////////////////////////////////////
var newgame = function(){
var name = document.evaluate(
'.//p[@class="username"]/a/text()',
document.body,
null,
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
null).snapshotItem(0).data;
var enemies = document.evaluate(
'.//*[@id="leftbar"]//img[@class="profile-image"]/@alt',
document.body,
null,
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
null
);
var enemy_id = enemies.snapshotItem(
Math.floor(
enemies.snapshotLength * Math.random()
)
).value;
for(var i=0; i < 10; i++){
var x = 30 + Math.random() * (window.innerWidth - 60);
var y = 30 + Math.random() * (window.innerHeight - 60);
new Rock(x,y);
}
new Player(name);
new Enemy(enemy_id);
};
var titleState = new GameTitleState();
Game.registerState('title', titleState);
var gamePlayState = new GamePlayingState();
gamePlayState.setInitializer(newgame);
Game.registerState('play', gamePlayState);
//debugger;
Game.gotoState('title');
})();
- Permalink
- このページへの個別リンクです。
- RAW
- 書かれたコードへの直接のリンクです。
- Packed
- 文字列が圧縮された書かれたコードへのリンクです。
- Userscript
- Greasemonkey 等で利用する場合の .user.js へのリンクです。
- Loader
- @require やソースコードが長い場合に多段ロードする Loader コミのコードへのリンクです。
- Metadata
- コード中にコメントで @xxx と書かれたメタデータの JSON です。