/* This software is licensed under the new BSD license. Copyright (c) 2011, Seussnu Koromotani All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the H.AltProject nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ var THIEF = { conf : { id : 'Thief', // was used "HTML's id", and "System tag". fontFace : 'monospace, sans-serif', textColor : '#FFFFFF', emTextColor : '#FF0000', bgColor : '#000000', grayColor : '#7f7f7f', gridStyle : '0px none #888888', // css td border options mapWidth : 60, mapHeight : 20, mapDepth : 256, language : null, languagePriolity : ['en', 'ja'], statusPosition : 'bottom', // top or bottom statusLineFormat : [ '${THIEF.global.actDG.%get@name%}:${THIEF.global.actDG.nowLV}' + ' ${THIEF.global.adventurer.%get@name%}' + ' ♥:${THIEF.global.adventurer.%get@hp%}' + ' STR:${THIEF.global.adventurer.%get@str%}' + ' CON:${THIEF.global.adventurer.%get@con%}' + ' DEX:${THIEF.global.adventurer.%get@dex%}' + ' INT:${THIEF.global.adventurer.%get@int%}' + ' POW:${THIEF.global.adventurer.%get@pow%}' + ' EDU:${THIEF.global.adventurer.%get@edu%}' + ' APP:${THIEF.global.adventurer.%get@app%}' + ' SAN:${THIEF.global.adventurer.%get@san%}' + ' AC:${THIEF.global.adventurer.%get@armorPoint%}', 'Lang:${THIEF.conf.language}' + ' Turn:${THIEF.global.turn}' + ' CT:${THIEF.global.adventurer.%get@ct%}' ], messagePosition : 'top', // top or bottom messageLines : 3, messageHistorySize : 256, // infomationOverlap : true, mainWindowPosition : 'left', // css float option (left, right, none) msgOverflow : 'hidden', // css overflow option (visible, scroll, hidden, auto) itemSort : ['$', '"', ']', '[', '%', '!', '?', '+', '=', '/', '(', ')', '*' ], debugmode : false }, global : { actLV : null, actDG : null, turn : 0, keycode : NaN, hist : [], adventurer : null, // Player size : { Window : {width:Number.NaN, height:Number.NaN}, Main : {width:Number.NaN, height:Number.NaN}, // Input, Message, Map, Status Input : {width:Number.NaN, height:Number.NaN}, // 入力欄 Message : {width:Number.NaN, height:Number.NaN}, // メッセージ欄(入力上部) Map : {width:Number.NaN, height:Number.NaN}, // マップ Status : {width:Number.NaN, height:Number.NaN}, // ステータス Infomation : {width:Number.NaN, height:Number.NaN}, // 情報出力(インベントリの内容など) Debug : {width:Number.NaN, height:Number.NaN} // デバッグ情報 } }, KEY : { // charCodeAt の値 Backspace : 8, Enter : 13, Shift : 16, Ctrl : 17, Alt : 18, Esc : 27, Space : 32, PageUp : 33, // ! PageDown : 34, // " End : 10035, // # = 35 Home : 36, // $ Left : 37, // % Up : 38, // & Right : 39, // ' Down : 40, // Comma : ','.charCodeAt(0), Period : '.'.charCodeAt(0), Minus : '-'.charCodeAt(0), Sharp : '#'.charCodeAt(0), LT : '<'.charCodeAt(0), GT : '>'.charCodeAt(0), Asterisk : '*'.charCodeAt(0), A : 65, B : 66, C : 67, D : 68, E : 69, F : 70, G : 71, H : 72, I : 73, J : 74, K : 75, L : 76, M : 77, N : 78, O : 79, P : 80, Q : 81, R : 82, S : 83, T : 84, U : 85, V : 86, W : 87, X : 88, Y : 89, Z : 90, a : 97, b : 98, c : 99, d : 100, e : 101, f : 102, g : 103, h : 104, i : 105, j : 106, k : 107, l : 108, m : 109, n : 110, o : 111, p : 112, q : 113, r : 114, s : 115, t : 116, u : 117, v : 118, w : 119, x : 120, y : 121, z : 122, // [ = WinKey = 91 // \ = WinKey = 92 F1 : 112, // = p F2 : 113, // = q F3 : 114, // = r F4 : 115, // = s F5 : 116, // = t F6 : 117, // = u F7 : 118, // = v F8 : 119, // = w F9 : 120, // = x F10 : 121, // = y F11 : 122, // = z F12 : 123 // = { = "Shift + [" }, PROP : { siz : 'siz', shapeWidth : 'shapeWidth', shapeHeight : 'shapeHeight', //shapeDepth : 'shapeDepth', inventoryWidth : 'inventoryWidth', inventoryHeight : 'inventoryHeight', inventoryDepth : 'inventoryDepth', inventoryCellSize : 'inventoryCellSize', myOwner : 'myOwner', mergeable : 'mergeable', stackable : 'stackable', symbol : 'symbol', name : 'name', color : 'color', displayName : 'displayName', symbolName : 'symbolName', alphabet : 'alphabet', actObjectList : 'actObjectList', active : 'active', ct : 'ct', maxct : 'maxct', dex : 'dex', hp : 'hp', attack : 'attack', equipable : 'equipable', slot : 'slot', slotName : 'slotName', db : 'db', ac : 'ac', armorPoint : 'armorPoint', equiped : 'equiped' } }; // // Override // //noinspection JSUnusedGlobalSymbols window.onload = function(onload) { return function () { if (!!onload) { onload(); } THIEF.util.init(); }; }(window.onload); //noinspection JSUnusedGlobalSymbols window.onresize = function(onresize) { return function() { if (!!onresize) { onresize(); } if (window.innerWidth) { THIEF.global.size.Window.width = window.innerWidth; THIEF.global.size.Window.height = window.innerHeight; } else { THIEF.global.size.Window.width = document.documentElement.clientWidth || document.body.clientWidth || document.body.scrollWidth; THIEF.global.size.Window.height = document.documentElement.clientHeight || document.body.clientHeight || document.body.scrollHeight; } /* THIEF.global.size.Main.width = document.getElementById('Main').offsetWidth; THIEF.global.size.Main.height = document.getElementById('Main').offsetHeight; THIEF.global.size.Input = document.getElementById('Input').offsetWidth; THIEF.global.size.Input = document.getElementById('Input').offsetHeight; THIEF.global.size.Message = document.getElementById('Message').offsetWidth; THIEF.global.size.Message = document.getElementById('Message').offsetHeight; THIEF.global.size.Map = document.getElementById('Map').offsetWidth; THIEF.global.size.Map = document.getElementById('Map').offsetHeight; THIEF.global.size.Status = document.getElementById('Status').offsetWidth; THIEF.global.size.Status = document.getElementById('Status').offsetHeight; THIEF.global.size.Infomation = document.getElementById('Infomation').offsetWidth; THIEF.global.size.Infomation = document.getElementById('Infomation').offsetHeight; THIEF.global.size.Debug = document.getElementById('Debug').offsetWidth; THIEF.global.size.Debug = document.getElementById('Debug').offsetHeight; */ }; }(window.onresize); //noinspection JSUnusedGlobalSymbols document.onkeypress = function(onkeypress) { return function(event) { if (!!onkeypress) { onkeypress(event); } THIEF.global.keycode = THIEF.util.getKeyCode(event); THIEF.util.next(); THIEF.global.keycode = 0; }; }(document.onkeypress); if (!Object.create) { Object.create = function(o) { var func = function() { }; func.prototype = o; return new func(); }; } if (!Math.sign) { Math.sign = function(num) { if (num > 0) { return 1; } else { if (num < 0) { return -1; } else { return 0; } } }; } if (!Math.dice) { Math.dice = function(fre, sides) { var val = 0; for (var i = 0; i < fre; i++) { val += Math.round(Math.random() * sides + 0.5); } return val; } } if (!Array.prototype.remove) { Array.prototype.remove = function(o) { var len = this.length; var result = false; for (var i = 0; i < len; i++) { if (!result && this[i] === o) { result = true; delete this[i]; } if (result) { // shifting this[i] = this[i + 1]; } } if (result) { this.length = len - 1; } return result; }; } if (!Array.prototype.getLast) { Array.prototype.getLast = function() { return this[this.length - 1]; }; } if (!Array.prototype.indexOf) { Array.prototype.indexOf = function(elt /*, from*/) { var len = this.length; var from = Number(arguments[1]) || 0; from = (from < 0) ? Math.ceil(from) : Math.floor(from); if (from < 0) { from += len; } for (; from < len; from++) { if (from in this && this[from] === elt) { return from; } } return -1; }; } // xxx : Manualable // $xxx : Common // _xxx : DebugModeOnly // THIEF.func = {}; THIEF.func.method = {}; THIEF.func.method.get = function (param) { var val; if (THIEF.global.debugmode) { if (typeof param === 'undefined') { alert('引数が指定されていません'); } } if (typeof this[param] === 'function') { val = this[param](); } else if (this[param]) { if (this[param][THIEF.conf.language]) { val = this[param][THIEF.conf.language]; } else { val = this[param]; for (var i = 0; i < THIEF.conf.languagePriolity.length; i++) { if (this[param][THIEF.conf.languagePriolity[i]]) { val = this[param][THIEF.conf.languagePriolity[i]]; break; } } } } return val; }; THIEF.func.method.isBasedOn = function (obj) { return this.uniqNumber === obj.uniqNumber; }; THIEF.func.method.removeFromInventory = function(obj) { var objX = obj.x; var objY = obj.y; THIEF.html.addDebugMessage(obj.get(THIEF.PROP.displayName) + 'を' + this.get(THIEF.PROP.displayName) + 'のインベントリ座標[' + obj.x + ', ' + obj.y + ']から除外...'); var shapeX = Math.ceil(obj.get(THIEF.PROP.shapeWidth) / this.get(THIEF.PROP.inventoryCellSize)) || 1; var shapeY = Math.ceil(obj.get(THIEF.PROP.shapeHeight) / this.get(THIEF.PROP.inventoryCellSize)) || 1; if (obj.myOwner === this) { obj.myOwner = null; obj.x = undefined; obj.y = undefined; for (var i = 0; i < shapeX; i++) { for (var j = 0; j < shapeY; j++) { THIEF.html.addDebugMessage('座標[' + (objX + i) + ', ' + (objY + j) + ']から除外'); this.inventory[objX + i][objY + j].remove(obj); THIEF.html.refreshXy(objX + i, objY + j); } } } THIEF.html.addDebugMessage('...' + obj.get(THIEF.PROP.displayName) + 'を' + this.get(THIEF.PROP.displayName) + 'のインベントリ座標[' + objX + ', ' + objY + ']から除外完了。'); return true; }; THIEF.func.method.addToInventory = function(obj, x, y) { var result = false; THIEF.html.addDebugMessage(obj.get(THIEF.PROP.displayName) + 'を' + this.get(THIEF.PROP.displayName) + 'のインベントリ座標[' + x + ', ' + y + ']に追加処理を開始...'); var target = this.getInventoryFreeSpace(obj, x, y); if (target.x >= 0 && target.y >= 0) { obj.x = target.x; obj.y = target.y; obj.myOwner = this; THIEF.html.addDebugMessage(target.x + ', ' + target.y + 'を基準に ' + target.width + 'x' + target.height + ' のサイズとして追加'); for (var i = target.x; i < target.x + target.width; i++) { for (var j = target.y; j < target.y + target.height; j++) { if (!this.inventory) { this.inventory = []; } if (!this.inventory[i]) { this.inventory[i] = []; } if (!this.inventory[i][j]) { this.inventory[i][j] = []; } // Item の場合のみアルファベットを設定 if (obj.isItem) { THIEF.html.addDebugMessage(obj.get(THIEF.PROP.displayName) + ' はアイテムなので、アルファベットの割り当てを行う...'); var alphabets = this.getInventoryAlphabets(); if (typeof obj.get(THIEF.PROP.alphabet) === 'string' && alphabets.indexOf(obj.get(THIEF.PROP.alphabet)) === -1) { // すでにアルファベットが登録済みで、かつ変更の必要は無い THIEF.html.addDebugMessage('すでに割りあたっているもの' + obj.get(THIEF.PROP.alphabet) + 'が再利用可能。'); } else { var alp = this.getInventoryNonExistAlphabet(); if (typeof alp === 'undefined') { alert('アルファベット関係の使用制限バグ'); } else { obj[THIEF.PROP.alphabet] = alp; } THIEF.html.addDebugMessage('新たに ' + alp + ' を割り当てた。') } THIEF.html.addDebugMessage('...アルファベットの割り当て処理完了。'); } this.inventory[i][j].push(obj); THIEF.html.addDebugMessage('[' + i + ', ' + j + ']に' + obj.get(THIEF.PROP.alphabet) + 'として追加'); result = true; } } } else { THIEF.html.addDebugMessage('追加可能なスペースがない。'); } THIEF.html.addDebugMessage('...' + obj.get(THIEF.PROP.displayName) + 'を' + this.get(THIEF.PROP.displayName) + 'のインベントリ座標[' + x + ', ' + y + ']に追加処理を完了。(return : ' + result + ')'); return result; }; THIEF.func.method.getInventoryRandomFreeSpace = function (obj) { var point; var challenge = this.get(THIEF.PROP.inventoryWidth) * this.get(THIEF.PROP.inventoryHeight); var counter = 0; while (counter < challenge) { counter++; var x = Math.round(Math.random() * this.get(THIEF.PROP.inventoryWidth)); var y = Math.round(Math.random() * this.get(THIEF.PROP.inventoryHeight)); point = this.getInventoryFreeSpace(obj, x, y); if (typeof point !== 'undefined' && this.inventory[x][y].getLast().type === THIEF.Data.Obj.$Floor.type) { //if (point.x === 'number' && typeof point.y === 'number') { break; } } // ランダムに見つからなければ、左上から順に検索 if (typeof point.x !== 'number' && typeof point.y !== 'number') { point = this.getInventoryFreeSpace(obj); } return point; }; THIEF.func.method.getInventoryFreeSpace = function(obj, _x, _y) { var invCellSize = this.get(THIEF.PROP.inventoryCellSize) || 10; var invWidth = this.get(THIEF.PROP.inventoryWidth) || 0; var invHeight = this.get(THIEF.PROP.inventoryHeight) || 0; var invDepth = this.get(THIEF.PROP.inventoryDepth) || 0; var objShapeWight = obj.get(THIEF.PROP.shapeWidth) || Math.ceil(obj.get(THIEF.PROP.siz) / 20) || 1; var objShapeHeight = obj.get(THIEF.PROP.shapeHeight) || Math.ceil(obj.get(THIEF.PROP.siz) / 20) || 1; var objInvShapeWight = Math.ceil(objShapeWight / invCellSize); var objInvShapeHeight = Math.ceil(objShapeHeight / invCellSize); var result; var startX = _x || 0; var startY = _y || 0; var once = (typeof _x === "number" || typeof _y === "number"); THIEF.html.addDebugMessage(this.get(THIEF.PROP.displayName) + 'のインベントリ(' + invHeight + ', ' + invWidth + ', ' + invDepth + ')の座標[' + _x + ', ' + _y + ']に' + obj.get(THIEF.PROP.displayName) + '(' + objInvShapeWight + ', ' + objInvShapeHeight + ')として格納できるか確認を開始...'); var free; outloop : for (var ancY = startY; ancY < invHeight; ancY++) { //alert(ancY + '行目を処理...'); for (var ancX = startX; ancX < invWidth; ancX++) { //alert(ancX + ', ' + ancY); free = true; //THIEF.html.addDebugMessage(ancX + ', ' + ancY + 'を確認...'); inloop : for (var targetY = ancY; targetY < ancY + objInvShapeHeight; targetY++) { for (var targetX = ancX + objInvShapeWight - 1; targetX >= ancX; targetX--) { if (this.inventory && this.inventory[targetX] && this.inventory[targetX][targetY]) { if (this.inventory[targetX][targetY].length <= invDepth) { var lastObj = this.inventory[targetX][targetY].getLast(); if (typeof lastObj === 'undefined' || lastObj.type !== THIEF.Data.Obj.$Wall.type && (obj.get(THIEF.PROP.stackable) || lastObj.get(THIEF.PROP.stackable)) /* || !lastObj.get(THIEF.PARAM.mergeable) */) { if (lastObj) { THIEF.html.addDebugMessage('[' + targetX + ', ' + targetY + ']にある' + lastObj.get(THIEF.PROP.displayName) + 'には重ねられる。'); } else { THIEF.html.addDebugMessage('[' + targetX + ', ' + targetY + ']には何も無いので重ねられる。'); } } else { free = false; ancX = targetX; //THIEF.html.addDebugMessage(targetX + ', ' + targetY + 'にある' + lastObj.get(THIEF.PARAM.name) + 'には重ねられない'); break inloop; } } else { free = false; ancX = targetX; //THIEF.html.addDebugMessage(targetX + ', ' + targetY + ' は高さが足りないからダメ'); break inloop; } } else { THIEF.html.addDebugMessage(targetX + ', ' + targetY + ' は空'); } } } if (free) { result = { x : ancX, y : ancY, width : objInvShapeWight, height : objInvShapeHeight }; break outloop; } if (once) { break outloop; } } } THIEF.html.addDebugMessage('...' + obj.get(THIEF.PROP.displayName) + '(' + objInvShapeWight + ', ' + objInvShapeHeight + ')として格納できるか確認を完了。'); return result; }; THIEF.func.method.attackedBy = function(obj) { THIEF.html.addDebugMessage(obj.get(THIEF.PROP.displayName) + ' による ' + this.get(THIEF.PROP.displayName) + ' への攻撃処理を開始...'); //my.slot[THIEF.PARAM.slot.hand] = selected[0]; var val; if (typeof obj.slot === 'undefined' || typeof obj.slot[THIEF.Data.Obj.$Item.slot] === 'undefined') { if (typeof obj.damage === 'undefined') { val = '1d2'; } else { val = obj.damage; } } else { val = obj.slot[THIEF.Data.Obj.$Item.slot].damage; } var revP = THIEF.util.infixToRevP(val); var damage = THIEF.util.execRevP(revP); THIEF.html.addDebugMessage('ベースダメージ : ' + damage); damage += obj.get(THIEF.PROP.db); THIEF.html.addDebugMessage('ダメージボーナス追加後 : ' + damage); var ac = this.get(THIEF.PROP.armorPoint); THIEF.html.addDebugMessage('AC適用後 : ' + (damage - ac)); damage -= ac; if (damage > 0) { THIEF.html.addDebugMessage(this.get(THIEF.PROP.displayName) + ' HP : ' + this.get(THIEF.PROP.hp) + ' -> ' + (this.get(THIEF.PROP.hp) - damage) + 'とする。'); this.hp -= damage; THIEF.html.addMessage(THIEF.util.getText(THIEF.Data.Text.attack, damage, this.get(THIEF.PROP.displayName), obj.get(THIEF.PROP.displayName))); } else { THIEF.html.addMessage(THIEF.util.getText(THIEF.Data.Text.defence, this.get(THIEF.PROP.displayName), obj.get(THIEF.PROP.displayName))); } obj.ct -= 100; if (this.get(THIEF.PROP.hp) <= 0) { THIEF.html.addDebugMessage('死亡処理を開始...'); THIEF.html.addMessage(THIEF.util.getText(THIEF.Data.Text.killBy, this.get(THIEF.PROP.displayName), obj.get(THIEF.PROP.displayName))); THIEF.global.actLV.removeFromInventory(this); this.next = function() { return true }; THIEF.html.addDebugMessage('...死亡処理を完了。') } THIEF.html.addDebugMessage('...攻撃処理を完了。'); return true; }; THIEF.func.method.getInventoryXyObjects = function(x, y) { return this.inventory[x][y]; }; THIEF.func.method.getInventoryXyLastObject = function(x, y) { var obj; // 必ずしも inventory の配列を完全に持っているとは限らないので注意すること if (this.inventory && this.inventory[x] && this.inventory[x][y]) { obj = this.inventory[x][y].getLast(); } return obj; }; THIEF.func.method.delInventoryObject = function(o) { this.delInventoryXyObject(o.x, o.y, o); }; THIEF.func.method.delInventoryXyObject = function(x, y, o) { this.inventory[x][y].remove(o); /* if (this.mapRefreshPointList) { this.mapRefreshPointList.push({ x: x, y: y }); } */ }; // ループ処理を加えること THIEF.func.method.getInventoryAlphabets = function() { var alphabets = []; if (this.inventory) { for (var i = 0; i < this.get(THIEF.PROP.inventoryWidth); i++) { if (this.inventory[i]) { for (var j = 0; j < this.get(THIEF.PROP.inventoryHeight); j++) { if (this.inventory[i][j]) { for (var k = 0; k < this.inventory[i][j].length; k++) { var alphabet = this.inventory[i][j][k].get(THIEF.PROP.alphabet); if (alphabet) { alphabets.push(alphabet); } } } } } } } return alphabets; }; THIEF.func.method.getInventoryNonExistAlphabet = function() { // 常にアルファベットの先頭が利用されないようにしている var key = THIEF.KEY; var index = key.a; return function() { var alphabets = this.getInventoryAlphabets(); var counter = 0; while (alphabets.indexOf(String.fromCharCode(index)) !== -1 && counter < 52) { counter++; index++; if (index === (key.z + 1)) { index = key.A; } else { if (index === (key.Z + 1)) { index = key.a; } } } var alphabet; if (counter < 52) { alphabet = String.fromCharCode(index); index++; } return alphabet; }; }(); // 配列を渡してカリー化(?) THIEF.func.method.selecter = function(objs, exec, immediately, abc) { var key = THIEF.KEY; var selected = []; var targets = []; var i, j; THIEF.html.addDebugMessage('セレクターを生成'); // root_abc var alphabetIndex = key.a; // 一覧出力 for (i = 0; i < THIEF.conf.itemSort.length; i++) { //アイテムがあるか確認 var first = true; for (j = 0; j < objs.length; j++) { if (THIEF.conf.itemSort[i] === objs[j].symbol) { if (first) { first = false; THIEF.html.addInfomation('' + objs[j].get(THIEF.PROP.symbolName) + ''); } var alphabet; if (abc) { alphabet = String.fromCharCode(alphabetIndex); alphabetIndex++; } else { alphabet = objs[j].alphabet || this.getInventoryNonExistAlphabet(); if (alphabet === null) { // ページ切り替え break; } objs[j].alphabet = alphabet; } THIEF.html.addInfomation(alphabet + ' - ' + objs[j].get(THIEF.PROP.displayName)); targets[alphabet] = objs[j]; } } } // 選択用関数返却 return function() { var finish = false; switch (THIEF.global.keycode) { case 0: break; case key.Asterisk: break; case key.Esc: finish = true; selected.length = 0; break; case key.Space: break; case key.Enter: finish = true; this.action = THIEF.func.method.action; break; // すべてを選択しない case key.Minus: if (immediately) { selected.push(undefined); finish = true; } else { selected.length = 0; for (var i = 0; i < targets.length; i++) { // 表示を初期化すること } } break; default: var item = targets[String.fromCharCode(THIEF.global.keycode)]; var itemID = THIEF.conf.id + '_' + String.fromCharCode(THIEF.global.keycode); if (typeof item !== 'undefined') { if (document.getElementById(itemID).innerHTML === ' - ') { document.getElementById(itemID).innerHTML = ' + '; selected.push(item); } else { document.getElementById(itemID).innerHTML = ' - '; selected.remove(item); } } if (immediately) { finish = true; } break; } var result = false; if (finish) { // 画面クリア THIEF.html.clearInfomation(''); // アクションを元に戻す this.action = THIEF.func.method.action; // 選択後のアクションを実行 result = exec(selected); } return result; }; }; THIEF.func.method.cpu = function() { THIEF.html.addDebugMessage('CPU行動開始...'); var dx = 0; var dy = 0; var diffX = THIEF.global.adventurer.x - this.x; var diffY = THIEF.global.adventurer.y - this.y; if (diffX > 0) { dx = 1; } else if (diffX < 0) { dx = -1; } if (diffY > 0) { dy = 1; } else if (diffY < 0) { dy = -1; } this.move(dx, dy); THIEF.html.addDebugMessage('...CPU行動完了'); return true; }; THIEF.func.method.move = function(dx, dy) { var result; THIEF.html.addDebugMessage('移動処理 move(' + dx + ', ' + dy + ') を開始...'); THIEF.html.addDebugMessage(THIEF.global.actLV.get(THIEF.PROP.displayName) + 'のインベントリ座標[' + (this.x + dx) + ', ' + (this.y + dy) + ']を調査を行う。'); var target = THIEF.global.actLV.getInventoryXyLastObject(this.x + dx, this.y + dy); if (this.get(THIEF.PROP.stackable) || target.get(THIEF.PROP.stackable)) { var sX = this.x; var sY = this.y; THIEF.html.addDebugMessage(this.get(THIEF.PROP.displayName) + '[' + this.x + ', ' + this.y + ']は' + target.get(THIEF.PROP.displayName) + '[' + (this.x + dx) + ', ' + (this.y + dy) + ']の上に移動する。'); THIEF.global.actLV.removeFromInventory(this); THIEF.html.addDebugMessage('移動前の座標[' + sX + ', ' + sY + ']の描画を更新'); THIEF.html.refreshXy(sX, sY); this.x = sX + dx; this.y = sY + dy; THIEF.global.actLV.addToInventory(this, this.x, this.y); THIEF.html.addDebugMessage('移動後の座標[' + this.x + ', ' + this.y + ']の描画を更新'); THIEF.html.refreshXy(this.x, this.y); this.ct -= 60; result = true; } else { if (target.get(THIEF.PROP.active)) { THIEF.html.addDebugMessage('active なものが対象なので攻撃してみる。'); target.attackedBy(this); result = true; } else { THIEF.html.addDebugMessage('インベントリ座標[' + (this.x + dx) + ', ' + (this.y + dy) + '] には移動できない。'); result = false; } } THIEF.html.addDebugMessage('...移動処理を完了。'); return result; }; THIEF.func.method.inputWait = function () { this.next = THIEF.func.method.manual; THIEF.html.addDebugMessage('入力待ち'); THIEF.html.addMessage(); // 改行 return false; }; // 基本は AI 処理とする THIEF.func.method.next = function() { return this.cpu(); }; THIEF.func.method.notify = function() { if (typeof this.inventory !== "undefined") { var width = this.get(THIEF.PROP.inventoryWidth); for (var x = 0; x < width; x++) { if (typeof this.inventory[x] === "undefined") { break; } var length = this.get(THIEF.PROP.inventoryHeight); for (var y = 0; y < length; y++) { if (typeof this.inventory[x][y] === "undefined") { break; } var height = this.inventory[x][y].length; for (var h = 0; h < height; h++) { this.inventory[x][y][h].notify(); } } } } // CT を増加 if (this.get(THIEF.PROP.active)) { if (this.ct > 100) { this.ct = 100; } if (typeof this.get(THIEF.PROP.dex) === undefined) { this[THIEF.PROP.ct] += this.get(THIEF.PROP.dex); } else { this[THIEF.PROP.ct] += 33; // test } THIEF.html.addDebugMessage(this.get(THIEF.PROP.displayName) + ' の CT を ' + this[THIEF.PROP.ct] + ' に増加。'); } // ct が 100 を超えていたら行動対象に登録 if (this.get(THIEF.PROP.ct) >= 100) { if (typeof this[THIEF.PROP.myOwner][THIEF.PROP.actObjectList] === "undefined") { this[THIEF.PROP.myOwner][THIEF.PROP.actObjectList] = []; } THIEF.html.addDebugMessage(this.get(THIEF.PROP.displayName) + ' を、オーナ:' + this[THIEF.PROP.myOwner].get(THIEF.PROP.displayName) + ' に登録。'); this[THIEF.PROP.myOwner][THIEF.PROP.actObjectList].push(this); } }; THIEF.func.method.action = function() { THIEF.html.addDebugMessage(this.get(THIEF.PROP.displayName) + ' の行動を開始...'); if (typeof this[THIEF.PROP.actObjectList] !== 'undefined') { // ct の小さい順にソートする for (var i = 0; i < this[THIEF.PROP.actObjectList].length - 2; i++) { var isSorted = true; for (var j = this[THIEF.PROP.actObjectList].length - 1; j >= (1 + i); j--) { if (this[THIEF.PROP.actObjectList][j].get(THIEF.PROP.ct) < this[THIEF.PROP.actObjectList][j - 1].get(THIEF.PROP.ct)) { var swap = this[THIEF.PROP.actObjectList][j]; this[THIEF.PROP.actObjectList][j] = this[THIEF.PROP.actObjectList][j - 1]; this[THIEF.PROP.actObjectList][j - 1] = swap; isSorted = false; } } if (isSorted) { break; } } // ct の大きい順(配列の最後部)から実行 var obj = this[THIEF.PROP.actObjectList].pop(); for (; typeof obj !== 'undefined'; obj = this[THIEF.PROP.actObjectList].pop()) { THIEF.html.addDebugMessage(obj.get(THIEF.PROP.displayName) + ' の処理を開始...'); if (obj.action() === false) { // 行動が完了しない場合、現在のオブジェクトを再登録して一時停止(結果的に入力を待つ) THIEF.html.addDebugMessage('...' + obj.get(THIEF.PROP.displayName) + ' は処理が完了しなかった。'); this[THIEF.PROP.actObjectList].push(obj); break; } THIEF.html.addDebugMessage('...処理を完了'); } } var result = true; if (typeof this[THIEF.PROP.actObjectList] === 'undefined' || this[THIEF.PROP.actObjectList].length === 0) { if (this.active && this.turnEnd !== THIEF.global.turn) { if (this.get(THIEF.PROP.ct) >= 100) { if (this.next()) { this.turnEnd = THIEF.global.turn; //this[THIEF.PARAM.ct] -= 100; result = true; } else { result = false; } } } } else if (this[THIEF.PROP.actObjectList].length > 0) { result = false; } THIEF.html.addDebugMessage('... ' + this.get(THIEF.PROP.displayName) + ' の行動を完了。 (result = ' + result + ')'); return result; }; THIEF.func.method.decay = function() { if (this.decayable) { this.dp--; if (this.dp <= 0) { THIEF.html.addMessage(THIEF.util.getText(THIEF.Data.Text.decay, this.name)); THIEF.global.actLV.delInventoryObject(this); } } return true; }; THIEF.func.method.manual = function() { var finish; var code = THIEF.global.keycode; if (code === 0) { finish = false; THIEF.html.addDebugMessage('無効なキー入力のため、何の処理もしない。') } else { THIEF.html.addDebugMessage(String.fromCharCode(code) + '(' + code + ')の入力に対する処理を開始...'); for (var name in THIEF.Data.Keymap) { if (THIEF.Data.Keymap[name].key !== 'undefined' && THIEF.Data.Keymap[name].key === code) { THIEF.html.addDebugMessage(this.get(THIEF.PROP.displayName) + ' の ' + name + ' コマンドを実行'); finish = THIEF.Data.Keymap[name].cmd.call(this); break; } } if (typeof finish === 'undefined') { if (code !== THIEF.KEY.Esc) { THIEF.html.addMessage(THIEF.util.getText(THIEF.Data.Text.commandNotFound, String.fromCharCode(code))); } finish = false; } THIEF.html.addDebugMessage('...' + String.fromCharCode(code) + '(' + code + ')の入力に対する処理を完了。(return : ' + finish + ')'); if (finish) { this.next = THIEF.func.method.inputWait; } } return finish; }; THIEF.func.method.spawn = function (obj) { THIEF.html.addDebugMessage('湧き処理を開始...'); // 空きスペースの検索条件 var point = this.getInventoryRandomFreeSpace(obj); this.addToInventory(obj, point.x, point.y); THIEF.html.refreshXy(obj.x, obj.y); THIEF.html.addDebugMessage('...湧き処理を完了'); return obj; }; THIEF.func.method.goTheStairs = function(trueIsUpFalseIsDown) { var floor = THIEF.global.actLV.getInventoryXyObjects(this.x, this.y); var stairs; var flag = false; // 足元に、階段があるか確認 for (var i = 0; i < floor.length; i++) { if (trueIsUpFalseIsDown && floor[i].symbol === THIEF.Data.Obj.UpStairs.symbol || !trueIsUpFalseIsDown && floor[i].symbol === THIEF.Data.Obj.DownStairs.symbol) { flag = true; stairs = floor[i]; break; } } if (flag) { if (trueIsUpFalseIsDown) { THIEF.html.addMessage(THIEF.util.getText(THIEF.Data.Text.goUpStairs, this.get(THIEF.PROP.displayName))); } else { THIEF.html.addMessage(THIEF.util.getText(THIEF.Data.Text.goDownStairs, this.get(THIEF.PROP.displayName))); } THIEF.util.createLevel(stairs.nextDun, stairs.nextLv); THIEF.global.actLV.removeFromInventory(this); var oldDG = THIEF.global.actDG; var oldLV = THIEF.global.actDG.nowLV; // 新しい階層をセット THIEF.global.actDG = stairs.nextDun; THIEF.global.actDG.nowLV = stairs.nextLv; THIEF.global.actLV = stairs.nextDun[stairs.nextLv]; // 対になる階段が泣ければ作成する if (!stairs.partner) { // ブランチと、通常で場合分け if (stairs.isBranch) { // ブランチの場合は新規作成 var branchStairs; if (trueIsUpFalseIsDown) { branchStairs = THIEF.util.factory(THIEF.Data.Obj.DownStairs); } else { branchStairs = THIEF.util.factory(THIEF.Data.Obj.UpStairs); } branchStairs.point = THIEF.global.actLV.getInventoryRandomFreeSpace(branchStairs); branchStairs.nextDun = oldDG; branchStairs.nextLv = oldLV; branchStairs.color = 'red'; // 互いに登録 stairs.partner = branchStairs; branchStairs.partner = stairs; THIEF.global.actLV.addToInventory(branchStairs, branchStairs.point.x, branchStairs.point.y); } else { // 通常の場合は既設にくっつける // 互いに登録 if (trueIsUpFalseIsDown) { stairs.partner = THIEF.global.actLV.downStairs; THIEF.global.actLV.downStairs.partner = stairs; } else { stairs.partner = THIEF.global.actLV.upStairs; THIEF.global.actLV.upStairs.partner = stairs; } } } THIEF.global.actLV.addToInventory(this, stairs.partner.point.x, stairs.partner.point.y); THIEF.html.refresh(); THIEF.util.updateStatus(); } else { if (trueIsUpFalseIsDown) { THIEF.html.addMessage(THIEF.util.getText(THIEF.Data.Text.doesNotGoUpStairs)); } else { THIEF.html.addMessage(THIEF.util.getText(THIEF.Data.Text.doesNotGoDownStairs)); } } return flag; }; THIEF.func.command = {}; // マニュアルコマンドで実行して良いもの THIEF.func.command.goUp = function() { return this.move(0, -1); }; THIEF.func.command.goDown = function() { return this.move(0, 1); }; THIEF.func.command.goLeft = function() { return this.move(-1, 0); }; THIEF.func.command.goRight = function() { return this.move(1, 0); }; THIEF.func.command.goUpperLeft = function() { return this.move(-1, -1); }; THIEF.func.command.goUpperRight = function() { return this.move(1, -1); }; THIEF.func.command.goLowerLeft = function() { return this.move(-1, 1); }; THIEF.func.command.goLowerRight = function() { return this.move(1, 1); }; THIEF.func.command.refreshHTML = function() { THIEF.html.refresh(); return false; }; THIEF.func.command.rest = function() { return true; }; THIEF.func.command.pickup = function() { var tmp = THIEF.global.actLV.getInventoryXyObjects(this.x, this.y); var finish = true; var objs = []; for (var i = 0; i < tmp.length; i++) { if (tmp[i].portable) { objs.push(tmp[i]); } } if (objs.length > 0) { var from = THIEF.global.actLV; var to = this; var exec = function(selected) { var result = false; for (var i = 0; i < selected.length; i++) { result = true; to.ct -= 20; // 重量オーバなら拾えない様にすること if (from.removeFromInventory(selected[i])) { to.addToInventory(selected[i]); THIEF.html.addMessage(THIEF.util.getText(THIEF.Data.Text.pickup, selected[i].get(THIEF.PROP.displayName), to.get(THIEF.PROP.displayName))); } else { THIEF.html.addMessage(THIEF.util.getText(THIEF.Data.Text.canNotPickup, selected[i].get(THIEF.PROP.displayName), to.get(THIEF.PROP.displayName))); } } return result; }; THIEF.html.addMessage(THIEF.util.getText(THIEF.Data.Text.pickupWhich)); this.action = THIEF.func.method.selecter(objs, exec, false, true); finish = false; } else { THIEF.html.addMessage(THIEF.util.getText(THIEF.Data.Text.noItemHere)); } return finish; }; THIEF.func.command.drop = function() { // とりあえず一次元配列化 var list = []; if (typeof this.inventory !== 'undefined') { for (var x = 0; x < this.get(THIEF.PROP.inventoryWidth); x++) { if (typeof this.inventory[x] === 'undefined') { continue; } for (var y = 0; y < this.get(THIEF.PROP.inventoryHeight); y++) { if (typeof this.inventory[x][y] === 'undefined') { continue; } var cell = this.inventory[x][y]; for (var i = 0; i < cell.length; i++) { //THIEF.html.addDebugMessage('[' + x + ', ' + y + '] ' + cell[i].get(THIEF.PARAM.displayName) ); list.push(cell[i]); } } } } // メソッド書き換え if (list.length > 0) { var from = this; var to = THIEF.global.actLV; var exec = function(selected) { var act = false; if (selected.length > 0) { act = true; for (var i = 0; i < selected.length; i++) { // 呪われてたら捨てれない様にすること if (from.removeFromInventory(selected[i])) { // 装備済みなら外す if (typeof from.slot !== 'undefined' && typeof from.slot[selected[i].slot] !== 'undefined') { delete from.slot[selected[i].slot].equiped; delete from.slot[selected[i].slot]; THIEF.html.addMessage(THIEF.util.getText(THIEF.Data.Text.equipNone, from.get(THIEF.PROP.displayName), selected[i].get(THIEF.PROP.slotName))); from.ct -= 100; } to.addToInventory(selected[i], from.x, from.y); from.ct -= 20; THIEF.html.addMessage(THIEF.util.getText(THIEF.Data.Text.drop, selected[i].get(THIEF.PROP.displayName), from.get(THIEF.PROP.displayName), to.get(THIEF.PROP.displayName))); } else { THIEF.html.addMessage(THIEF.util.getText(THIEF.Data.Text.canNotDrop, selected[i].get(THIEF.PROP.displayName), from.get(THIEF.PROP.displayName), to.get(THIEF.PROP.displayName))); } } // キャラクターは再配置して、一番上にする。 var adx = THIEF.global.adventurer.x; var ady = THIEF.global.adventurer.y; to.removeFromInventory(THIEF.global.adventurer); to.addToInventory(THIEF.global.adventurer, adx, ady); THIEF.html.refreshXy(adx, ady); } return act; }; this.action = THIEF.func.method.selecter(list, exec, false, false); //result = this.action(); //result = false; } else { THIEF.html.addMessage(THIEF.util.getText(THIEF.Data.Text.inventoryIsEmpty)); } return false; }; THIEF.func.command.showInventory = function() { THIEF.html.addDebugMessage(this.get(THIEF.PROP.displayName) + 'のインベントリ表示を開始...'); var list = []; if (typeof this.inventory !== 'undefined') { for (var x = 0; x < this.get(THIEF.PROP.inventoryWidth); x++) { if (typeof this.inventory[x] === 'undefined') { continue; } for (var y = 0; y < this.get(THIEF.PROP.inventoryHeight); y++) { if (typeof this.inventory[x][y] === 'undefined') { continue; } var cell = this.inventory[x][y]; for (var i = 0; i < cell.length; i++) { THIEF.html.addDebugMessage('[' + x + ', ' + y + '] ' + cell[i].get(THIEF.PROP.displayName)); list.push(cell[i]); } } } } if (list.length === 0) { THIEF.html.addMessage(THIEF.util.getText(THIEF.Data.Text.inventoryIsEmpty)); } else { this.action = THIEF.func.method.selecter(list, THIEF.func.method.inputWait, true, false); } THIEF.html.addDebugMessage('...' + this.get(THIEF.PROP.displayName) + 'のインベントリ表示を完了。'); return false; }; THIEF.func.command.printHistory = function() { var key = THIEF.KEY; var index = THIEF.global.hist.length - 1; //for (var i = THIEF.conf.messageLines; i >= 0; i--) { for (var i = THIEF.conf.mapHeight; i >= 0; i--) { var hoge = THIEF.global.hist.length - i; if (typeof THIEF.global.hist[hoge] !== 'undefined') { THIEF.html.addInfomation(THIEF.global.hist[hoge].turn + ' : ' + THIEF.global.hist[hoge].text); } } THIEF.html.addInfomation('
--Now--
'); this.action = function() { var finish = false; switch (THIEF.global.keycode) { case key.Esc: finish = true; break; // Next case key.b: case key.k: case key.p: case key.PageUp: case key.Up: case key.LT: index--; if (index < 0) { index = THIEF.global.hist.length - 1; } break; // Prev case key.n: case key.j: case key.Enter: case key.Space: case key.PageDown: case key.Down: case key.GT: index++; if (index >= THIEF.global.hist.length) { index = 0; } break; default: break; } if (finish) { // 画面クリア THIEF.html.clearInfomation(''); // アクションを元に戻す this.action = THIEF.func.method.action; } else { THIEF.html.clearInfomation(); //for (var i = THIEF.conf.messageLines - 1; i >= 0; i--) { for (var i = THIEF.conf.mapHeight; i >= 0; i--) { if (typeof THIEF.global.hist[index - i] !== 'undefined') { THIEF.html.addInfomation(THIEF.global.hist[index - i].turn + ' : ' + THIEF.global.hist[index - i].text); if (index - i === THIEF.global.hist.length - 1) { THIEF.html.addInfomation('
--Now--
'); } } } } return false; }; return false; }; THIEF.func.command.extendedCommand = function() { THIEF.html.setInput('#'); var com = []; for (var name in THIEF.Data.Keymap) { if (typeof THIEF.Data.Keymap[name].text !== 'undefined') { com.push(name); } } com.sort(); THIEF.html.addInfomation('-- COMMAND HINT --'); for (var i = 0; i < com.length; i++) { THIEF.html.addInfomation(com[i]); } var line = ''; var target = ''; this.action = function() { var finish = false; var update = false; if ((THIEF.KEY.a <= THIEF.global.keycode && THIEF.global.keycode <= THIEF.KEY.z) || (THIEF.KEY.A <= THIEF.global.keycode && THIEF.global.keycode <= THIEF.KEY.Z)) { line += String.fromCharCode(THIEF.global.keycode); update = true; } else if (THIEF.global.keycode === THIEF.KEY.Space) { line += ' '; update = true; } else if (THIEF.global.keycode === THIEF.KEY.Backspace) { line = line.slice(0, line.length - 1); update = true; } else if (THIEF.global.keycode === THIEF.KEY.Esc) { THIEF.html.setInput(' '); THIEF.html.clearInfomation(); this.action = THIEF.func.method.action; } else if (THIEF.global.keycode === THIEF.KEY.Enter) { THIEF.html.setInput(' '); THIEF.html.clearInfomation(); this.action = THIEF.func.method.action; if (typeof THIEF.Data.Keymap[target].cmd === 'function') { THIEF.global.keycode = 0; finish = THIEF.Data.Keymap[target].cmd.call(this); } else { THIEF.html.addMessage(THIEF.util.getText(THIEF.Data.Text.commandNotFound, line)); } } if (update) { THIEF.html.setInput('#' + line); var count = 0; for (var i = 0; i < com.length; i++) { if (com[i].indexOf(line, 0) === 0) { target = com[i]; count++; } } if (count === 1) { THIEF.html.addInput('' + target.slice(line.length, target.length) + ''); } else { THIEF.html.addInput('¦'); target = ''; } } return finish; }; return false; }; THIEF.func.command.equip = function () { // とりあえず一次元配列化 var list = []; if (typeof this.inventory !== 'undefined') { for (var x = 0; x < this.get(THIEF.PROP.inventoryWidth); x++) { if (typeof this.inventory[x] === 'undefined') { continue; } for (var y = 0; y < this.get(THIEF.PROP.inventoryHeight); y++) { if (typeof this.inventory[x][y] === 'undefined') { continue; } var cell = this.inventory[x][y]; for (var i = 0; i < cell.length; i++) { list.push(cell[i]); } } } } var my = this; // メソッド書き換え if (list.length > 0) { var exec = function(selected) { var act = false; if (selected.length > 0) { act = true; if (typeof my.slot === 'undefined') { my.slot = {}; } // 既に付けている装備を外す必要があるか if (typeof selected[0] !== 'undefined' && typeof my.slot[selected[0].slot] !== 'undefined') { if (selected[0] === my.slot[selected[0].slot]) { THIEF.html.addMessage(THIEF.util.getText(THIEF.Data.Text.equipAlready, selected[0].get(THIEF.PROP.displayName), my.get(THIEF.PROP.displayName))); selected[0] = undefined; } else { // 何も渡されないなら、手の装備を外す THIEF.html.addMessage(THIEF.util.getText(THIEF.Data.Text.equipNone, my.get(THIEF.PROP.displayName), selected[0].get(THIEF.PROP.slotName))); delete my.slot[selected[0].slot].equiped; delete my.slot[selected[0].slot]; } } // 装備 if (typeof selected[0] !== 'undefined') { my.slot[selected[0].slot] = selected[0]; my.slot[selected[0].slot].equiped = true; THIEF.html.addMessage(THIEF.util.getText(THIEF.Data.Text.equipItem, selected[0].get(THIEF.PROP.displayName), my.get(THIEF.PROP.displayName), selected[0].get(THIEF.PROP.slotName))); THIEF.html.addDebugMessage(selected[0].get(THIEF.PROP.displayName) + ' を装備した。'); my.ct -= 100; } } return act; }; this.action = THIEF.func.method.selecter(list, exec, true, false); } else { THIEF.html.addMessage(THIEF.util.getText(THIEF.Data.Text.inventoryIsEmpty)); } return false; }; THIEF.func.command.goUpTheStairs = function() { return THIEF.func.method.goTheStairs.call(this, true); }; THIEF.func.command.goDownTheStairs = function() { return THIEF.func.method.goTheStairs.call(this, false); }; THIEF.util = {}; THIEF.util.init = function() { THIEF.html.addDebugMessage('初期化(ターン0)を開始...'); // ブラウザ言語の取得 THIEF.conf.language = (THIEF.conf.language // || navigator.browserLanguage || navigator.language || navigator.userLanguage).substr(0, 2); //THIEF.conf.language = 'en'; // シナリオの読み込み THIEF.util.loadScenario(); // HTML タグ初期化 THIEF.html.init(); // ヘルプの表示 THIEF.html.showCommandHelp(); // テスト用コードの実行 THIEF.util.test(); THIEF.html.refresh(); // 1 ターン目開始 THIEF.global.turn = 0; THIEF.html.addDebugMessage('...初期化(ターン0)を完了。'); THIEF.util.next(); }; THIEF.util.test = function() { THIEF.global.adventurer = THIEF.util.factory(THIEF.Data.Obj.Human); THIEF.global.adventurer.color = 'red'; THIEF.global.adventurer.next = THIEF.global.adventurer.inputWait; THIEF.global.adventurer.name = { ja : '冒険者', en : 'Adventurer' }; THIEF.global.adventurer.inventoryDepth = 1; THIEF.global.adventurer.inventoryHeight = 10; THIEF.global.adventurer.inventoryWidth = 10; THIEF.global.adventurer.inventoryCellSize = 1; THIEF.global.adventurer.ct = 100; var point = THIEF.global.actLV.getInventoryRandomFreeSpace(THIEF.global.adventurer); THIEF.global.actLV.addToInventory(THIEF.global.adventurer, point.x, point.y); var tmp; tmp = THIEF.util.factory(THIEF.Data.Obj.DebugDagger); tmp.alphabet = 'k'; THIEF.global.adventurer.addToInventory(tmp); tmp = THIEF.util.factory(THIEF.Data.Obj.DebugArmor); tmp.alphabet = 'r'; THIEF.global.adventurer.addToInventory(tmp); //THIEF.global.actLV.spawn(THIEF.util.factory(THIEF.Data.Obj.DebugDaemon)); //THIEF.global.actLV.spawn(THIEF.util.factory(THIEF.Data.Obj.DebugDaemon)); //THIEF.global.actLV.spawn(THIEF.util.factory(THIEF.Data.Obj.DebugDaemon)); THIEF.global.actLV.spawn(THIEF.util.factory(THIEF.Data.Obj.DebugHighArmor)); THIEF.global.actLV.spawn(THIEF.util.factory(THIEF.Data.Obj.DebugHighDagger)); }; THIEF.util.next = function () { if (THIEF.util.step()) { THIEF.html.addDebugMessage('100ms後にstepを再度実行'); //noinspection DynamicallyGeneratedCodeJS setTimeout(THIEF.util.next, 100); } // 毎ターンの終了時、または中断時(ユーザの入力待ち)に、画面の更新を行う //THIEF.html.addMessage(); }; THIEF.util.step = function () { var completion = true; var compCounter = 15; return function () { if (completion) { THIEF.global.turn++; THIEF.html.addDebugMessage('ターン:' + THIEF.global.turn + ' を開始...'); THIEF.html.addDebugMessage('通知を開始...'); THIEF.global.actLV.notify(); THIEF.html.addDebugMessage('...通知を完了'); // 各レベルに実施させること if (!THIEF.global.actLV.safety && Math.random() > 0.9) { THIEF.global.actLV.spawn(THIEF.util.factory(THIEF.Data.Obj.DebugDaemon)); } } else { THIEF.html.addDebugMessage('ターン:' + THIEF.global.turn + ' を再開...'); } THIEF.html.addDebugMessage('アクティブになったオブジェクトの処理を実施...'); completion = THIEF.global.actLV.action(); THIEF.html.addDebugMessage('...アクティブになったオブジェクトの処理を完了'); THIEF.util.updateStatus(); if (compCounter < 0) { compCounter = 15; completion = false; THIEF.html.addDebugMessage('無限ループにならないように、15回以上ループするなら強制的にブレイク。') } else if (completion) { compCounter--; } else { compCounter = 15; } if (completion) { THIEF.html.addDebugMessage('...ターン:' + THIEF.global.turn + ' を完了。'); } else { THIEF.html.addDebugMessage('...ターン:' + THIEF.global.turn + ' を中断。(ユーザからの入力待ち)'); THIEF.html.addDebugMessage(); } return completion; }; }(); THIEF.util.updateStatus = function () { // 何かが行動するたびにステータス表示を更新 THIEF.html.addDebugMessage('ステータス画面の更新開始...'); for (var i = 0; i < THIEF.conf.statusLineFormat.length; i++) { document.getElementById(THIEF.conf.id + 'Status' + i).innerHTML = THIEF.util.getText(THIEF.conf.statusLineFormat[i]); } THIEF.html.addDebugMessage('...ステータス画面の更新完了。'); }; THIEF.util.getKeyCode = function(e) { // Browser bug fix if (!e) { //noinspection AssignmentToFunctionParameterJS e = window.event; } // キーコード取得 var code = 0; //noinspection PlatformDetectionJS if (document.all) { code = event.keyCode; } else if (document.getElementById) { code = parseInt((parseInt(e.keyCode) === 0) ? e.charCode : e.keyCode); } else { // document.layers code = parseInt(e.which); } // 基本的にはブラウザに処理を渡さない var block = true; // コンビネーションキー "だけ" 押されたイベントは無視 // ブラウザにも処理を回す if (code === THIEF.KEY.Shift || code === THIEF.KEY.Ctrl || code === THIEF.KEY.Alt) { code = 0; block = false; } else { if (e.shiftKey) { // } else { switch (code) { // ファンクションキーはブラウザに処理を回す // 他の文字とコードはかぶっているが問題なしとする case THIEF.KEY.F1: case THIEF.KEY.F2: case THIEF.KEY.F3: case THIEF.KEY.F4: case THIEF.KEY.F5: case THIEF.KEY.F6: case THIEF.KEY.F7: case THIEF.KEY.F8: case THIEF.KEY.F9: case THIEF.KEY.F10: case THIEF.KEY.F11: case THIEF.KEY.F12: block = false; //code = 0; break; case 35: // Shift + 3 = #, or End code = THIEF.KEY.End; break; default: break; } } if (e.ctrlKey) { switch (code) { // コピペ・ブックマーク用 case THIEF.KEY.a: case THIEF.KEY.c: case THIEF.KEY.d: case THIEF.KEY.v: if (e.ctrlKey) { block = false; code = 0; } break; default: break; } } } // ブラウザへのイベント伝播を防止 if (block) { if (e.preventDefault) { e.preventDefault(); e.stopPropagation(); } e.returnValue = false; e.cancelBubble = true; } return code; }; THIEF.util.factory = function() { var tree = []; var id = 0; return function(obj) { //THIEF.html.addDebugMessage('オブジェクトの生成処理を開始...'); //THIEF.html.addDebugMessage('マスターの有無を確認...'); if (typeof obj !== 'undefined' && typeof obj.uniqNumber === 'undefined') { THIEF.html.addDebugMessage('要求オブジェクトのマスタが存在しないので作成開始...'); var inh = obj.inheritance || '$'; var inhPrev = '$'; var stack = []; while (typeof inh !== 'undefined') { stack.push(inh); inh = THIEF.Data.Obj[inh].inheritance; } if (stack.getLast() !== '$') { stack.push('$'); } THIEF.html.addDebugMessage('以下の継承を遡って親マスタの有無を確認'); THIEF.html.addDebugMessage('this,' + stack.toString() + '.'); // 以下の順番で作成します var propName; while (stack.length > 0) { inhPrev = inh || '$'; inh = stack.pop(); if (typeof THIEF.Data.Obj[inh].uniqNumber === 'undefined') { THIEF.html.addDebugMessage(inh + ' のマスタが生成されていないので作成...'); if (typeof THIEF.Data.Obj[inh].uniqNumber === 'undefined') { THIEF.Data.Obj[inh].uniqNumber = id; id++; } if (inh === '$') { tree[THIEF.Data.Obj[inh].uniqNumber] = {}; THIEF.html.addDebugMessage('以下のプロパティを Data.Obj.' + inh + ' からコピー...'); for (propName in THIEF.func.method) { if (THIEF.func.method.hasOwnProperty(propName)) { tree[THIEF.Data.Obj[inh].uniqNumber][propName] = THIEF.func.method[propName]; THIEF.html.addDebugMessage('.' + propName); } } for (propName in THIEF.func.command) { if (THIEF.func.command.hasOwnProperty(propName)) { tree[THIEF.Data.Obj[inh].uniqNumber][propName] = THIEF.func.command[propName]; THIEF.html.addDebugMessage('.' + propName); } } for (propName in THIEF.Data.Prop) { if (THIEF.Data.Prop.hasOwnProperty(propName)) { tree[THIEF.Data.Obj[inh].uniqNumber][propName] = THIEF.Data.Prop[propName]; THIEF.html.addDebugMessage('.' + propName); } } for (propName in THIEF.Data.Obj.$) { if (THIEF.Data.Obj.$.hasOwnProperty(propName)) { tree[THIEF.Data.Obj[inh].uniqNumber][propName] = THIEF.Data.Obj.$[propName]; THIEF.html.addDebugMessage('.' + propName); } } THIEF.html.addDebugMessage('...コピー完了'); } else { THIEF.html.addDebugMessage(inhPrev + 'マスタを元に' + inh + 'のマスタを作成'); tree[THIEF.Data.Obj[inh].uniqNumber] = Object.create(tree[THIEF.Data.Obj[inhPrev].uniqNumber]); THIEF.html.addDebugMessage('以下のプロパティを Data.Obj.' + inh + ' からコピー...'); for (propName in THIEF.Data.Obj[inh]) { if (THIEF.Data.Obj[inh].hasOwnProperty(propName)) { tree[THIEF.Data.Obj[inh].uniqNumber][propName] = THIEF.Data.Obj[inh][propName]; THIEF.html.addDebugMessage('.' + propName); } } THIEF.html.addDebugMessage('.inheritance = ' + inhPrev); tree[THIEF.Data.Obj[inh].uniqNumber].inheritance = inhPrev; THIEF.html.addDebugMessage('...コピー完了'); } THIEF.html.addDebugMessage('...作成完了'); } } inhPrev = inh; THIEF.html.addDebugMessage('...作成完了'); obj.uniqNumber = id; id++; if (obj !== THIEF.Data.Obj.$) { THIEF.html.addDebugMessage('要求オブジェクトのマスタ作成開始...'); THIEF.html.addDebugMessage(inhPrev + ' より雛形オブジェクトを作成'); tree[obj.uniqNumber] = Object.create(tree[THIEF.Data.Obj[inhPrev].uniqNumber]); THIEF.html.addDebugMessage('Own Property をコピー開始...'); for (propName in obj) { if (obj.hasOwnProperty(propName)) { tree[obj.uniqNumber][propName] = obj[propName]; THIEF.html.addDebugMessage('.' + propName); } } THIEF.html.addDebugMessage('.inheritance = ' + inhPrev); tree[obj.uniqNumber].inheritance = inhPrev; THIEF.html.addDebugMessage('...コピー完了'); THIEF.html.addDebugMessage('...マスタ作成完了'); } //THIEF.html.addDebugMessage('...マスターの生成を完了'); } else { //THIEF.html.addDebugMessage('...マスターは生成済み'); } THIEF.html.addDebugMessage(tree[obj.uniqNumber].get(THIEF.PROP.displayName) + 'マスターからインスタンスを作成'); THIEF.html.addDebugMessage('初期化を開始...'); var val = Object.create(tree[obj.uniqNumber]); for (propName in val) { if (typeof val[propName] === 'string' && val[propName].charAt(0) === '_') { THIEF.html.addDebugMessage(propName + ' の変数を初期化します'); val[propName] = THIEF.util.execRevP(THIEF.util.infixToRevP(val[propName].slice(1))); THIEF.html.addDebugMessage(propName + ' : ' + val[propName]); } } if (typeof val.pow === 'number' && typeof val.san !== 'number') { val.san = val.pow * 5; THIEF.html.addDebugMessage('san : ' + val.san); } if (typeof val.hp === 'undefined') { val.hp = Math.ceil((val.con + val.siz) / 2); } THIEF.html.addDebugMessage('...初期化完了'); //THIEF.html.addDebugMessage('...オブジェクトの生成処理を完了'); return val; }; }(); // TODO THIEF.util.loadScenario = function () { THIEF.html.addDebugMessage('シナリオ読み込み開始...'); THIEF.util.createDungeon(THIEF.Data.Scenario[THIEF.Data.Scenario.$.root]); THIEF.global.actDG = THIEF.Data.Scenario[THIEF.Data.Scenario.$.root]; THIEF.html.addDebugMessage('開始レベルを作成開始...'); THIEF.util.createLevel(THIEF.global.actDG, THIEF.global.actDG.nowLV); THIEF.global.actLV = THIEF.global.actDG[THIEF.global.actDG.nowLV]; THIEF.html.addDebugMessage('...開始レベルを作成完了'); THIEF.html.addDebugMessage('...シナリオ読み込み完了'); }; THIEF.util.createDungeon = function (dun) { THIEF.html.addDebugMessage('ダンジョン(' + THIEF.func.method.get.call(dun, THIEF.PROP.name) + ')を作成...'); // 開始レベルは 1 dun.nowLV = dun.startLevel || 1; if (dun.hierarchy) { for (var i = 0; i < dun.hierarchy.length; i++) { var base = dun.hierarchy[i].base; var range = dun.hierarchy[i].range || 0; var chance = dun.hierarchy[i].chance || 100; if (chance > Math.random() * 100) { // シナリオの場所決定 var rand = 0; for (var j = 0; j < range * 2; j++) { rand = Math.round(Math.random() * range - 0.5); if (typeof dun[base + rand] === 'undefined') { break; } if (j === range * 2 - 1) { for (var k = 0; k < range; k++) { if (typeof dun[base + k] === 'undefined') { rand = k; break; } } } } if (typeof dun[base + rand] === 'undefined') { THIEF.html.addDebugMessage('第 ' + (base + rand) + ' レベルに個別シナリオを格納'); dun[base + rand] = dun.hierarchy[i]; if (dun.hierarchy[i].branch) { THIEF.util.createDungeon(THIEF.Data.Scenario[dun.hierarchy[i].branch]); } } else { THIEF.html.addDebugMessage('base + range の範囲内に開いているレベルがありません。'); } } else { THIEF.html.addDebugMessage(dun.hierarchy[i].level + ' or ' + dun.hierarchy[i].branch + ' は作成しません。'); } } } dun.get = THIEF.func.method.get; THIEF.html.addDebugMessage('...ダンジョンの作成を完了'); }; THIEF.util.createLevel = function(dun, lvNum) { if (dun[lvNum] && dun[lvNum].created) { THIEF.html.addDebugMessage(THIEF.func.method.get.call(dun, THIEF.PROP.name) + 'の第 ' + lvNum + ' レベルは既に作成済み'); return; } else { THIEF.html.addDebugMessage(THIEF.func.method.get.call(dun, THIEF.PROP.name) + 'の第 ' + lvNum + ' レベルを作成...'); } var level = THIEF.util.factory(THIEF.Data.Obj.$Level); level[THIEF.PROP.inventoryWidth] = dun.width || THIEF.Data.Scenario.$.width || 60; level[THIEF.PROP.inventoryHeight] = dun.height || THIEF.Data.Scenario.$.height || 20; level[THIEF.PROP.inventoryDepth] = dun.depth || THIEF.Data.Scenario.$.depth || 256; level.inventoryCellSize = 1; var template = {}; if (dun[lvNum]) { THIEF.html.addDebugMessage('個別シナリオのテンプレート展開...'); // レベル系テンプレートの読み込み if (dun[lvNum].level) { if (typeof dun[lvNum].level === 'string') { dun[lvNum].level = [dun[lvNum].level]; } THIEF.html.addDebugMessage('複数のテンプレートがしていされているため逐次的に書式コピー'); for (var i = 0; i < dun[lvNum].level.length; i++) { for (var name in THIEF.Data.Scenario.$Template[dun[lvNum].level[i]]) { template[name] = THIEF.Data.Scenario.$Template[dun[lvNum].level[i]][name] } } } // 分岐系テンプレートの読み込み if (dun[lvNum].branch) { template.branchDun = THIEF.Data.Scenario[dun[lvNum].branch]; template.branchDir = dun[lvNum].branchDir; if (typeof template.branchDir === 'undefined') { alert('分岐への移動方法 (branchDir) が指定されて居ません.'); } template.branchDirPoint = dun[lvNum].branchDirPoint; } THIEF.html.addDebugMessage('...展開完了。'); } // テンプレートに基づいて処理を実行 // for (name in obj) は、オンオフの処理が後々面倒になりそうなので、 // ここで逐次処理にする if (template.map) { level.inventory = THIEF.util.mapParser(template.map); level[THIEF.PROP.inventoryWidth] = level.inventory.length; level[THIEF.PROP.inventoryHeight] = level.inventory[0].length; } else if (template.width && template.height) { level.inventory = THIEF.util.createMap(template.width, template.height); } else { level.inventory = THIEF.util.createMap(level[THIEF.PROP.inventoryWidth], level[THIEF.PROP.inventoryHeight]); } if (template.upStairs) { } else { if (lvNum > 1) { var upStairs = THIEF.util.factory(THIEF.Data.Obj.UpStairs); upStairs.nextDun = dun; upStairs.nextLv = lvNum - 1; upStairs.point = level.getInventoryRandomFreeSpace(upStairs); level.addToInventory(upStairs, upStairs.point.x, upStairs.point.y); level.upStairs = upStairs; } } if (template.downStairs) { } else { if (lvNum < THIEF.global.actDG.levelNum) { var downStairs = THIEF.util.factory(THIEF.Data.Obj.DownStairs); downStairs.nextDun = dun; downStairs.nextLv = lvNum + 1; downStairs.point = level.getInventoryRandomFreeSpace(downStairs); level.addToInventory(downStairs, downStairs.point.x, downStairs.point.y); level.downStairs = downStairs; } } if (template.branchDun) { var branchWay; switch (template.branchDir) { case 'up': case '<': branchWay = THIEF.util.factory(THIEF.Data.Obj.UpStairs); branchWay.nextDun = template.branchDun; branchWay.nextLv = template.branchDun.startLevel || 1; if (typeof branchWay.nextDun[branchWay.nextLv] === 'undefined') { branchWay.nextDun[branchWay.nextLv] = {}; } branchWay.nextDun[branchWay.nextLv].branchDun = dun; branchWay.nextDun[branchWay.nextLv].branchDir = 'down'; break; case 'down': case '>': branchWay = THIEF.util.factory(THIEF.Data.Obj.DownStairs); branchWay.nextDun = template.branchDun; branchWay.nextLv = template.branchDun.startLevel || 1; if (typeof branchWay.nextDun[branchWay.nextLv] === 'undefined') { branchWay.nextDun[branchWay.nextLv] = {}; } branchWay.nextDun[branchWay.nextLv].branchDun = dun; branchWay.nextDun[branchWay.nextLv].branchDir = 'up'; break; case 'teleport': default: break; } if (template.branchDirPoint) { branchWay.point = template.branchDirPoint; } else { branchWay.point = level.getInventoryRandomFreeSpace(branchWay) } branchWay.color = 'red'; branchWay.isBranch = true; level.addToInventory(branchWay, branchWay.point.x, branchWay.point.y); } if (template.safety) { level.safety = template.safety; } level.created = true; dun[lvNum] = level; THIEF.html.addDebugMessage('...レベルの作成を完了'); }; THIEF.util.createMap = function(width, height) { var i, j; var map = []; var x, y; // マップ作成、外壁を初期化 for (i = 0; i < width; i++) { var line = []; for (j = 0; j < height; j++) { line[j] = []; if (i === 0 || j === 0 || i === (width - 1) || j === (height - 1)) { line[j].push(THIEF.util.factory(THIEF.Data.Obj.NonDiggableWall)); } } map[i] = line; } // マップ分割 var maxRooms = Math.floor(Math.random() * (width + height) / 10) + (width + height) / 20; var anchors = []; // 部屋の起点(一番左上=若い座標) var spaceEdges = [ {x:1, y:1} ]; // 左上は常にスペースの角のため var roonCount = 0; var roop = 0; var roopLimit = 300; // 300 回くらい試行して駄目ならあきらめる while (roonCount < maxRooms && roop < roopLimit) { roop++; //アンカーは分割線(通路)の開始位置 //アンカーを計算(周囲 2(=1*2) マスと、さらに 2 マス(部屋壁の厚み分) は引いている) var ax = Math.floor((Math.random() * (width - width * 0.1 - 4) ) + width * 0.1 + 2); var ay = Math.floor((Math.random() * (height - height * 0.1 - 4) ) + height * 0.1 + 2); // アンカーに、既に部屋があればやり直し if (map[ax][ay].length !== 0) { continue; } // 距離チェック var lx = 0; var hx = 0; var ly = 0; var hy = 0; // 左壁、または分割線までの距離 for (i = ax - 1; i > 0; i--) { if (map[i][ay].length === 0) { lx++; } else { break; } } // 右壁、または分割線までの距離 for (i = ax + 1; i < width; i++) { if (map[i][ay].length === 0) { hx++; } else { break; } } // 上壁、または分割線までの距離 for (i = ay - 1; i > 0; i--) { if (map[ax][i].length === 0) { ly++; } else { break; } } // 下壁、または分割線までの距離 for (i = ay + 1; i < height; i++) { if (map[ax][i].length === 0) { hy++; } else { break; } } // 壁、または分割線までの最小距離 var mx = Math.min(lx, hx); var my = Math.min(ly, hy); // 最短距離が狭すぎるならやり直し // 目安として、中に幅(壁含まず)が 3x5 より大きい部屋を作れるようにしている if (Math.max(mx, my) < 5 || (Math.max(mx, my) === 5 && Math.min(mx, my) < 7 )) { continue; } // 一般的にマップ全体は横幅の方が長いと仮定している // 分割は、最初は縦、次は横の方がなんとなく綺麗になる if ((roonCount % 2 === 0 && my > mx) || (roonCount % 2 === 1 && my <= mx)) { continue; } // 一般的にマップ全体は横幅の方が長いと仮定している // 壁までの距離が短い方に伸ばすように分割線を引く // 長い方に伸ばすように引くと部屋が小さくなるため map[ax][ay].push(THIEF.util.factory(THIEF.Data.Obj.DarkFloor)); anchors.push({x:ax, y:ay}); if (my < mx) { // 縦に引く for (i = ay - 1; i >= 0; i--) { if (map[ax][i].length === 0) { map[ax][i].push(THIEF.util.factory(THIEF.Data.Obj.DarkFloor)); } else { // 上端に付いた場合には、その一つ右は必ず領域の一番左上になる spaceEdges.push({x:ax + 1, y:i + 1}); break; } } for (i = ay + 1; i < height; i++) { if (map[ax][i].length === 0) { map[ax][i].push(THIEF.util.factory(THIEF.Data.Obj.DarkFloor)); } else { break; } } } else { // 横に引く for (i = ax - 1; i >= 0; i--) { if (map[i][ay].length === 0) { map[i][ay].push(THIEF.util.factory(THIEF.Data.Obj.DarkFloor)); } else { // 左端に付いた場合には、その一つ下は必ず領域の一番左上になる spaceEdges.push({x:i + 1, y:ay + 1}); break; } } for (i = ax + 1; i < width; i++) { if (map[i][ay].length === 0) { map[i][ay].push(THIEF.util.factory(THIEF.Data.Obj.DarkFloor)); } else { break; } } } roonCount++; } // 部屋を作成 var roomsInfo = []; // {x, y, width, height, flag...} for (var index = 0; index < spaceEdges.length; index++) { // 分割済みの領域の一番左上を取得 var point = spaceEdges[index]; x = point.x; y = point.y; // 分割済みの領域のサイズを取得 var roomWidth = 0; var roomHeight = 0; for (i = x; i < width; i++) { if (map[i][y].length === 0) { roomWidth++; } else { break; } } for (i = y; i < height; i++) { if (map[x][i].length === 0) { roomHeight++; } else { break; } } // 部屋の大きさ決定 var startX = Math.floor(Math.random() * (roomWidth / 3)); var startY = Math.floor(Math.random() * (roomHeight / 3)); var endX = Math.floor(Math.random() * (roomWidth / 3) + roomWidth * 2 / 3); var endY = Math.floor(Math.random() * (roomHeight / 3) + roomHeight * 2 / 3); // 狭すぎるならちょっと広げる if (endX - startX < 4) { startX--; endX++; } if (endY - startY < 4) { startY--; endY++; } roomsInfo.push({x:x + startX, y:y + startY, width:endX - startX, height:endY - startY}); // 部屋作成 for (i = startX; i < endX; i++) { for (j = startY; j < endY; j++) { if (i === startX || i === (endX - 1) || j === startY || j === (endY - 1)) { map[x + i][y + j].push(THIEF.util.factory(THIEF.Data.Obj.NormalWall)); } else { map[x + i][y + j].push(THIEF.util.factory(THIEF.Data.Obj.LitFloor)); } } } } // 部屋の位置関係を取得し、縦横各1通を生成する // 部屋は次の部屋と比較され、最後の部屋は最初の部屋と比較される(孤立する部屋を作らないようにするため) roomsInfo.push(roomsInfo[0]); for (i = 0; i < (roomsInfo.length - 1); i++) { // 中心を計算し、通路を伸ばす方向を決める var vx = Math.sign((roomsInfo[i].x + roomsInfo[i].width / 2) - (roomsInfo[i + 1].x + roomsInfo[i + 1].width / 2)); var vy = Math.sign((roomsInfo[i].y + roomsInfo[i].height / 2) - (roomsInfo[i + 1].y + roomsInfo[i + 1].height / 2)); // 部屋の中から適当に一つポイントを決める // 壁にめり込まないように 座標+1, 長さ-3 (-1分は原点) ax = 1 + roomsInfo[i].x + Math.round(Math.random() * (roomsInfo[i].width - 3)); ay = 1 + roomsInfo[i].y + Math.round(Math.random() * (roomsInfo[i].height - 3)); if (THIEF.conf.debugmode) { map[roomsInfo[i].x + Math.round(roomsInfo[i].width / 2) - 1][roomsInfo[i].y + Math.round(roomsInfo[i].height / 2) - 1].getLast().color = 'red'; map[roomsInfo[i].x][roomsInfo[i].y].getLast().color = 'olive'; map[ax][ay].getLast().color = 'green'; } //alert('x:' + roomsInfo[i].x + ', y:' + roomsInfo[i].y + ', width:' + roomsInfo[i].width + ', height:' + roomsInfo[i].height + '\nvx:' + vx + ', vy:' + vy + ', ax:' + ax + ', ay:' + ay); // 通路を作成 // 二度ドアを作るか、通路に当たったら終了 var wallCounter; var fl; var setFloor = function (x, y, vx, vy) { index = 0; wallCounter = 0; var diffX; var diffY; while (true) { diffX = index * vx; diffY = index * vy; fl = map[ax - diffX][ay - diffY].getLast(); index++; // 名にもないのであればとりあえず通路を作る if (typeof fl === 'undefined') { map[ax - diffX][ay - diffY].push(THIEF.util.factory(THIEF.Data.Obj.DarkFloor)); continue; } // 壁ならば通路に置換 if (fl.isBasedOn(THIEF.Data.Obj.NormalWall)) { map[ax - diffX][ay - diffY].pop(); map[ax - diffX][ay - diffY].push(THIEF.util.factory(THIEF.Data.Obj.LitFloor)); wallCounter++; if (wallCounter === 2) { break; } continue; } // それ以外なら終了 if (fl.isBasedOn(THIEF.Data.Obj.DarkFloor) || fl.isBasedOn(THIEF.Data.Obj.NonDiggableWall)) { break; } } }; // 縦軸 if (vx !== 0) { setFloor(ax, ay, vx, 0); } // 横軸 if (vy !== 0) { setFloor(ax, ay, 0, vy); } } // 不要な通路を削除 var clearFloor = function(_x, _y) { var around; var x = _x; var y = _y; var nextX, nextY; var flr = map[x][y].getLast(); if (typeof flr !== 'undefined' && flr.type === THIEF.Data.Obj.$Floor.type) { while (true) { // 周囲に通路が一つだけか調べる around = 0; if (typeof map[x - 1][y - 1].getLast() !== 'undefined' && map[x - 1][y - 1].getLast().type === THIEF.Data.Obj.$Floor.type) { around++; nextX = x - 1; nextY = y - 1; } if (typeof map[x][y - 1].getLast() !== 'undefined' && map[x][y - 1].getLast().type === THIEF.Data.Obj.$Floor.type) { around++; nextX = x; nextY = y - 1; } if (typeof map[x + 1][y - 1].getLast() !== 'undefined' && map[x + 1][y - 1].getLast().type === THIEF.Data.Obj.$Floor.type) { around++; nextX = x + 1; nextY = y - 1; } if (typeof map[x - 1][y].getLast() !== 'undefined' && map[x - 1][y].getLast().type === THIEF.Data.Obj.$Floor.type) { around++; nextX = x - 1; nextY = y; } // me if (typeof map[x + 1][y].getLast() !== 'undefined' && map[x + 1][y].getLast().type === THIEF.Data.Obj.$Floor.type) { around++; nextX = x + 1; nextY = y; } if (typeof map[x - 1][y + 1].getLast() !== 'undefined' && map[x - 1][y + 1].getLast().type === THIEF.Data.Obj.$Floor.type) { around++; nextX = x - 1; nextY = y + 1; } if (typeof map[x][y + 1].getLast() !== 'undefined' && map[x][y + 1].getLast().type === THIEF.Data.Obj.$Floor.type) { around++; nextX = x; nextY = y + 1; } if (typeof map[x + 1][y + 1].getLast() !== 'undefined' && map[x + 1][y + 1].getLast().type === THIEF.Data.Obj.$Floor.type) { around++; nextX = x + 1; nextY = y + 1; } if (around === 1) { map[x][y].pop(); map[x][y].push(THIEF.util.factory(THIEF.Data.Obj.HiddenWall)); map[nextX][nextY].pop(); map[nextX][nextY].push(THIEF.util.factory(THIEF.Data.Obj.HiddenWall)); x = nextX; y = nextY; } else { break; } } } }; for (i = 1; i < width - 2; i++) { clearFloor(i, 1); clearFloor(i, height - 2); } for (i = 1; i < height - 2; i++) { clearFloor(1, i); clearFloor(width - 2, i); } // 残りを初期化 for (i = 0; i < width; i++) { for (j = 0; j < height; j++) { if (map[i][j].length === 0) { map[i][j].push(THIEF.util.factory(THIEF.Data.Obj.HiddenWall)); //map[i][j].push( THIEF.util.factory(THIEF.Data.Obj.LitFloor)); } } } return map; }; THIEF.util.mapParser = function(fmt) { var width = fmt[0].length; var height = fmt.length; var map = []; for (var h = 0; h < height; h++) { for (var w = 0; w < width; w++) { switch (fmt[h].charAt(w)) { case '.': if (typeof map[w] === 'undefined') { map[w] = []; } if (typeof map[w][h] === 'undefined') { map[w][h] = []; } map[w][h].push(THIEF.util.factory(THIEF.Data.Obj.LitFloor)); break; default: THIEF.html.addDebugMessage(map[h].charAt(w) + 'はまだ対応していない。'); break; } } } // 外枠を追加 var left = []; var right = []; for (var i = 0; i < height; i++) { left[i] = []; right[i] = []; left[i].push(THIEF.util.factory(THIEF.Data.Obj.NonDiggableWall)); right[i].push(THIEF.util.factory(THIEF.Data.Obj.NonDiggableWall)); } map.unshift(left); map.push(right); for (var j = 0; j < map.length; j++) { map[j].unshift([]); map[j].push([]); map[j][0].push(THIEF.util.factory(THIEF.Data.Obj.NonDiggableWall)); map[j][height + 1].push(THIEF.util.factory(THIEF.Data.Obj.NonDiggableWall)); } return map; }; THIEF.util.infixToRevP = function (val) { //var val = '2d100 * (2 + 15) + Math.max(3, 4) + (Math.round(11.2) * Math.PI + 6)'; //var val = '2d100*(2+15)+Math.max(3,4)+(Math.round(11.2)*Math.PI+6)'; //var val = '(Math.round(11.2)*Math.PI+6)'; //var val = '(Math.round(11.2) * Math.PI)'; //var val = 'Math.max(3, 4) + Math.round(11.2) + Math.PI'; //var val = '8 + Math.max(3, 4) + Math.PI'; var stack = []; var revP = []; var math; var subIndex; THIEF.html.addDebugMessage('数式を逆ポーランドに変換開始...'); THIEF.html.addDebugMessage('変換前:' + val); for (var i = 0; i < val.length; i++) { var ope = val.charAt(i); switch (ope) { case '+' : case '-' : while (/^[\*\/\^%dM]$/.test(stack.getLast())) { revP.push(stack.pop()); } stack.push(ope); break; case '*' : while (/^[\/\^%dM]$/.test(stack.getLast())) { revP.push(stack.pop()); } stack.push(ope); break; case '/' : while (/^[\^%dM]$/.test(stack.getLast())) { revP.push(stack.pop()); } stack.push(ope); break; case '^' : case '%' : while (/^[dM]$/.test(stack.getLast())) { revP.push(stack.pop()); } stack.push(ope); break; case 'd' : while (/^[M]$/.test(stack.getLast())) { revP.push(stack.pop()); } stack.push(ope); break; case 'M' : // M から演算子 or 空白 までを取り出す // Math. を必ず含むこと // Math.PId6 とかは使用上無理 // Math.round () というように、()の前にスペースが入るのも無理 for (subIndex = i + 5; subIndex < val.length; subIndex++) { if (/[^a-zA-Z]$/.test(val.charAt(subIndex))) { math = val.substring(i + 5, subIndex); break; } else if (subIndex === val.length - 1) { math = val.substring(i + 5, val.length); break; } } if (typeof Math[math] === 'function') { revP.push('dummy'); stack.push('('); // 先に括弧を処理するため、インデックスはそのまま stack.push(Math[math]); } else { revP.push(Math[math]); subIndex--; // 定数の場合は、インデックスを一つ戻す } i = subIndex; break; case '(' : stack.push(ope); break; case ')' : // Math 関数の引数の数が不定なので、判別用のダミーの引数を加えておく if (typeof stack.getLast() === 'function') { //revP.push('dummy'); } while (stack.getLast() !== '(') { revP.push(stack.pop()); } stack.pop(); // Delete ( break; case '0' : case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : case '7' : case '8' : case '9' : case '.' : for (subIndex = i; subIndex < val.length; subIndex++) { if (/[^0-9\.]/.test(val.charAt(subIndex))) { revP.push(parseFloat(val.substring(i, subIndex))); subIndex--; // 行き過ぎたインデックスを元に戻す break; } else if (subIndex === val.length - 1) { revP.push(parseFloat(val.substring(i, val.length))); break; } } i = subIndex; break; default : break; } } while (stack.getLast()) { revP.push(stack.pop()); } THIEF.html.addDebugMessage('変換後:' + revP); THIEF.html.addDebugMessage('...変換完了'); return revP; }; THIEF.util.execRevP = function (val) { // そのうちいろいろな乱数に対応させること // http://nethackwiki.com/wiki/Category:Random_number_functions THIEF.html.addDebugMessage('逆ポーランド記法の数式を計算開始...'); var revP; if (typeof val === 'number') { revP = []; revP[0] = val; } else { revP = val.slice(0); } var index = 0; THIEF.html.addDebugMessage('X = ' + revP); for (; revP.length > 1; index++) { var ope = revP[index]; switch (typeof ope) { case 'number' : break; case 'string' : switch (ope) { case '+' : revP[index - 2] = revP[index - 2] + revP[index - 1]; break; case '-' : revP[index - 2] = revP[index - 2] - revP[index - 1]; break; case '/' : revP[index - 2] = revP[index - 2] / revP[index - 1]; break; case '*' : revP[index - 2] = revP[index - 2] * revP[index - 1]; break; case '%' : revP[index - 2] = revP[index - 2] % revP[index - 1]; break; case '^' : revP[index - 2] = revP[index - 2] ^ revP[index - 1]; break; case 'd' : revP[index - 2] = Math.dice(revP[index - 2], revP[index - 1]); break; case 'dummy' : continue; default : break; } revP.splice(index - 1, 2); index -= 2; break; case 'function' : if (revP[index - 2] === 'dummy') { revP[index - 2] = revP[index](revP[index - 1]); revP.splice(index - 1, 2); index -= 2; } else if (revP[index - 3] === 'dummy') { revP[index - 3] = revP[index](revP[index - 2], revP[index - 1]); revP.splice(index - 2, 3); index -= 3; } break; default : break; } } THIEF.html.addDebugMessage('  = ' + revP[0]); THIEF.html.addDebugMessage('...計算完了。'); return revP[0]; }; THIEF.util.getText = function() { var text; var i, j; THIEF.html.addDebugMessage('テキスト変換処理を開始...'); if (arguments[0]) { text = arguments[0]; // 言語が複数あれば選択 if (arguments[0][THIEF.conf.language]) { text = arguments[0][THIEF.conf.language]; THIEF.html.addDebugMessage('言語設定はコンフィグ(' + THIEF.conf.language + ')とする。'); } else { for (i = 0; i < THIEF.conf.languagePriolity.length; i++) { if (arguments[0][THIEF.conf.languagePriolity[i]]) { text = arguments[0][THIEF.conf.languagePriolity[i]]; THIEF.html.addDebugMessage('言語設定は、優先度 ' + i + ' の ' + THIEF.conf.languagePriolity[i] + ' とする。'); break; } } } // 書式選択 // 選択した言語に、複数の書式があるか確認 if (text instanceof Array) { var num = Math.floor(Math.random() * text.length); THIEF.html.addDebugMessage(text.length + ' 個の選択肢から ' + (num + 1) + ' 番目のテキストを選択'); text = text[num]; } // 選択した書式が関数か確認 if (typeof text === 'function') { text = text(); THIEF.html.addDebugMessage('関数処理を実施。'); } // ${number} は引数パラメータとして扱う。 // ${0} をテキストフォーマットに使用しているので、1から開始 // $<> はエフェクトパラメータとする。(未実装) var org = text; for (i = 1; i < arguments.length; i++) { THIEF.html.addDebugMessage('『' + org + '』の『${' + i + '}』を『' + arguments[i] + '』に置換。'); text = text.replace('${' + i + '}', arguments[i]); } // ${xxx} は動的変数として、内部の構文を解析する // 変数内部は、通常の変数のように ドット で区切る // %xxx@yyy@zzz% と表記されている部分は関数として処理する // @ で区切られた部分は関数の引数とする // 例:${THIEF.global.adventurer.%get@name%} ♥:${THIEF.global.adventurer.%get@hp%} var variables = text.match(/\$\{[A-Za-z0-9_$.%@]+\}/g); if (variables !== null) { for (i = 0; i < variables.length; i++) { // ${ と } を削除 var variable = variables[i].slice(2, variables[i].length - 1); // ピリオドで分割 var tree = variable.split('.'); // 値格納用変数 var val = THIEF; // 確認 // 0 は THIEF なのでパス。(現在は一応確認) if (tree[0] !== 'THIEF') { alert('変数指定の方法が間違っています。'); } for (j = 1; j < tree.length; j++) { // 変数処理 if (tree[j].search(/^%.+%$/) === -1) { //THIEF.html.addDebugMessage(val + ' -> ' + tree[i]); val = val[tree[j]]; //THIEF.html.addDebugMessage(val) // 関数処理 } else { // 前後の % を削除し、@で分割 var arguments = tree[j].slice(1, tree[j].length - 1).split('@'); var args = []; var func = arguments[0]; for (var l = 1; l < arguments.length; l++) { args.push(arguments[l]); } val = val[func].apply(val, args); } } if (typeof val === 'undefined') { THIEF.html.addDebugMessage(variable + ' は定義されていない'); } else { THIEF.html.addDebugMessage(variable + ' = ' + val.toString()); } text = text.replace(variables[i], val); } } } THIEF.html.addDebugMessage('...テキスト変換処理を完了。'); return text; }; THIEF.html = {}; THIEF.html.setXy = function(x, y, text) { if (0 <= x && x < THIEF.conf.mapWidth && 0 <= y && y < THIEF.conf.mapHeight) { document.getElementById(THIEF.conf.id + x + '_' + y).innerHTML = text; } }; THIEF.html.refreshXy = function(x, y) { if (THIEF.global.actLV.inventory[x] && THIEF.global.actLV.inventory[x][y]) { var obj = THIEF.global.actLV.inventory[x][y].getLast(); if (obj.color) { THIEF.html.setXy(x, y, '' + obj.symbol + ''); } else { THIEF.html.setXy(x, y, obj.symbol); } } else { THIEF.html.setXy(x, y, ' '); //░ ╳ } }; THIEF.html.refresh = function() { THIEF.html.addDebugMessage('再描画を開始...'); for (var i = 0; i < THIEF.conf.mapWidth; i++) { for (var j = 0; j < THIEF.conf.mapHeight; j++) { THIEF.html.refreshXy(i, j); } } THIEF.html.addDebugMessage('...再描画を完了。'); }; THIEF.html.setInput = function(text) { document.getElementById(THIEF.conf.id + 'Input').innerHTML = text; }; THIEF.html.addInput = function(text) { document.getElementById(THIEF.conf.id + 'Input').innerHTML += text; }; THIEF.html.addMessage = function(text) { if (text) { var log = {}; log.text = text; log.turn = THIEF.global.turn; THIEF.global.hist.push(log); } if (THIEF.global.hist.length > THIEF.conf.messageHistorySize * 2) { THIEF.html.addDebugMessage('ヒストリーが既定値の二杯まで増えたのでシュリンク'); THIEF.global.hist = THIEF.global.hist.slice(THIEF.conf.messageHistorySize); } if (document.getElementById(THIEF.conf.id + 'Message' + (THIEF.conf.messageLines - 1)).innerHTML !== ' ') { var prev; for (var i = 0; i < THIEF.conf.messageLines - 1; i++) { prev = document.getElementById(THIEF.conf.id + 'Message' + (i + 1)).innerHTML || ' '; document.getElementById(THIEF.conf.id + 'Message' + i).innerHTML = prev; } } document.getElementById(THIEF.conf.id + 'Message' + (THIEF.conf.messageLines - 1)).innerHTML = text || ' '; }; THIEF.html.clearMessage = function() { for (var i = 0; i < THIEF.conf.messageLines; i++) { document.getElementById(THIEF.conf.id + 'Message' + i).innerHTML = ' '; } }; THIEF.html.clearInfomation = function() { document.getElementById(THIEF.conf.id + 'Infomation').innerHTML = ''; }; THIEF.html.addInfomation = function(text) { document.getElementById(THIEF.conf.id + 'Infomation').innerHTML += '

' + text + '

\n'; }; //noinspection JSUnusedGlobalSymbols THIEF.html.addDebugMessage = function() { var indent = ''; var hist = []; return function(text) { if (THIEF.conf.debugmode) { if (typeof text === 'string') { // 文字列が ... から開始する場合にはインデントを減らす if (text.match(/^\.\.\./) && indent.length > 0) { indent = indent.slice(24); } hist.push(indent + text); // 文字列が ... で終わる場合にはインデントを増やす if (text.match(/\.\.\.$/)) { indent += '    '; } } else { var line = ''; for (var i = 0; i < hist.length; i++) { line += hist[i] + '
\n'; } document.getElementById(THIEF.conf.id + 'Debug').innerHTML = line; document.getElementById(THIEF.conf.id + 'Debug').scrollTop = 0; hist = []; //indent = ''; } /* if(THIEF.conf.debugMessageClear){ document.getElementById(THIEF.conf.id + 'Debug').scrollTop = 0; } */ //document.getElementById(THIEF.conf.id + 'Debug').scrollTop = 65535; //document.getElementById(THIEF.conf.id + 'Debug').offsetTop = 65535; } }; }(); /* THIEF.html.clearDebugMessage = function() { if (THIEF.conf.debugmode && THIEF.conf.debugMessageClear ) { document.getElementById(THIEF.conf.id + 'Debug').scrollTop = 0; //document.getElementById(THIEF.conf.id + 'Debug').innerHTML = ''; } }; */ THIEF.html.showCommandHelp = function() { var help = ''; help += '\n'; help += '\n'; var com = []; for (var name in THIEF.Data.Keymap) { if (typeof THIEF.Data.Keymap[name].text !== 'undefined') { com.push(name); } } com.sort(function(a, b) { var x = THIEF.Data.Keymap[a].key; var y = THIEF.Data.Keymap[b].key; if (x === THIEF.KEY.Sharp) { x -= 70000; } if (y === THIEF.KEY.Sharp) { y -= 70000; } if (THIEF.KEY.a <= x && x <= THIEF.KEY.z) { x -= 50000; } if (THIEF.KEY.a <= y && y <= THIEF.KEY.z) { y -= 50000; } if (THIEF.KEY.A <= x && x <= THIEF.KEY.Z) { x -= 20000; } if (THIEF.KEY.A <= y && y <= THIEF.KEY.Z) { y -= 20000; } return x - y; }); help += '\n'; for (var i = 0; i < com.length; i++) { help += '\n'; } help += '
Command Help
' + THIEF.util.getText(THIEF.Data.Keymap.$) + '
' + String.fromCharCode(THIEF.Data.Keymap[com[i]].key) + '' + com[i] + '' + THIEF.util.getText(THIEF.Data.Keymap[com[i]].text) + '
\n'; document.getElementById(THIEF.conf.id + 'Footer').innerHTML = help; }; THIEF.html.init = function() { var line = ''; var i, j; // スタイルシート挿入用メソッド作成 var style = document.createElement('style'); style.setAttribute('type', 'text/css'); document.getElementsByTagName('head')[0].appendChild(style); var sheet = document.styleSheets[0]; if (sheet.insertRule) { sheet.addStyle = function(selector, property, value) { sheet.insertRule(selector + '{' + property + ':' + value + ';}', sheet.cssRules.length); }; } else { sheet.addStyle = function(selector, property, value) { sheet.addRule(selector, property + ':' + value); }; } // スタイルシート適応 var root = THIEF.conf.id; var main = THIEF.conf.id + 'Main'; // Input, Message, Map, Status var inpt = THIEF.conf.id + 'Input'; // 入力欄 var mesg = THIEF.conf.id + 'Message'; // メッセージ欄(入力上部) var maps = THIEF.conf.id + 'Map'; // マップ var stus = THIEF.conf.id + 'Status'; // ステータス var info = THIEF.conf.id + 'Infomation'; // 情報出力(インベントリの内容など) var foot = THIEF.conf.id + 'Footer'; // var dbug = THIEF.conf.id + 'Debug'; // デバッグ情報 sheet.addStyle('#' + root, 'color', THIEF.conf.textColor); sheet.addStyle('#' + root, 'background-color', THIEF.conf.bgColor); sheet.addStyle('#' + root, 'font-family', THIEF.conf.fontFace); sheet.addStyle('#' + root, 'letter-spacing', '0px'); sheet.addStyle('#' + root, 'line-height', '1'); sheet.addStyle('#' + root + ' p', 'margin', '0em'); sheet.addStyle('#' + root + ' p', 'padding', '0em'); sheet.addStyle('#' + root + ' em', 'color', THIEF.conf.emTextColor); sheet.addStyle('#' + main, 'margin', '1em'); sheet.addStyle('#' + main, 'float', THIEF.conf.mainWindowPosition); sheet.addStyle('#' + maps, 'text-align', 'center'); sheet.addStyle('#' + maps, 'vertical-align', 'middle'); sheet.addStyle('#' + maps + ' table', 'border', '1px solid ' + THIEF.conf.textColor); sheet.addStyle('#' + maps + ' table', 'border-collapse', 'collapse'); sheet.addStyle('#' + maps + ' table', 'border-spacing', '0px'); sheet.addStyle('#' + maps + ' td', 'border', THIEF.conf.gridStyle); sheet.addStyle('#' + maps + ' td', 'margin', '0px'); sheet.addStyle('#' + maps + ' td', 'padding', '0px'); sheet.addStyle('#' + maps + ' td', 'color', THIEF.conf.textColor); // テーブル内文字色には必須? sheet.addStyle('#' + info, 'border', THIEF.conf.gridStyle); //sheet.addStyle('#' + info, 'height', THIEF.conf.mapHeight + 'em'); sheet.addStyle('#' + info, 'overflow', THIEF.conf.msgOverflow); sheet.addStyle('#' + info, 'padding', '1em'); sheet.addStyle('#' + dbug, 'border', THIEF.conf.gridStyle); //sheet.addStyle('#' + dbug, 'height', THIEF.conf.debugMessageHeight); //sheet.addStyle('#' + dbug, 'overflow', 'scroll'); // メイン画面の位置決め line += '
\n'; //ステータス //メッセージ if (THIEF.conf.statusPosition === 'top') { line += '
\n'; for (i = 0; i < THIEF.conf.statusLineFormat.length; i++) { line += '
\n'; } line += '
\n'; } if (THIEF.conf.messagePosition === 'top') { line += '
\n'; for (i = 0; i < THIEF.conf.messageLines; i++) { line += '
 
\n'; } line += '
\n'; // キー入力画面 line += '
 
\n'; } // マップ line += '
\n'; line += '\n'; for (i = 0; i < THIEF.conf.mapHeight; i++) { line += ''; for (j = 0; j < THIEF.conf.mapWidth; j++) { line += ''; } line += '\n'; } line += '
#
\n'; line += '
\n'; //ステータス //メッセージ if (THIEF.conf.statusPosition === 'bottom') { line += '
\n'; for (i = 0; i < THIEF.conf.statusLineFormat.length; i++) { line += '
\n'; } line += '
\n'; } if (THIEF.conf.messagePosition === 'bottom') { line += '
\n'; for (i = 0; i < THIEF.conf.messageLines; i++) { line += '
メッセージライン:' + i + '
\n'; } line += '
\n'; // キー入力画面 line += '
 
\n'; } // メイン画面終了 line += '
\n'; // インフォメーション画面 line += '
\n'; // 後処理 // メッセージの回り込みを解除 // span を使うと回りこみの影響で背景色が変になるので注意 //   がないと、CSSの処理がおかしくなる line += '

 \n'; line += '
\n'; // デバッグ画面 if (THIEF.conf.debugmode) { line += '

Debug Info

\n'; line += '
\n'; } document.getElementById(THIEF.conf.id).innerHTML = line; THIEF.html.refresh(); }; /* Date オブジェクトのついて Data クラスはプリセットされたデータセットです。 配下には以下の三つの階層があります。 Data.Obj モンスターやアイテムの管理用 Data.Scenario イベントやシナリオの管理用 Data.Text ローカライズの管理用 Data.Prop 特殊変数の管理用 Data.Obj 配下の $ から始まるオブジェクトは、テンプレートとして扱われます。 ※ スポーンの対象にならない・一覧に表示されない、など Date.Obj 配下に格納されるオブジェクトは、 自動的に THIEF.func.command, THIEF.func.method 内のメソッドを継承し、その対象になります。 その為、すべてのオブジェクトが eat や wear メソッドの対象になります。 ※ 各コマンド名に -able をつけたプロパティ (eatable, wearable) の Boolean値 (true/false) に応じて動作します。 オブジェクト内に inheritance プロパティがあると、 そのオブジェクトも継承します。 inheritance は文字列で指定してください。 ※ inheritance : 'Human' 何もない場合は、Date.Obj.$を継承します。 プロパティは以下の順番で参照されます。 ※ Object -> THIEF.func.command -> THIEF.func.method -> Date.Obj.$ -> Date.Obj.Human -> this 現段階では、多段的な階層はサポートしていません。 今後もするつもりはありません。 ※ Date.Monster.Humanoid.Elf $ (テンプレート) 以外のオブジェクトも、実質的にはテンプレートとして扱われます。 オブジェクトの使用時にマスターオブジェクトが作成され、 以降はそれをクローニングしてスポーン処理などを行います。 そのため、起動後に Date 内のオブジェクトを修正した場合、 その結果が新しいインスタンスには反映されません。 Data.Obj 配下のオブジェクトのプロパティ値が _ から始まる場合、 数式として処理されます。 str : '_3d6' */ THIEF.Data = {}; THIEF.Data.Text = { attack : { // 1 : damage // 2 : subject // 3 : from ja : [ '${3} は ${2} に ${1} のダメージを与えた。', '${2} は ${1} のダメージを受けた。' ], //ja : '${3} は ${2} に ${1} のダメージを与えた。', en : '${2} Hit decreased by ${1}.' }, defence : { // 1 : subject // 2 : from ja : '${2} からの攻撃を防いだ。', en : '${1} avoided attacks from ${2}.' }, killBy : { // 1 : who // 2 : by ja : '${2} は ${1} を殺した。', en : '${1} are killed by ${2}.' }, pickup : { // 1 : item // 2 : subject // 3 : from ja : '${2} は ${1} を拾った。', en : '${2} pickup ${1}.' }, canNotPickup : { // 1 : item // 2 : subject // ja : '${1} を拾えない。', en : '${2} cannot pickup ${1}.' }, pickupWhich : { ja : 'どれを拾う?', en : 'Whitch item pickup?' }, noItemHere : { ja : 'ホコリくらいしか落ちていない...', en : 'You find any dusts...' }, drop : { // 1 : item // 2 : subject // 3 : to ja : '${1} を置いた。', en : '${2} drop ${1}' }, canNotDrop : { // 1 :item // 2 : subject // 3 : to ja : '${1} を置けない。', en : '${2} cannot drop ${1}' }, inventoryIsEmpty : { ja : '何も持っていません。', en : 'Your inventory is empty.' }, commandNotFound : { // 1 : command name ja : '"${1}" コマンド?', en : '"${1}" command?' }, decay : { // 1 : subject ja : '${1} は腐りきった。', en : '${1} is gone bad.' }, equipItem : { // 1 : item // 2 : subject // 3 : slot ja : '${2} は ${1} を ${3} に装備した。', en : '${2} equip ${1} on ${3}.' }, equipNone : { // 1 : subject // 2 : slot ja : '${1} は ${2} から装備を外した。', en : '${1} take off ${2}-item.' }, equipAlready : { // 1 : item // 2 : subject ja : '${1} は既に装備している。', en : 'Already, ${2} equiped ${1}.' }, goUpStairs : { // 1 : Subject ja : '${1}は階段を登った', en : '${1} go up the stairs.' }, doesNotGoUpStairs : { ja : '上り階段が見当たらない', en : 'You can not found the up stairs.' }, goDownStairs : { // 1 : Subject ja : '${1}は階段を降った', en : '${1} go down the stairs.' }, doesNotGoDownStairs : { ja : '下り階段が見当たらない', en : 'You can not found the sown stairs.' }, startGame : { ja : '冒険を開始します。', en : 'Start Game' }, cmdInventoryDesc : { ja : '持ち物を表示', en : 'show your inventory.' }, cmdEquipDesc : { ja : 'アイテムを装備', en : 'equip your item.' }, cmdPrintHistoryDesc : { ja : 'メッセージの履歴を表示', en : 'show messages history' }, cmdRefreshScreenDesc : { ja : '画面を再描画する', en : 'refresh screen' }, cmdPickupDesc : { ja : '足元のアイテムを拾う', en : 'pickup items from your step.' }, cmdDropDesc : { ja : 'アイテムを置く', en : 'drop your items.' }, cmdExecuteCommandDesc : { ja : '指定したコマンドを実行します', en : 'execute command' }, cmdGoUpStairs : { ja : '階段を登る', en : 'go up the stairs' }, cmdGoDownStairs : { ja : '階段を降りる', en : 'go down the stairs' }, objDefaultName : { ja : '無名', en : 'Nameless' }, objDefaultHumanName : { ja : '人間', en : 'Human' }, objDefaultSymbolName : { ja : '不明', en : 'Unknown' }, slotNameOfHand : { ja : '手', en : 'hand' }, slotNameOfChest : { ja : '胴', en : 'chest' }, symbolNameOfArmor : { ja : '防具', en : 'Armor' }, symbolNameOfFood : { ja : '食料', en : 'Food' }, symbolNameOfWeapn : { ja : '武器', en : 'Weapn' }, symbolNameOfMail : { ja : '鎧', en : 'Mail' }, objDebugDaemonName : { ja : 'デバッグ用モンスター', en : 'DebugMonster' }, objLitFloorName : { ja : '明るい床', en : 'Lit Floor' }, objDarkFloorName : { ja : '暗い床', en : 'Dark Floor' }, objRationName : { ja : '食料', en : 'Ration' }, objDebuggingDaggerName : { ja : 'テスト用ダガー', en : 'DebuggingDagger' }, objDebuggingHighDaggerName : { ja : 'テスト用上位ダガー', en : 'Debugging High Dagger' }, objDebuggingArmorName : { ja : 'テスト用鎧', en : 'Debugging Armor' }, objDebuggingHighArmorName : { ja : 'テスト用固鎧', en : 'Debugging High Armor' }, objDoorName : { ja : '扉', en : 'Door' }, objHiddenWallName : { ja : '壁の中', en : 'Hidden Wall' }, objUnbreakableWallName : { ja : '壊せない壁', en : 'Unbreakable Wall' }, objNormalWallName : { ja : '壁', en : 'Wall' }, objUpStairsName : { ja : '上り階段', en : 'Up Stairs' }, objDownStairsName : { ja : '下り階段', en : 'Down Stairs' }, sceTheDungeonName : { ja : '迷宮', en : 'The Dungeon' }, sceSideRoadName : { ja : '脇道', en : 'Side Road' } }; THIEF.Data.Prop = { // 特殊変数登録用 displayName : function() { var text = ''; if (this.equiped) { text += '[E] ' } text += this.get(THIEF.PROP.name) + '(' + this.get(THIEF.PROP.symbol) + ')'; return text; }, db : function () { var val = 0; // 体力に応じたダメージボーナス if (typeof this.con === 'undefined') { //val += 0; } else if (this.con >= 18) { val += (this.con / 6); } else if (this.con >= 17) { val += 2; } else if (this.con >= 15) { val += 1; } else if (this.con >= 7) { //val += 0; } else if (this.con >= 4) { val += -1; } else if (this.con >= 3) { val += -2; } else if (this.con < 3) { val += -3; } // 腕力、サイズに応じたダメージボーナス if (typeof this.str === 'undefined') { //val += 0; } else if (typeof this.siz === 'undefined') { // STR のみ定義されている場合 if (this.str >= 25) { val += (this.str / 2.27); } else if (this.str >= 18.99) { val += 4; } else if (this.str > 18) { val += 3; } else if (this.str >= 18) { val += 2; } else if (this.str >= 16) { val += 1; } else if (this.str >= 6) { //val += 0; } else if (this.str < 6) { val += -1; } } else { // STR と SIZ の両方が定義されている場合 var num = this.str + this.siz; if (num >= 57) { val += Math.dice(2 + Math.floor((num - 41) / 16), 6); } else if (num >= 41) { val += Math.dice(2, 6); } else if (num >= 33) { val += Math.dice(1, 6); } else if (num >= 25) { val += Math.dice(1, 4); } else if (num >= 17) { //val += 0; } else if (num >= 13) { val += -Math.dice(1, 4); } else if (num >= 0) { val += -Math.dice(1, 6); } } // アイテムに応じたダメージボーナス // 武器によるダメージは別で処理(二刀流対応のため) return val; }, armorPoint : function () { var val = 0; if (typeof this.slot !== 'undefined') { for (var slotName in this.slot) { if (typeof this.slot[slotName].ac === 'number') { val += this.slot[slotName].ac; } } } return val; } }; THIEF.Data.Obj = {}; // Monster & Item THIEF.Data.Obj.$ = { // Default name : THIEF.Data.Text.objDefaultName, symbol : '╳', // 未登録用印(X に近い) symbolName : THIEF.Data.Text.objDefaultSymbolName, type : 'unknown' }; THIEF.Data.Obj.$Monster = { active : true, ct : 0, maxct : 100, type : 'monster' }; THIEF.Data.Obj.$Item = { stackable : true, mergeable : true, portable : true, isItem : true, slot : 'Hand', // すべてのアイテムは手に持てる。 slotName : THIEF.Data.Text.slotNameOfHand, type : 'item' }; THIEF.Data.Obj.$Armor = { inheritance : '$Item', symbol : '[', symbolName : THIEF.Data.Text.symbolNameOfArmor, color : 'brown' }; THIEF.Data.Obj.$Level = { active : false, type : 'level' }; THIEF.Data.Obj.$Humanoid = { inheritance : '$Monster', symbol : '@' }; THIEF.Data.Obj.Human = { inheritance : '$Humanoid', color : 'red', name : THIEF.Data.Text.objDefaultHumanName, str : '_3d6', con : '_3d6', pow : '_3d6', dex : '_3d6', app : '_3d6', siz : '_2d6 + 6', 'int' : '_2d6 + 6', edu : '_3d6 + 3' }; THIEF.Data.Obj.$Daemon = { inheritance : '$Humanoid', symbol : '&' }; THIEF.Data.Obj.DebugDaemon = { inheritance : '$Daemon', color : '#00FF88', name : THIEF.Data.Text.objDebugDaemonName, str : '_3d6', con : '_3d6', pow : '_3d6', dex : '_3d6', app : '_3d6', siz : '_2d6 + 6', 'int' : '_2d6 + 6', edu : '_3d6 + 3' }; THIEF.Data.Obj.$Floor = { stackable : true, type : 'floor' // diggable : true }; THIEF.Data.Obj.LitFloor = { inheritance : '$Floor', symbol : '.', name : THIEF.Data.Text.objLitFloorName }; THIEF.Data.Obj.DarkFloor = { inheritance : '$Floor', symbol : '#', name : THIEF.Data.Text.objDarkFloorName }; THIEF.Data.Obj.UpStairs = { inheritance : '$Floor', symbol : '<', name : THIEF.Data.Text.objUpStairsName }; THIEF.Data.Obj.DownStairs = { inheritance : '$Floor', symbol : '>', name : THIEF.Data.Text.objDownStairsName }; THIEF.Data.Obj.$Wall = { symbol : '▓', // 濃いドット模様 type : 'wall' //diggable : true }; THIEF.Data.Obj.NonDiggableWall = { inheritance : '$Wall', symbol : '░', // 薄いドット模様 name : THIEF.Data.Text.objUnbreakableWallName }; THIEF.Data.Obj.NormalWall = { inheritance : '$Wall', symbol : '+', name : THIEF.Data.Text.objNormalWallName }; THIEF.Data.Obj.HiddenWall = { inheritance : '$Wall', symbol : ' ', name : THIEF.Data.Text.objHiddenWallName }; THIEF.Data.Obj.Door = { inheritance : '$Wall', symbol : '+', color : 'burlywood', name : THIEF.Data.Text.objDoorName }; THIEF.Data.Obj.$Food = { inheritance : '$Item', symbol : '%', symbolName : THIEF.Data.Text.symbolNameOfFood }; THIEF.Data.Obj.$Corpse = { inheritance : '$Food', decayable : true, next : THIEF.func.method.decay, dp : 20 }; THIEF.Data.Obj.Ration = { inheritance : '$Food', name : THIEF.Data.Text.objRationName, color : 'red' }; THIEF.Data.Obj.$Weapon = { inheritance : '$Item', symbol : ']', symbolName : THIEF.Data.Text.symbolNameOfWeapn }; THIEF.Data.Obj.DebugDagger = { inheritance : '$Weapon', name : THIEF.Data.Text.objDebuggingDaggerName, damage : '2d4', color : 'silver' }; THIEF.Data.Obj.DebugHighDagger = { inheritance : '$Weapon', name : THIEF.Data.Text.objDebuggingHighDaggerName, damage : '3d4', color : 'silver' }; THIEF.Data.Obj.$Mail = { inheritance : '$Armor', slot : 'Chest', slotName : THIEF.Data.Text.slotNameOfChest, symbolName : THIEF.Data.Text.symbolNameOfMail }; THIEF.Data.Obj.DebugArmor = { inheritance : '$Mail', ac : 2, name : THIEF.Data.Text.objDebuggingArmorName }; THIEF.Data.Obj.DebugHighArmor = { inheritance : '$Mail', ac : 4, name : THIEF.Data.Text.objDebuggingHighArmorName }; /* * Symbol Memo * * ■小文字アルファベットは基本的に 弱い or 普通の強さ……のものが多い * amphibia カエルなどの(小型)両生類 * blob * chiken, cockatrice, (basilisk) 小型の鳥? * dog * eyes, element 球形のもの * felid (cat) * gremlin, gargoyle, goblin, (kobold) 主に敵対的 * humanoid, hobbit, elf, (gnome) 主に友好的 * insect 昆虫。芋虫系は w * j * kop 無能な警官・警備員 * lacertilia, lizard トカゲっぽい小さな生き物。大型のは Reptilia へ。 * mimic * nymph * orc * piercer つらら状生物。 天井から落ちてくる。(それまでは見えない) * quadrupeds 小さい(草食的な)四足獣。大きくて強いのは Q * rat * slime * t * undead (zombi, mummy) * vortex 意思のないエネルギー体 * worm 昆虫は i * x * y * z * * ■大文字アルファベットは普通に強い * Angel * Bard, Bat 大型の飛行生物。猛禽類・ハーピーなど * Centaur -> T * Doragon * Element でかくて強いエレメント。精霊 * Fungus * Golem * HUMANOID (Giant) * Invisible monster * J * K * Lich * Minotaur -> T * Naga -> T * Ogre 鬼。小さい鬼は goblin * Predator 凄まじく猛獣。 * Quadrupeds 大きい(肉食的な)四足獣。小さくて弱いのは q * Reptilia 大型爬虫類。小型は l? * Sea Serpent 海上生物。クラーケンとかもここ。 * Therianthropy 半人半獣 * Unicorn, Uma(笑) * Vampire * Wight, Wraith * X * Y * Z * @ : human, Aventurier and sametype * & : Daemon * # : dark floor, unbreakable floor * . : lit floor * ~ : tail? -> ̰ ˷ * ^ : trap * ' : * ` : * { : fountain, lake (safe water place) * } : pool, lava (dangerous water place ) * : : Iron Bars (transparent wall) * ; : * - : wall, opened door * | : wall, opened door, tomb * \ : tree -> \ * < : staircase up * > : staircase down * _ : altar, throne, tomb? * 0(ZERO) : STONE * ? : scrool * ! : potion * " : amulet * + : wall, closed door, edge, book * $ : money * % : food * ( : tool * ) : unknown (oopart) * = : ring * [ : armor * ] : weapon * * : stone, jewel, gem * / : wand * */ //noinspection JSUnusedGlobalSymbols THIEF.Data.Keymap = { $ : { // help ja : '' + 'y k u' + '    ' + 'yuhjklbn: 指定方向に一歩歩く' + '
\n' + ' ╲|╱ ' + '    ' + '←↑↓→: 同' + '
\n' + 'h-.-k' + '    ' + 'Home,End,PageUp,PageDown: 斜め方向に一歩歩く' + '
\n' + ' ╱|╲ ' + '    ' + '.: 何もしない' + '
\n' + 'b j n' + '    ' + '' + '
\n' }, moveUp1 : { cmd : THIEF.func.command.goUp, key : THIEF.KEY.k }, moveUp2 : { cmd : THIEF.func.command.goUp, key : THIEF.KEY.Up }, moveDown1 : { cmd : THIEF.func.command.goDown, key : THIEF.KEY.j }, moveDown2 : { cmd : THIEF.func.command.goDown, key : THIEF.KEY.Down }, moveLeft1 : { cmd : THIEF.func.command.goLeft, key : THIEF.KEY.h }, moveLeft2 : { cmd : THIEF.func.command.goLeft, key : THIEF.KEY.Left }, moveRight1 : { cmd : THIEF.func.command.goRight, key : THIEF.KEY.l }, moveRight2 : { cmd : THIEF.func.command.goRight, key : THIEF.KEY.Right }, moveUpperLeft1 : { cmd : THIEF.func.command.goUpperLeft, key : THIEF.KEY.y }, moveUpperLeft2 : { cmd : THIEF.func.command.goUpperLeft, key : THIEF.KEY.Home }, moveUpperRight1 : { cmd : THIEF.func.command.goUpperRight, key : THIEF.KEY.u }, moveUpperRight2 : { cmd : THIEF.func.command.goUpperRight, key : THIEF.KEY.PageUp }, moveLowerLeft1 : { cmd : THIEF.func.command.goLowerLeft, key : THIEF.KEY.b }, moveLowerLeft2 : { cmd : THIEF.func.command.goLowerLeft, key : THIEF.KEY.End }, moveLowerRight1 : { cmd : THIEF.func.command.goLowerRight, key : THIEF.KEY.n }, moveLowerRight2 : { cmd : THIEF.func.command.goLowerRight, key : THIEF.KEY.PageDown }, rest : { cmd : THIEF.func.command.rest, key : THIEF.KEY.Period }, inventory : { cmd : THIEF.func.command.showInventory, key : THIEF.KEY.i, text : THIEF.Data.Text.cmdInventoryDesc }, Equip : { cmd : THIEF.func.command.equip, key : THIEF.KEY.E, text : THIEF.Data.Text.cmdEquipDesc }, printHistory : { cmd : THIEF.func.command.printHistory, key : THIEF.KEY.p, text : THIEF.Data.Text.cmdPrintHistoryDesc }, RrefreshScreen : { cmd : THIEF.func.command.refreshHTML, key : THIEF.KEY.R, text : THIEF.Data.Text.cmdRefreshScreenDesc }, pickup : { cmd : THIEF.func.command.pickup, key : THIEF.KEY.Comma, text : THIEF.Data.Text.cmdPickupDesc }, drop : { cmd : THIEF.func.command.drop, key : THIEF.KEY.d, text : THIEF.Data.Text.cmdDropDesc }, executeCommand : { cmd : THIEF.func.command.extendedCommand, key : THIEF.KEY.Sharp, text : THIEF.Data.Text.cmdExecuteCommandDesc }, goUpStairs : { cmd : THIEF.func.command.goUpTheStairs, key : THIEF.KEY.LT, text : THIEF.Data.Text.cmdGoUpStairs }, goDownStairs : { cmd : THIEF.func.command.goDownTheStairs, key : THIEF.KEY.GT, text : THIEF.Data.Text.cmdGoDownStairs } }; /* // 独自追加パラメータ Height: その(配下の)マップの縦幅 Width: その(配下の)マップの横幅 StartLevel: 開始階層 levelNum: 階層数 // とりあえず NetHack Wiki から引っ張ってきた変数一覧 http://nethackwiki.com/wiki/Dungeon.def_format ALIGNMENT BRANCH CHAINBRANCH CHAINLEVEL DESCRIPTION DUNGEON LEVALIGN LEVEL LEVELDESC PROTOFILE RNDLEVEL http://nethackwiki.com/wiki/Des-file_format ALTAR BRANCH CHANCE CONTAINER DOOR DRAWBRIDGE ENDMAP ENGRAVING FLAGS FOUNTAIN GEOMETRY GOLD INIT_MAP LADDER LEVEL MAP MAZE MAZEWALK MESSAGE MONSTER NAME NOMAP NON_DIGGABLE NON_PASSWALL OBJECT PORTAL RANDOM_CORRIDORS RANDOM_MONSTERS RANDOM_OBJECTS RANDOM_PLACES REGION ROOM SINK STAIR SUBROOM TELEPORT_REGION TRAP WALLIFY */ THIEF.Data.Scenario = { $ : { root : 'TheDungeon', width : 60, height : 20, depth : 256 }, TheDungeon : { name : THIEF.Data.Text.sceTheDungeonName, levelNum : 10, startLevel : 2, hierarchy : [ {level : ['First', 'SmallRoom', 'Safety'], base : 2}, {branch : 'SideRoad', base : 3, branchDir : 'down'} //{level : 'BigRoom', base : 4, range :3, chance : 50} ] }, SideRoad : { name : THIEF.Data.Text.sceSideRoadName, levelNum : 3, width : 60, height : 10 }, $Template : { First : { messages : { firstIn : THIEF.Data.Text.startGame } }, SmallRoom : { map : [ '........', '........', '........', '........', '........' ] }, BigRoom : { width : 120, height : 40 }, Safety : { safety : true } } };