init before WX13

This commit is contained in:
WEBXOSS 2016-10-23 13:28:51 +08:00
commit 1129c71e16
110 changed files with 135233 additions and 0 deletions

5
.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
node_modules
trash
.DS_Store
*.sublime-*
images

66
Button.js Normal file
View file

@ -0,0 +1,66 @@
'use strict';
function Button (txt,onclick) {
createjs.Container.prototype.initialize.call(this);
this.changed = true;
var paddingTop = 5;
var paddingLeft = 8;
var fontSize = Button.HEIGHT;
var font = fontSize + 'px "Segoe UI", Arial, "Microsoft Yahei", Simsun, sans-serif';
this.text = new createjs.Text(txt,font);
this.text.textBaseline = "middle";
this.height = fontSize + paddingTop*2;
this.width = this.text.getMeasuredWidth() + paddingLeft*2;
this.regX = this.width/2;
this.regY = this.height/2;
this.text.x = paddingLeft;
this.text.y = this.height/2;
this.background = new createjs.Shape();
this.toDefaultStyle();
this.addChild(this.background,this.text);
this.on('click',onclick);
this.on('mouseover',this.toHoverStyle);
this.on('mouseout',this.toDefaultStyle);
}
Button.HEIGHT = 11;
Button.prototype = new createjs.Container();
Button.prototype.constructor = Button;
Button.prototype.toDefaultStyle = function () {
this.changed = true;
this.text.color = '#444';
this.background.graphics
.clear()
.beginStroke('#c0c0c0')
.beginLinearGradientFill(['#ededed','#ededed','#dedede'],[0,0.38,1],0,0,0,this.height)
.drawRoundRect(0,0,this.width,this.height,2);
};
Button.prototype.toHoverStyle = function () {
this.changed = true;
this.text.color = '#000';
this.background.graphics
.clear()
.beginStroke('#afafaf')
.beginLinearGradientFill(['#f0f0f0','#f0f0f0','#e0e0e0'],[0,0.38,1],0,0,0,this.height)
.drawRoundRect(0,0,this.width,this.height,2);
};
Button.prototype.update = function () {
var changed = this.changed;
this.changed = false;
return changed;
};

50
ButtonList.js Normal file
View file

@ -0,0 +1,50 @@
'use strict';
function ButtonList () {
createjs.Container.prototype.initialize.call(this);
this.changed = false;
this.buttons = [];
}
ButtonList.prototype = new createjs.Container();
ButtonList.prototype.constructor = ButtonList;
ButtonList.prototype.addButton = function (btn) {
this.changed = true;
this.buttons.push(btn);
this.addChild(btn);
};
ButtonList.prototype.setButtonPositions = function () {
var base = -this.buttons.length/2 + 0.5;
this.buttons.forEach(function (btn,i) {
btn.x = 0;
btn.y = (base + i)*btn.height;
});
};
ButtonList.prototype.removeButton = function (btn) {
removeFromArr(btn,this.buttons);
if (this.removeChild(btn)) this.changed = true;
};
ButtonList.prototype.removeAllButtons = function () {
if (!this.buttons.length) return;
this.changed = true;
this.buttons.length = 0;
this.removeAllChildren();
};
ButtonList.prototype.update = function () {
if (this.changed) {
this.setButtonPositions();
}
var changed = this.changed;
this.buttons.forEach(function (btn) {
if (btn.update()) changed = true;
});
this.changed = false;
return changed;
};

279
Card.js Normal file
View file

@ -0,0 +1,279 @@
'use strict';
function Card (game,isWhiteBack) {
createjs.Container.prototype.initialize.call(this);
// 基本属性
this.game = game;
this.isWhiteBack = isWhiteBack;
this.width = Card.WIDTH;
this.height = Card.HEIGHT;
this.pid = 0;
this.zone = null;
this.zIndex = 0;
this.changed = true;
// 渲染层
// 因为EasleJS没有位置偏移(translate),
// 所以增加渲染层来提供偏移. 即:
// Card.x 指定卡片的水平位置, renderLayer.x 指定其水平偏移.
// 所有显示对象都放到渲染层,这样,它们都会跟着偏移.
this.renderLayer = new createjs.Container();
this.addChild(this.renderLayer);
// 变形原点在中心.
this.renderLayer.regX = this.width/2;
this.renderLayer.regY = this.height/2;
// 位图层
this.bitmap = new CardBitmap('',game.imageManager.noimage);
this.renderLayer.addChild(this.bitmap);
// 状态层 (冻结,枪兵等状态)
this.stateLayer = new createjs.Container();
this.renderLayer.addChild(this.stateLayer);
// 效果层
this.effectShape = new createjs.Shape();
this.renderLayer.addChild(this.effectShape);
// 按钮层
this.buttonLayer = new ButtonList();
this.buttonLayer.x = this.width/2;
this.buttonLayer.y = this.height/2
this.renderLayer.addChild(this.buttonLayer);
// 样式
this.style = new Style({
x: 0,
y: 0,
zIndex: 0,
top: false,
covered: false,
offX: 0, // 位置偏移
offY: 0,
rotation: 0,
scaleX: -1, // 用于翻面,负数表示反面,正数表示正面
shadowColor: "#f7ff00",
shadowBlur: 0,
flash: 0, // 用于闪烁: alpha = 1 - (flash % 1)
// flash 从0渐变到n,则闪烁n次.
shine: 0 // 用于闪耀,范围为0至1,
// 表示闪耀动画进行的百分比.
});
this.style.checkSkip = function () {
return this.game.skip;
}.bind(this);
this.on('mouseover',this.handleMouseover);
this.on('mouseout',this.handleMouseout);
this.game.addCard(this);
}
Card.WIDTH = 63;
Card.HEIGHT = 88;
Card.prototype = new createjs.Container();
Card.prototype.constructor = Card;
Card.prototype.up = function () {
var rotation = this.zone.opposite? 180 : 0;
this.style.transit('rotation',rotation,0.2);
};
Card.prototype.down = function () {
var rotation = this.zone.opposite? 180 : 0;
rotation += 90;
this.style.transit('rotation',rotation,0.2);
};
Card.prototype.faceup = function () {
this.style.transit('scaleX',1,0.2);
};
Card.prototype.facedown = function () {
this.style.transit('scaleX',-1,0.2);
};
Card.prototype.move = function (pid,zone,up,faceup,bottom) {
this.pid = pid;
this.floatdown();
if (this.zone) this.zone.removeCard(this);
this.zone = zone;
this.zone.addCard(this,bottom);
up? this.up() : this.down();
faceup? this.faceup() : this.facedown();
};
Card.prototype.moveTo = function (x,y,coveredMoving,coveredSettaled) {
this.style.transit('x',x,0.2);
this.style.transit('y',y,0.2);
if (x !== this.x || y !== this.y) {
this.style.set('top',true);
this.style.transit('top',false,0.2);
}
this.style.set('covered',coveredMoving);
this.style.transit('covered',coveredSettaled,0.2);
};
Card.prototype.floatup = function () {
this.style.set('top',true);
// this.style.transit('offY',-10,0.1,Style.linear);
};
Card.prototype.floatdown = function () {
this.style.set('top',false);
// this.style.transit('offY',0,0.1,Style.linear);
};
Card.prototype.outlineOn = function () {
this.style.transit('shadowBlur',5,0.2,Style.linear);
};
Card.prototype.outlineOff = function () {
this.style.transit('shadowBlur',0,0.2,Style.linear);
};
Card.prototype.flash = function () {
this.style.set('flash',0);
this.style.transit('flash',3,0.3,Style.linear);
};
Card.prototype.shine = function () {
this.style.set('shine',0);
this.style.transit('shine',1,0.3,Style.linear);
};
Card.prototype.addStates = function (states) {
this.changed = true;
this.stateLayer.visible = true;
states.forEach(function (state) {
var stateImage = this.game.imageManager.getStateImage(state);
var bitmap = new createjs.Bitmap(stateImage);
this.stateLayer.addChild(bitmap);
},this);
};
Card.prototype.removeStates = function () {
this.changed = true;
this.stateLayer.visible = false;
this.stateLayer.removeAllChildren();
};
Card.prototype.addButton = function (txt,onclick) {
// var useDialog = this.shouldUseDialog();
// if (useDialog) {
// this.zone.addButton(txt,onclick.bind(this,this),this);
// return;
// }
var btn = new Button(txt,onclick.bind(this,this));
this.buttonLayer.addButton(btn);
// this.outlineOn();
};
// 在给卡片添加按钮时,有时候卡片被遮挡,
// 须要用卡片选择框.
// 问:具体什么情况下要用选择框呢?
// 答:满足以下条件之一:
// 1.卡片在TileZone(除了己方手牌),牌组,LRIG牌组,废弃区,LRIG废弃区;
// 2.卡片在检查区,且检查区不止1张卡.
// 3.卡片在SIGNI区或LRIG区,并且,不在最上面.
Card.prototype.shouldUseDialog = function () {
var zoneName = this.zone.name;
if (this.zone.constructor === TileZone) {
if (this.zone !== this.game.player.handZone) {
return true;
}
}
if (inArr(zoneName,['MainDeck','LrigDeck','TrashZone','LrigTrashZone'])) {
return true;
}
if (zoneName === 'CheckZone') {
if (this.zone.cards.length > 1) {
return true;
}
}
if (inArr(zoneName,['SigniZone','LrigZone'])) {
if (this.zone.getCardIndex(this) !== 0) {
return true;
}
}
return false;
};
Card.prototype.removeButtons = function () {
this.buttonLayer.removeAllButtons();
// this.outlineOff();
};
Card.prototype.update = function () {
var changed = this.style.isChanged() || this.changed;
concat(this.buttonLayer,this.bitmap).forEach(function (obj) {
if (obj.update()) changed = true;
},this);
if (!changed) return false;
this.changed = false;
// shortcuts
var layer = this.renderLayer;
var effectShape = this.effectShape;
var bitmap = this.bitmap;
// 计算样式
var style = this.style.getComputedStyle();
// 位置
this.x = style.x;
this.y = style.y;
// 旋转
this.rotation = style.rotation;
this.buttonLayer.rotation = -style.rotation;
// 闪烁 (flash)
this.alpha = 1 - (style.flash % 1);
// 偏移
layer.x = style.offX;
layer.y = style.offY;
// 翻面
var cardName = this.pid? Localize.cardName(CardInfo[this.pid]) : '???';
if (style.scaleX > 0) {
// 正面
this.bitmap.setAltImage(cardName,this.game.imageManager.getImageByPid(this.pid));
} else {
// 背面
this.bitmap.setAltImage(cardName,this.game.imageManager.getBackImage(this.isWhiteBack));
}
layer.scaleX = Math.abs(style.scaleX);
// outline (其实是shadow)
bitmap.shadow = new createjs.Shadow(style.shadowColor,0,0,style.shadowBlur);
// 闪耀 (shine)
// 闪耀就是在效果层上画一道移动的白光
if (style.shine > 0) {
var w = 40; // 白光的"宽度" (等于白光实际宽度除以根号2,因为白光是45度倾斜的)
var x0 = style.shine*(this.width+w) - w;
var y0 = style.shine*(this.height+w) - w;
var x1 = x0 + w;
var y1 = y0 + w;
this.effectShape.graphics
.clear()
.beginLinearGradientFill(['rgba(255,255,255,0)','white','rgba(255,255,255,0)'],[0,0.5,1],x0,y0,x1,y1)
.drawRect(0,0,this.width,this.height);
}
// zIndex
this.zIndex = style.top? 512 : style.zIndex;
this.visible = style.top || !style.covered;
return true;
};
Card.prototype.handleMouseover = function (event) {
this.game.cardDetail.show(this.pid);
if (this.zone.opposite || this.zone.name !== 'HandZone') return;
this.floatup();
};
Card.prototype.handleMouseout = function (event) {
if (this.zone.opposite || this.zone.name !== 'HandZone') return;
this.floatdown();
};

58
CardBitmap.js Normal file
View file

@ -0,0 +1,58 @@
'use strict';
function CardBitmap (alt,img) {
createjs.Container.prototype.initialize.call(this);
this.setAltImage(alt,img);
this.setWidthHeight(Card.WIDTH,Card.HEIGHT);
this.complete = img.complete;
this.text = new createjs.Text(alt);
this.bitmap = new createjs.Bitmap(img);
this.addChild(this.text,this.bitmap);
this.changed = true;
}
CardBitmap.prototype = new createjs.Container();
CardBitmap.prototype.constructor = CardBitmap;
CardBitmap.prototype.setAltImage = function (alt,img) {
if (alt !== this.alt || img !== this.img) {
this.changed = true;
this.alt = alt;
this.img = img;
}
};
CardBitmap.prototype.setWidthHeight = function (width,height) {
if (width !== this.width || height !== this.height) {
this.changed = true;
this.width = width;
this.height = height;
}
};
CardBitmap.prototype.update = function () {
if (this.complete != this.img.complete) {
this.changed = true;
}
if (!this.changed) return false;
this.changed = false;
this.complete = this.img.complete;
this.text.text = this.alt;
if (this.img.complete && this.img.naturalWidth !== 0) {
this.bitmap.image = this.img;
this.bitmap.scaleX = this.width / this.img.width;
this.bitmap.scaleY = this.height / this.img.height;
this.bitmap.visible = true;
this.text.visible = false;
} else {
this.bitmap.visible = false;
this.text.visible = true;
}
return true;
};

95
CardButton.js Normal file
View file

@ -0,0 +1,95 @@
'use strict';
function CardButton (card,onclick) {
createjs.Container.prototype.initialize.call(this);
this.changed = true;
this.selected = false;
this.disabled = false;
this.hovered = false;
this.order = 0;
var img = card.game.imageManager.getImageByCid(card.cid);
this.bitmap = new CardBitmap(card.cid,img);
this.width = Card.WIDTH*2;
this.height = Card.HEIGHT*2;
this.bitmap.setWidthHeight(this.width,this.height);
this.addChild(this.bitmap);
this.maskLayer = new createjs.Shape();
this.orderLayer = new createjs.Text();
this.addChild(this.maskLayer,this.orderLayer);
this.orderLayer.font = "70px 'Segoe UI', Arial, 'Microsoft Yahei', Simsun, sans-serif'";
this.orderLayer.textBaseline = "middle";
this.orderLayer.textAlign = 'center';
this.orderLayer.x = this.width/2;
this.orderLayer.y = this.height/2;
this.on('click',onclick);
this.on('mouseover',this.onmouseover);
this.on('mouseout',this.onmouseout);
}
CardButton.prototype = new createjs.Container();
CardButton.prototype.constructor = CardButton;
CardButton.prototype.onmouseover = function (event) {
if (this.hovered) return;
this.hovered = true;
this.changed = true;
};
CardButton.prototype.onmouseout = function (event) {
if (!this.hovered) return;
this.hovered = false;
this.changed = true;
};
CardButton.prototype.setOrder = function (order) {
if (this.order === order) return;
this.order = order;
this.changed = true;
};
CardButton.prototype.disable = function () {
if (this.disabled) return;
this.disabled = true;
this.changed = true;
};
CardButton.prototype.enable = function () {
if (!this.disabled) return;
this.disabled = false;
this.changed = true;
};
CardButton.prototype.update = function () {
if (this.bitmap.update()) this.changed = true;
if (!this.changed) return false;
this.changed = false;
this.maskLayer.graphics.clear();
if (this.order) {
this.orderLayer.visible = true;
this.orderLayer.text = this.order;
} else {
this.orderLayer.visible = false;
}
if (this.selected) {
this.maskLayer.graphics
.beginFill('rgba(255,255,255,0.5)')
.drawRect(0,0,this.width,this.height);
} else if (this.disabled) {
this.maskLayer.graphics
.beginFill('rgba(0,0,0,0.5)')
.drawRect(0,0,this.width,this.height);
} else if (this.hovered) {
this.maskLayer.graphics
.beginFill('rgba(255,255,255,0.1)')
.drawRect(0,0,this.width,this.height);
} else {
this.maskLayer.graphics.clear();
}
return true;
};

256
CardDialog.js Normal file
View file

@ -0,0 +1,256 @@
'use strict';
function CardDialog (game,id) {
this.game = game;
this.stage = new createjs.Stage(id+'Canvas');
this.stage.enableMouseOver(10);
this.scrollDiv = document.getElementById(id+'ScrollDiv');
this.windowDiv = document.getElementById(id+'WindowDiv');
this.warpDiv = document.getElementById(id+'WarpDiv');
this.titleDiv = document.getElementById(id+'TitleDiv');
this.closeIcon = document.getElementById(id+'CloseIcon');
this.okButton = document.getElementById(id+'OkButton');
this.cardButtons = [];
this.hidden = true;
this.canClose = false;
this.warpDiv.style.visibility = 'hidden';
var dialog = this;
}
CardDialog.prototype.showCards = function (title,cards,callback) {
var dialog = this;
cards.forEach(function (card) {
var btn = new CardButton(card,function () {});
dialog.cardButtons.push(btn);
dialog.stage.addChild(btn);
},this);
dialog.okButton.disabled = false;
dialog.okButton.textContent = '确定';
dialog.okButton.onclick = function (event) {
dialog.close();
callback();
}
dialog.okButton.hidden = false;
dialog.canClose = false;
dialog.pop(title);
};
CardDialog.prototype.selectOne = function (title,cards,callback) {
var dialog = this;
cards.forEach(function (card) {
var btn = new CardButton(card,function (event) {
dialog.close();
callback(card);
});
dialog.cardButtons.push(btn);
dialog.stage.addChild(btn);
},this);
dialog.okButton.hidden = true;
dialog.pop(title);
};
CardDialog.prototype.selectSomeAdvanced = function (title,cards,careOrder,onSelectChange,callback) {
var dialog = this;
var selectedIndexes = [];
cards.forEach(function (card,idx) {
var btn = new CardButton(card,function (event) {
if (!this.selected && this.disabled) return;
this.changed = true;
dialog.cardButtons.forEach(function (btn) {
btn.enable();
},this);
if (this.selected) {
this.selected = false;
removeFromArr(idx,selectedIndexes);
} else {
this.selected = true;
selectedIndexes.push(idx);
}
handleSelectChange();
});
dialog.cardButtons.push(btn);
dialog.stage.addChild(btn);
},this);
handleSelectChange();
this.okButton.textContent = '确定';
this.okButton.hidden = false;
dialog.pop(title);
function handleSelectChange () {
var done = onSelectChange(selectedIndexes,disable);
if (careOrder) {
dialog.cardButtons.forEach(function (btn,idx) {
var order = selectedIndexes.indexOf(idx) + 1;
btn.setOrder(order);
});
}
if (done) {
dialog.okButton.disabled = false;
dialog.okButton.onclick = function (event) {
dialog.close();
callback(selectedIndexes);
};
} else {
dialog.okButton.disabled = true;
dialog.okButton.onclick = null;
}
}
function disable (idx) {
dialog.cardButtons[idx].disable();
}
};
CardDialog.prototype.selectSome = function (title,cards,min,max,careOrder,callback) {
this.selectSomeAdvanced(title,cards,careOrder,function (selectedIndexes,disable) {
var len = selectedIndexes.length;
if (len >= max) {
cards.forEach(function (card,idx) {
disable(idx);
},this);
return true;
}
return len >= min;
},function (selectedIndexes) {
callback(selectedIndexes.map(function (idx) {
return cards[idx];
}));
});
};
CardDialog.prototype.seekSome = function (title,cards,targets,min,max,callback) {
this.selectSomeAdvanced(title,cards,false,function (selectedIndexes,disable) {
// 先 disable 掉 targets 以外的卡.
cards.forEach(function (card,idx) {
if (!inArr(card,targets)) disable(idx);
},this);
var len = selectedIndexes.length;
if (len >= max) {
cards.forEach(function (card,idx) {
disable(idx);
},this);
return true;
}
return len >= min;
},function (selectedIndexes) {
callback(selectedIndexes.map(function (idx) {
return cards[idx];
}));
});
};
CardDialog.prototype.selectEner = function (title,cards,colors,cost,callback) {
this.selectSomeAdvanced(title,cards,false,onSelectChange,function (selectedIndexes) {
callback(selectedIndexes.map(function (idx) {
return cards[idx];
}));
});
function onSelectChange (selectedIndexes,disable) {
var need = {};
var total = 0;
['colorless','white','black','red','blue','green','multi'].forEach(function (color) {
need[color] = cost[color] || 0;
total += need[color];
},this);
if (selectedIndexes.length >= total) {
cards.forEach(function (card,idx) {
disable(idx);
},this);
return true;
}
selectedIndexes.forEach(function (idx) {
var color = colors[idx];
if (color === 'multi') return;
if (need[color] > 0) {
need[color] -= 1;
} else {
need.colorless -= 1;
}
},this);
if (need.colorless > 0) return false;
cards.forEach(function (card,idx) {
var color = colors[idx];
if (color === 'multi') return;
if (!need[color]) disable(idx);
},this);
}
};
CardDialog.prototype.draw = function () {
var width = Card.WIDTH*2;
var height = Card.HEIGHT*2;
var space = 10;
var len = this.cardButtons.length;
var singleRow = len <= 3;
var totalWidth = (width + 2*space) * 3;
var totalHeight = (height + 2*space) * Math.ceil(this.cardButtons.length/3);
this.cardButtons.forEach(function (btn,i) {
var row = i%3; // 第row行 (下标从0开始);
var col = Math.floor(i/3); // 第col列 (下标从0开始);
if (singleRow) {
btn.x = (totalWidth - len*(width+2*space)) / 2;
} else {
btn.x = 0;
}
btn.x += row*(width+2*space) + space;
btn.y = col*(height+2*space) + space;
},this);
this.stage.canvas.width = totalWidth;
this.stage.canvas.height = totalHeight;
};
CardDialog.prototype.pop = function (title) {
this.draw();
this.titleDiv.textContent = title;
if (this.canClose) {
this.closeIcon.style.display = '';
this.closeIcon.onclick = this.close.bind(this);
} else {
this.closeIcon.style.display = 'none';
this.closeIcon.onclick = null;
}
this.hidden = false;
var warp = this.warpDiv;
var win = this.windowDiv;
var scroll = this.scrollDiv;
warp.style.opacity = '0';
warp.style.visibility = 'visible';
// 消灭水平滚动条
scroll.style.width = this.stage.canvas.width + scroll.offsetWidth - scroll.clientWidth + 'px';
// 居中
win.style.top = (this.game.stage.canvas.height - win.offsetHeight)/2 + 'px';
win.style.left = (this.game.stage.canvas.width - win.offsetWidth)/2 + 'px';
warp.style.opacity = '1';
this.update();
};
CardDialog.prototype.close = function () {
this.hidden = true;
this.cardButtons.length = 0;
this.stage.removeAllChildren();
this.warpDiv.style.visibility = 'hidden';
};
CardDialog.prototype.update = function () {
if (this.hidden) return;
var changed = false;
this.stage.children.forEach(function (bitmap) {
if (bitmap.update()) changed = true;
},this);
if (changed) {
this.stage.update();
}
};

42
CardDialogLayer.js Normal file
View file

@ -0,0 +1,42 @@
'use strict';
function CardDialogLayer (game,stage) {
this.game = game;
this.stage = stage;
this.div = stage.canvas.parentElement;
this.scrollDiv = this.div.parentElement;
}
CardDialogLayer.prototype.show = function (cards,callbacks) {
var width = Card.WIDTH*2;
var height = Card.HEIGHT*2;
var space = 10;
var totalWidth = (width + 2*space) * 3;
var totalHeight = (height + 2*space) * Math.ceil(this.cards.length/3);
this.cards.forEach(function (card,i) {
var img = this.game.getImageByCid(card.cid);
var bitmap = new CardBitmap(card.cid,img);
var row = i%3; // 第row行 (下标从0开始);
var col = Math.floor(i/3); // 第col列 (下标从0开始);
bitmap.x = row*(width+2*space) + space;
bitmap.y = col*(height+2*space) + space;
bitmap.setWidthHeight(width,height);
bitmap.on('click',callbacks[i]);
this.stage.addChild(bitmap);
},this);
this.stage.canvas.width = totalWidth;
this.stage.canvas.height = totalHeight;
this.stage.update();
var div = this.div;
var scrollDiv = this.scrollDiv;
div.style.opacity = '0';
div.style.visibility = 'visible';
// 消灭水平滚动条
scrollDiv.style.width = totalWidth + scrollDiv.offsetWidth - scrollDiv.clientWidth + 'px';
// 居中
scrollDiv.style.top = (this.stage.canvas.height - scrollDiv.offsetHeight)/2 + 'px';
scrollDiv.style.left = (this.stage.canvas.width - scrollDiv.offsetWidth)/2 + 'px';
div.style.opacity = '1';
};

115244
CardInfo.js Normal file

File diff suppressed because it is too large Load diff

19
CardInfo_ru.js Normal file

File diff suppressed because one or more lines are too long

10
Charging.html Normal file
View file

@ -0,0 +1,10 @@
<html>
<head>
<meta charset="UTF-8">
<title>WEBXOSS收费标准</title>
</head>
<body>
<p>放心,WEBXOSS将保持免费.</p>
</body>
</html>

67
ChatManager.js Normal file
View file

@ -0,0 +1,67 @@
'use strict';
function ChatManager (socket) {
this.socket = socket;
this.dialogue = document.getElementById('chat-dialogue');
this.input = document.getElementById('chat-input');
// socket.on('chat feedback',this.receiveFeedback.bind(this));
// socket.on('chat',this.onChat.bind(this));
this.input.onkeypress = function (event) {
var keyCode = event.keyCode || event.which || event.charCode;
if (keyCode !== 13) return;
this.sendMsg(this.input.value);
this.input.value = null;
}.bind(this);
}
// ChatManager.prototype.receiveFeedback = function (msg) {
// var chatCount = sessionStorage.getItem('chat count') || 0;
// chatCount++;
// sessionStorage.setItem('chat count',chatCount);
// this.addMsg(msg,'self');
// };
// ChatManager.prototype.onChat = function (msgObj) {
// this.addMsg(msg.nickname+':' + msg.msg,'opponent');
// };
ChatManager.prototype.sendMsg = function (msg) {
if (!msg) return;
this.socket.emit('chat',msg);
};
ChatManager.prototype.addSysMsg = function (msg) {
var div = document.createElement('div');
div.classList.add('sys');
div.textContent = msg;
this.dialogue.appendChild(div);
this.dialogue.scrollTop = this.dialogue.scrollHeight;
};
ChatManager.prototype.addMsg = function (name,content,isOpponent,isSpectator) {
var div = document.createElement('div');
if (isOpponent) div.classList.add('opponent');
if (isSpectator) div.classList.add('spectator');
var spanName = document.createElement('span');
spanName.classList.add('name');
spanName.textContent = name;
var spanContent = document.createElement('span');
spanContent.classList.add('content');
spanContent.textContent = content;
div.appendChild(spanName);
div.appendChild(spanContent);
this.dialogue.appendChild(div);
if (this.dialogue.children.length > 64) {
this.dialogue.removeChild(this.dialogue.firstChild);
}
this.dialogue.scrollTop = this.dialogue.scrollHeight;
};
ChatManager.prototype.clear = function () {
this.dialogue.innerHTML = '';
};

3
DeckEditor.appcache Normal file
View file

@ -0,0 +1,3 @@
CACHE MANIFEST
# 2015/04/06 23:59:53

1
DeckEditor/Deck.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,8 @@
CACHE MANIFEST
CACHE:
NETWORK:
*
# 2016/03/07 01:46:04

1
DeckEditor/DeckEditor.js Normal file

File diff suppressed because one or more lines are too long

583
DeckEditor/Rules.js Normal file
View file

@ -0,0 +1,583 @@
'use strict';
function TextualRule (prop,map,exact) {
this.prop = prop;
this.map = map;
this.exact = exact;
}
TextualRule.prototype.parse = function (words) {
var keywords = [];
for (var i = 0; i < words.length; i++) {
var word = words[i];
var keyword = this.parseWord(word);
if (!keyword) continue;
keywords.push(keyword);
words.splice(i,1);
i--;
}
var prop = this.prop;
var exact = this.exact;
return function filter (info) {
if (!keywords.length) return true;
return keywords.some(function (keyword) {
var value = info[prop].toLowerCase();
if (exact) {
return value === keyword;
} else {
return value.indexOf(keyword) !== -1;
}
});
};
};
TextualRule.prototype.parseWord = function (word) {
for (var keyword in this.map) {
var matchWords = this.map[keyword];
if (matchWords.some(function (matchWord) {
return word === Localize.traditionalize(matchWord);
},this)) {
return keyword;
}
}
return null;
};
///////////////////////////////////////////////////////////////
//
// 枚举型规则,包括:
// ColorRule,TypeRule,RarityRule
// 匹配例子:
// "白 LRIG SR 红"
// "白色 精灵 lr red"
//
///////////////////////////////////////////////////////////////
var ColorRule = new TextualRule('color',{
'colorless': ['colorless','无','无色','無','incolore'],
'white': ['white','白','白色','白','белая','bianco'],
'black': ['black','黑','黑色','黒','чёрная','nero'],
'red': ['red','红','红色','赤','красная','rosso'],
'blue': ['blue','蓝','蓝色','青','синяя','blu'],
'green': ['green','绿','绿色','緑','зелёная','verde']
},false);
var TypeRule = new TextualRule('cardType',{
'lrig': ['l','lrig','分身','ルリグ','идел'],
'signi': ['s','signi','精灵','シグニ','запись'],
'spell': ['spell','魔法','スペル','магия'],
'arts': ['arts','必杀','技艺','アーツ','умение'],
'resona': ['resona','共鸣','レゾナ','отголосок']
},true);
var RarityRule = new TextualRule('rarity',{
'c': ['c'],
'r': ['r'],
'lc': ['lc'],
'sr': ['sr'],
'lr': ['lr'],
'st': ['st'],
'pr': ['pr'],
'sp': ['sp']
},true);
///////////////////////////////////////////////////////////////
//
// 效果(能力)型规则:
// SkillRule
// 匹配例子:
// "常","常时","常时效果","常时能力"
// "※","爆发","爆发效果","迸发","迸发效果"
//
///////////////////////////////////////////////////////////////
var SkillRule = {};
SkillRule.parse = function (words) {
var effectProps = [];
for (var i = 0; i < words.length; i++) {
var word = words[i];
var effectProp = this.parseWord(word);
if (!effectProp) continue;
effectProps.push(effectProp);
words.splice(i,1);
i--;
}
return function filter (info) {
if (!effectProps.length) return true;
info = CardInfo[info.cid];
return effectProps.some(function (effectProp) {
var effectTexts = info[effectProp];
return effectTexts && effectTexts.length;
});
};
};
SkillRule.parseWord = TextualRule.prototype.parseWord;
SkillRule.map = {
'constEffectTexts': [
'【常】','常','常时','常时效果','常時能力',
'【常】','常','常時','常時效果','常時能力',
'[constant]','const','constant',
'[постоянно]','постоянно'
],
'startUpEffectTexts': [
'【出】','出','出场','出场效果','出场能力',
'【出】','出','出現','出現效果','出現能力',
'[on-play]','[onplay]','on-play','onplay',
'[при вводе]','при вводе'
],
'actionEffectTexts': [
'【起】','起','起动','起动效果','起动能力',
'【起】','起','起動','起動效果','起動能力',
'[action]','action',
'[действие]','действие'
],
'burstEffectTexts': [
'【※】','※','爆发','迸发','爆发效果','迸发效果','生命爆发','生命迸发','生命爆发效果','生命迸发效果',
'ライフバースト','バースト',
'burst','lifeburst','lb',
'вспышка','жизненная вспышка'
]
};
///////////////////////////////////////////////////////////////
//
// 无迸发规则:
// NoBurstRule
// 匹配例子:
// "无爆发","noburst"
//
///////////////////////////////////////////////////////////////
var NoBurstRule = {};
NoBurstRule.parse = function (words) {
var matched = false;
for (var i = 0; i < words.length; i++) {
var word = words[i];
if (!this.parseWord(word)) continue;
matched = true;
words.splice(i,1);
i--;
}
return function filter (info) {
if (!matched) return true;
info = CardInfo[info.cid];
return (info.cardType === 'SIGNI' || info.cardType === 'SPELL') &&
(!info.burstEffectTexts || !info.burstEffectTexts.length);
};
};
NoBurstRule.parseWord = TextualRule.prototype.parseWord;
NoBurstRule.map = {
'burstEffectTexts': [
'无爆发','无迸发','无爆发效果','无迸发效果','无生命爆发','无生命迸发','无生命爆发效果','无生命迸发效果',
'noburst',
'ライフバースト-','バースト-',
'迸发-','爆发-',
'burst-','lifeburst-','lb-',
'вспышка-','жизненная вспышка-'
]
};
///////////////////////////////////////////////////////////////
//
// CROSS规则:
// CrossRule
// 匹配例子:
// "cross","交错","クロス"等
//
///////////////////////////////////////////////////////////////
var CrossRule = {};
CrossRule.parse = function (words) {
var matched = false;
for (var i = 0; i < words.length; i++) {
var word = words[i];
if (!this.parseWord(word)) continue;
matched = true;
words.splice(i,1);
i--;
}
return function filter (info) {
if (!matched) return true;
info = CardInfo[info.cid];
return (info.cardType === 'SIGNI') &&
(info.crossLeft || info.crossRight);
};
};
CrossRule.parseWord = TextualRule.prototype.parseWord;
CrossRule.map = {
'cross': [
'cross','交错','クロス',
'[cross]','>cross<','【cross】','【交错】','【クロス】','[связь]'
]
};
///////////////////////////////////////////////////////////////
//
// 时点规则:
// TimmingRule
// 匹配例子:
// "【主要阶段】","主要阶段"
// "【魔法切入】","魔法切入"
//
///////////////////////////////////////////////////////////////
var TimmingRule = {};
TimmingRule.parse = function (words) {
var timmings = [];
for (var i = 0; i < words.length; i++) {
var word = words[i];
var timming = this.parseWord(word);
if (!timming) continue;
timmings.push(timming);
words.splice(i,1);
i--;
}
return function filter (info) {
if (!timmings.length) return true;
return timmings.some(function (timming) {
if (!info.timmings) return false;
return inArr(timming,info.timmings);
});
};
};
TimmingRule.parseWord = TextualRule.prototype.parseWord;
TimmingRule.map = {
'mainPhase': [
'主要阶段','【主要阶段】','主要',
'メインフェイズ','【メインフェイズ】',
'[mainphase]','mainphase','main',
'[основнаяфаза]','основнаяфаза','основная'
],
'attackPhase': [
'攻击阶段','【攻击阶段】','攻击',
'アタックフェイズ','【アタックフェイズ】',
'[attackphase]','attackphase','attack',
'[фазаатаки]','фазаатаки','атака'
],
'spellCutIn': [
'魔法切入','【魔法切入】','切入',
'スペルカットイン','【スペルカットイン】',
'[spellcut-in]','[cut-in]','[spellcutin]','[cutin]','spellcutin','cutin','cut',
'[ответнамагию]','[ответ]','ответнамагию','ответ'
]
};
///////////////////////////////////////////////////////////////
//
// 限定规则:
// LimitingRule
// 匹配例子:
// "小玉"
// "小玉+"
//
///////////////////////////////////////////////////////////////
var LimitingRule = {};
LimitingRule.parse = function (words) {
var matchedClasses = [];
var flagNoLimiting = false;
for (var i = 0; i < words.length; i++) {
var word = words[i];
var classes = [
'タマ','花代','ユヅキ','ピルルク','エルドラ','ミルルン','緑子',
'アン','ウリス','イオナ','ウムル','リメンバ','タウィル','サシェ',
'ミュウ','アイヤイ','アルフォウ','ハナレ'
];
for (var j = 0; j < classes.length; j++) {
var cls = classes[j];
var matched = false;
var localizedClass = Localize('class',cls).toLowerCase();
if (word === localizedClass) {
matched = true;
} else if (word === localizedClass+'+') {
matched = true;
flagNoLimiting = true;
}
if (matched) {
matchedClasses.push(cls);
words.splice(i,1);
i--;
break;
}
}
}
return function filter (info) {
if (!matchedClasses.length) return true;
if (info.cardType === 'LRIG') {
return matchedClasses.some(function (cls) {
if (!info.classes) return false;
return inArr(cls,info.classes);
},this);
}
if (!info.limiting) return flagNoLimiting;
return inArr(info.limiting,matchedClasses);
};
};
///////////////////////////////////////////////////////////////
//
// 类别规则:
// ClassRule
// 匹配例子:
// "精武"
// "武装"
//
///////////////////////////////////////////////////////////////
var ClassRule = {};
ClassRule.parse = function (words) {
var matchedClasses = [];
for (var i = 0; i < words.length; i++) {
var word = words[i];
var classes = [
'精像','天使','悪魔','美巧','精武','アーム','ウェポン','遊具',
'毒牙','精羅','鉱石','宝石','植物','原子','宇宙','精械','電機',
'古代兵器','迷宮','精生','水獣','空獣','地獣','龍獣','凶蟲','精元'
];
for (var j = 0; j < classes.length; j++) {
var cls = classes[j];
if (word === Localize('class',cls).toLowerCase().replace(' ','')) {
matchedClasses.push(cls);
words.splice(i,1);
i--;
break;
}
}
}
return function filter (info) {
if (!matchedClasses.length) return true;
return matchedClasses.some(function (cls) {
if (!info.classes) return false;
return inArr(cls,info.classes);
},this);
};
};
///////////////////////////////////////////////////////////////
//
// 数值型规则,包括:
// PowerRule,LevelRule,LimitRule
// 匹配格式:
// 相等: "关键字:=数值"
// 大于: "关键字:>=数值" 或 "关键字:数值+"
// 小于: "关键字:<=数值" 或 "关键字:数值-"
// 范围: "关键字:min~max" 或 "关键字:min-max"
// (注: 上述的":"和"="可省略.)
// 匹配例子(每行等价):
// "力量>100","Power>100","Power:100+","Power100+"
// "等级=4","level=4","level4","lv4","lv.4"
// "界限>7 界限<9","limit7+ 界限9-","界限:7-9","界限7~9"
//
///////////////////////////////////////////////////////////////
function NumericRule (prop,keywords) {
this.prop = prop;
this.keywords = keywords;
}
NumericRule.prototype.parseExpression = function (str) {
var match;
// 相等
match = str.match(/^=*(\d+)$/);
if (match) {
var num = parseInt(match[1]);
return [num,num];
}
// 大于
match = str.match(/^>=*(\d+)\+?$/);
if (!match) match = str.match(/^(\d+)\+$/);
if (match) {
var num = parseInt(match[1]);
return [num,Infinity];
}
// 小于
match = str.match(/^<=*(\d+)\-?$/);
if (!match) match = str.match(/^(\d+)\-$/);
if (match) {
var num = parseInt(match[1]);
return [-Infinity,num];
}
// 范围
match = str.match(/^(\d+)[\-\~](\d+)$/);
if (match) {
var a = parseInt(match[1]);
var b = parseInt(match[2]);
return [a,b];
}
return null
};
NumericRule.prototype.parseWord = function (word) {
for (var i = 0; i < this.keywords.length; i++) {
var keyword = Localize.traditionalize(this.keywords[i]);
if (word.indexOf(keyword) === 0) {
word = word.slice(keyword.length).replace(/^:/,'');
return this.parseExpression(word);
}
}
return null;
};
NumericRule.prototype.parse = function (words) {
var ranges = [];
for (var i = 0; i < words.length; i++) {
var word = words[i];
var range = this.parseWord(word);
if (!range) continue;
ranges.push(range);
words.splice(i,1);
i--;
}
var prop = this.prop;
return function filter (info) {
if (!ranges.length) return true;
if ((info.cardType === 'SPELL') || (info.cardType === 'ARTS')) {
return false;
}
return ranges.every(function (range) {
var value = info[prop];
var min = range[0];
var max = range[1];
return (value >= min) && (value <= max);
});
};
};
var PowerRule = new NumericRule('power',['力量','パワー','power','сила']);
var LevelRule = new NumericRule('level',['等级','レベル','level','lv.','lv','уровень','livello']);
var LimitRule = new NumericRule('limit',['界限','リミット','limite','limit','ограничение']);
///////////////////////////////////////////////////////////////
//
// 数字规则: NumberRule.
// 匹配说明:
// 力量和等级的关键字可以省略,此时用NumberRule匹配.
// 数字小于10,则匹配为等级,否则匹配为力量.
// 匹配例子:
// "1000+ 3-" // 等价于"power>1000 level<3"
// ">3 2000~3000" // 等价于"level>3 power:2000~3000"
//
///////////////////////////////////////////////////////////////
var NumberRule = new NumericRule('',['']);
NumberRule.parse = function (words) {
var ranges = [];
for (var i = 0; i < words.length; i++) {
var word = words[i];
var range = this.parseWord(word);
if (!range) continue;
ranges.push(range);
words.splice(i,1);
i--;
}
return function filter (info) {
return ranges.every(function (range) {
if ((info.cardType === 'SPELL') || (info.cardType === 'ARTS')) return false;
function matchLevel (num) {
return !isFinite(num) || (num < 10);
}
var value = range.every(matchLevel)? info.level : info.power;
var min = range[0];
var max = range[1];
return (value >= min) && (value <= max);
});
};
};
///////////////////////////////////////////////////////////////
//
// 画师规则: IllustRule.
// 匹配例子:
// "画师:pop"
// "Illust:藤真拓哉"
//
///////////////////////////////////////////////////////////////
var IllustRule = {};
IllustRule.parseWord = function (word) {
var match = word.match(/illust:?(.+)/);
if (!match) match = word.match(/画师:?(.+)/);
if (!match) match = word.match(/畫師:?(.+)/);
if (!match) return null;
return match[1];
}
IllustRule.parse = function (words) {
var illusts = [];
for (var i = 0; i < words.length; i++) {
var word = words[i];
var illust = this.parseWord(word);
if (!illust) continue;
illusts.push(illust);
words.splice(i,1);
i--;
}
return function filter (info) {
if (!illusts.length) return true;
return illusts.some(function (illust) {
var cardIllust = info.illust.toLowerCase();
return cardIllust.indexOf(illust) !== -1;
});
};
};
///////////////////////////////////////////////////////////////
//
// ID规则: WxidRule.
// 匹配例子:
// "WD01-001","wd01-001","wD01001" // 仅匹配"WD01-001"
// "wx02-01","WX0201" // 匹配"WX02-010"至"WX02-019"
// "wd03-","WD03" // 匹配"WD03"的所有卡
//
///////////////////////////////////////////////////////////////
var WxidRule = {};
WxidRule.parseWord = function (word) {
var match = word.match(/^(wx\d{2}|wd\d{2}|pr|sp\d{2})-?(\d{0,3})$/);
if (!match) return null;
return match[1] + '-' + match[2];
}
WxidRule.parse = function (words) {
var idLimits = [];
for (var i = 0; i < words.length; i++) {
var word = words[i];
var limit = this.parseWord(word);
if (!limit) continue;
idLimits.push(limit);
words.splice(i,1);
i--;
}
return function filter (info) {
if (!idLimits.length) return true;
return idLimits.some(function (limit) {
return info.wxid.toLowerCase().indexOf(limit) === 0;
});
};
};
///////////////////////////////////////////////////////////////
//
// 卡名规则: NameRule.
// 匹配例子:
// "喷流 知识" // 卡名同时包含"喷流"和"知识".
// "喷流|知识" // 卡名包含"喷流"或"知识".
// "喷流||知识" // 卡名包含"喷流"或"知识".
// "喷流||的||知识" // 卡名包含"喷流"或"知识"或"的".
//
///////////////////////////////////////////////////////////////
var NameRule = {};
NameRule.dotRegex =
/[\u00B7\u0387\u05BC\u2022\u2027\u2219\u22C5\u30FB\uFF0E\uFF65]/g;
NameRule.fullWidth =
'' +
'' +
'';
NameRule.halfWidth =
'0123456789=@' +
'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
'abcdefghijklmnopqrstuvwxyz';
NameRule.normalizeString = function (str) {
var chars = str.replace(this.dotRegex,'').split('');
chars.forEach(function (char,idx) {
var i = this.fullWidth.indexOf(char);
chars[idx] = this.halfWidth[i] || char;
},this);
return chars.join('').toLowerCase();
};
NameRule.parse = function (words) {
var wordSets = [];
words.forEach(function (word) {
wordSets.push(word.split(/\|+/));
},this);
words.length = 0;
var that = this;
return function filter (info) {
return wordSets.every(function (wordSet) {
if (!wordSet.length) return true;
return wordSet.some(function (word) {
var name = that.normalizeString(Localize.cardName(info));
var w = that.normalizeString(word);
return (name.indexOf(w) !== -1);
},this);
},this);
};
};

View file

@ -0,0 +1,71 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Search Tips</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<style>
body,html {
font-family: 'Microsoft YaHei','WenQuanYi Micro Hei',sans-serif;
}
ul {
margin: 0;
padding: 0 0 0 1.5em;
list-style-type: none;
}
#examples > li {
margin: 0.5em 0;
padding-left: 0.5em;
border-left: #888 2px solid;
}
code {
font-family: monospace;
white-space: nowrap;
padding: 0.2em 0.5em;
background-color: #F0F0F0;
}
</style>
</head>
<body>
<section>
<p>在WEBXOSS中,您可以按卡牌的编号,名字,类型,颜色,等级等进行组合搜索.</p>
<p>搜索示例:</p>
<ul id="examples">
<li>
ID为WX01-001的卡: <code>WX01-001</code>
</li>
<li>
预组包02: <code>WD02</code>
</li>
<li>
白色或红色的精灵: <code>白 红 S</code>
</li>
<li>
蓝色的0级pr分身: <code>蓝色 0 L pr</code>
</li>
<li>
使用时点包括攻击阶段的必杀: <code>必杀 攻击阶段</code>
</li>
<li>
力量3000以上,等级2的精灵: <code>3000+ 2</code>
</li>
<li>
小玉限定的有爆发效果的卡: <code>小玉 爆发</code>
</li>
<li>
小玉限定或无限定的,没有爆发效果的卡: <code>小玉+ 爆发-</code>
</li>
<li>
类别为"地兽"的精灵: <code>地兽</code>
</li>
<li>
有出场效果和常时效果的卡: <code>出 常</code>
</li>
<li>
画师是POP的卡: <code>画师:POP</code>
</li>
</ul>
</section>
</body>
</html>

View file

@ -0,0 +1,71 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Search Tips</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<style>
body,html {
font-family: Arial,Helvetica,sans-serif;
}
ul {
margin: 0;
padding: 0 0 0 1.5em;
list-style-type: none;
}
#examples > li {
margin: 0.5em 0;
padding-left: 0.5em;
border-left: #888 2px solid;
}
code {
font-family: monospace;
white-space: nowrap;
padding: 0.2em 0.5em;
background-color: #F0F0F0;
}
</style>
</head>
<body>
<section>
<p>In WEBXOSS, you can search card by its id, name, type, color, level, etc, combined.</p>
<p>Examples:</p>
<ul id="examples">
<li>
Card with ID WX01-001: <code>WX01-001</code>
</li>
<li>
Pre-built deck 02: <code>WD02</code>
</li>
<li>
White or red SIGNI: <code>white red S</code>
</li>
<li>
Level 0, blue, PR LRIG: <code>blue 0 L pr</code>
</li>
<li>
ARTS with use timing of attack phase: <code>ARTS AttackPhase</code>
</li>
<li>
Level 2 SIGNI with power 3000 or above: <code>3000+ 2</code>
</li>
<li>
Tama only with life burst: <code>tama burst</code>
</li>
<li>
Tama only or unlimited, without life burst: <code>tama+ burst-</code>
</li>
<li>
&lt;Earth Beast&gt; SIGNI: <code>EarthBeast</code>
</li>
<li>
With [On-Play] and [Constant] abilities: <code>OnPlay Constant</code>
</li>
<li>
Illust by POP: <code>Illust:POP</code>
</li>
</ul>
</section>
</body>
</html>

View file

@ -0,0 +1,71 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Search Tips</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<style>
body,html {
font-family: Arial,Helvetica,sans-serif;
}
ul {
margin: 0;
padding: 0 0 0 1.5em;
list-style-type: none;
}
#examples > li {
margin: 0.5em 0;
padding-left: 0.5em;
border-left: #888 2px solid;
}
code {
font-family: monospace;
white-space: nowrap;
padding: 0.2em 0.5em;
background-color: #F0F0F0;
}
</style>
</head>
<body>
<section>
<p>In WEBXOSS, puoi ricercare le carte tramite il loro id, nome, tipo, colore, livello, etc, contemporaneamente.</p>
<p>Esempi:</p>
<ul id="examples">
<li>
Carte con id WX01-001: <code>WX01-001</code>
</li>
<li>
Deck precostruiti 02: <code>WD02</code>
</li>
<li>
SIGNI bianchi o rossi: <code>white red S</code>
</li>
<li>
Livello 0, blue, PR LRIG: <code>blue 0 L pr</code>
</li>
<li>
ARTS attivabili nell'attack phase: <code>ARTS AttackPhase</code>
</li>
<li>
SIGNI livello 2 con potere 3000 o piu: <code>3000+ 2</code>
</li>
<li>
Tama only con life burst: <code>tama burst</code>
</li>
<li>
Tama only o senza restrizioni senza lifeburst: <code>tama+ burst-</code>
</li>
<li>
&lt;Earth Beast&gt; SIGNI: <code>EarthBeast</code>
</li>
<li>
Con abilita [On-Play] e [Constant]: <code>OnPlay Constant</code>
</li>
<li>
Illustrate da POP: <code>Illust:POP</code>
</li>
</ul>
</section>
</body>
</html>

36
DeckEditor/Searcher.js Normal file
View file

@ -0,0 +1,36 @@
'use strict';
function Searcher () {
this.infos = [];
for (var pid in CardInfo) {
this.infos.push(CardInfo[pid]);
}
this.rules = [
ColorRule,
CrossRule,
TypeRule,
RarityRule,
SkillRule,
NoBurstRule,
// LifeBurstRule,
TimmingRule,
LimitingRule,
ClassRule,
PowerRule,
LevelRule,
LimitRule,
NumberRule,
IllustRule,
WxidRule,
NameRule
];
}
Searcher.prototype.search = function (str) {
// if (!str) return [];
var words = str.toLowerCase().split(/\s+/);
var filters = this.rules.map(function (rule) {
return rule.parse(words);
},this);
return filters.reduce(function (results,filter) {
return results.filter(filter);
},this.infos);
};

234
DeckEditor/editor.css Normal file
View file

@ -0,0 +1,234 @@
html {
margin: 0;
padding: 0;
/*font-family: 'Hiragino Sans GB','Microsoft YaHei','WenQuanYi Micro Hei',sans-serif;*/
font-family: 'Microsoft YaHei','WenQuanYi Zen Hei',sans-serif;
background-image: url('../background/bg.png');
}
body {
margin: 5px 5px 0;
padding: 0;
width: 1100px;
}
table, th, td {
border: 1px solid black;
padding: 0;
}
table {
border-collapse: collapse;
border-spacing: 0px;
}
ol,ul,li {
margin: 0;
padding: 0;
list-style-type: none;
}
/*#Detail,#Deck,#Search {
min-height: 720px;
}*/
#div-search-results {
max-height: 600px;
overflow-y: auto;
}
#Detail {
float: left;
width: 260px;
}
#detail-card-figure {
height: 349px;
}
#detail-card-image {
border-radius: 4%;
}
#detail-card-data {
height: 340px;
overflow-y: auto;
overflow-x: hidden;
position: relative;
}
#detail-card-wxid {
font-size: small;
}
#detail-card-name {
font-weight: bold;
}
#detail-card-name > a {
color: inherit;
text-decoration: none;
}
#detail-card-name > a:active {
color: red;
}
#detail-card-limiting {
position: absolute;
top: 0;
right: 0.5em;
}
#detail-table {
width: 100%;
white-space: pre-line;
}
#detail-table[lang="ru"] {
word-break: break-all;
}
#Deck {
position: relative;
float: left;
width: 620px;
}
#link-back-to-webxoss {
position: absolute;
right: 1em;
top: 1em;
color: blue;
text-decoration: none;
/* font-size: small; */
}
#div-import-warp {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: 10;
background-color: rgba(255, 255, 255, 0.8);
}
#div-import-export {
position: absolute;
width: 500px;
top: 10%;
left: 0;
right: 0;
margin: auto;
padding: 1em 2em;
background-color: white;
box-shadow: 0px 0px 38px rgba(0,0,0,0.7);
}
#textarea-import-export {
display: block;
width: 100%;
height: 15em;
box-sizing: border-box;
resize: vertical;
}
#div-import-footer {
text-align: center;
margin-top: 0.5em;
}
#div-import-footer > button {
margin-left: 0.5em;
/* margin-right: 0.5em; */
}
#select-decks,
#input-new-deck-name {
width: 10em;
box-sizing: border-box;
}
#main-deck-text-bar,
#lrig-deck-text-bar {
font-size: small;
}
#main-deck-burst {
float: right;
}
#main-deck-mayus-room {
display: none;
text-decoration: none;
margin-left: 1em;
}
#main-deck-mayus-room.invalid {
display: inline;
}
#main-deck-title,
#lrig-deck-title {
color: green;
transition: color 0.5s,background-color 0.5s;
}
#main-deck-title.invalid,
#lrig-deck-title.invalid,
#main-deck-mayus-room.invalid {
color: red;
background-color: yellow;
}
#main-deck-title::before,
#lrig-deck-title::before,
#main-deck-mayus-room::before {
content: '\2714';
}
#main-deck-title.invalid::before,
#lrig-deck-title.invalid::before,
#main-deck-mayus-room.invalid::before {
content: '\2718';
}
#main-deck-zone img,
#lrig-deck-zone img {
position: absolute;
width: 62px;
height: 87px;
transition: left 0.3s, top 0.3s;
cursor: pointer;
}
#main-deck-zone {
position: relative;
width: 620px;
height: 435px;
}
#lrig-deck-zone {
position: relative;
width: 620px;
height: 174px;
}
#Search {
float: left;
width: 200px;
text-align: center;
}
#div-search-tips {
text-align: right;
}
#link-search-tips {
font-size: small;
color: inherit;
text-decoration: underline;
}
#search-input {
text-align: center;
width: 100%;
box-sizing: border-box;
}
#search-list li {
margin: 5px 0;
}
#search-list img {
width: 127px;
height: 176px;
cursor: pointer;
}
#search-show-more {
cursor: pointer;
color: blue;
}

523
DeckEditor/editor.js Normal file
View file

@ -0,0 +1,523 @@
'use strict';
// window.addEventListener('DOMContentLoaded',function () {
var $ = document.getElementById.bind(document);
function hide (el) {
el.style.display = 'none';
}
function show (el) {
el.style.display = '';
}
function disable (el) {
el.disabled = true;
}
function enable (el) {
el.disabled = false;
}
function newElement (tag) {
var el = document.createElement(tag);
for (var i = 1; i < arguments.length; i++) {
el.classList.add(arguments[i]);
}
return el;
}
function getImageUrlByPid (pid) {
return imageManager.getUrlByPid(pid);
}
Localize.init();
Localize.DOM('DeckEditor');
window.searcher = new Searcher();
window.imageManager = new ImageManager('../');
window.detail = new CardDetail(imageManager);
// ====== 搜索开始 ======
var results = [];
var RESULTS_LENGTH = 20;
var _shown = 0;
$('search-input').onchange = search;
$('search-input').onkeyup = search;
function search () {
var q = $('search-input').value;
results = searcher.search(q);
showResults();
}
function showResults () {
_shown = 0;
$('search-list').innerHTML = '';
showMore();
}
$('search-show-more').onclick = showMore;
function showMore () {
for (var i = 0; i < RESULTS_LENGTH; i++) {
var idx = _shown;
if (idx >= results.length) break;
var info = results[idx];
var li = newElement('li');
var img = new Image();
img.src = getImageUrlByPid(info.pid);
img.alt = Localize.cardName(info);
img.title = Localize.cardName(info);
img.onmousemove = showDetail.bind(null,info);
img.onclick = addCardByInfo.bind(null,info,false);
li.appendChild(img);
$('search-list').appendChild(li);
_shown++;
}
if (_shown < results.length) {
show($('search-show-more'));
} else {
hide($('search-show-more'));
}
}
function showDetail (info) {
detail.show(info.pid);
}
// ====== 搜索结束 ======
// ====== 组卡开始 ======
var WIDTH = 62;
var HEIGHT = 87;
var mainData = {
deck: 'main',
limit: 50,
deckObjs: [],
zone: $('main-deck-zone')
};
var lrigData = {
deck: 'lrig',
limit: 20,
deckObjs: [],
zone: $('lrig-deck-zone')
};
var deckManager = new DeckManager();
var deckNames = [];
var deckName = '';
var deckIndex = -1;
var whiteHope = {
mainDeck: [
112,113,114,115,116,117,118,119,120,121,
112,113,114,115,116,117,118,119,120,121,
112,113,114,115,116,117,118,119,120,121,
112,113,114,115,116,117,118,119,120,121
],
lrigDeck: [104,105,106,107,108,109,110,111]
};
updateDeckList();
selectDeck(0);
function updateDeckList () {
$('select-decks').innerHTML = '';
deckNames = deckManager.getDeckNames();
if (!deckNames.length) {
createDeck('WHITE_HOPE',whiteHope);
return;
}
deckNames.forEach(function (name) {
var eOption = newElement('option');
eOption.textContent = name;
$('select-decks').appendChild(eOption);
},this);
}
function loadDeck (name) {
var deck = deckManager.loadDeck(name);
removeAllCards(mainData);
deck.mainDeck.forEach(function (pid) {
var info = CardInfo[pid];
addCardByInfo(info,true);
});
removeAllCards(lrigData);
deck.lrigDeck.forEach(function (pid) {
var info = CardInfo[pid];
addCardByInfo(info,true);
});
updateDeck(mainData);
updateDeck(lrigData);
}
function saveDeck (name) {
deckManager.saveDeck(name,getCurrentDeck());
}
var emptyDeck = {
mainDeck: [],
lrigDeck: []
};
$('button-new-deck').onclick = function (event) {
if (!$('input-new-deck-name').value) {
window.alert(Localize.editor('PLEASE_INPUT_A_DECK_NAME'));
$('input-new-deck-name').focus();
return;
}
createDeck($('input-new-deck-name').value,emptyDeck);
$('input-new-deck-name').value = '';
};
$('button-copy-deck').onclick = function (event) {
if (!$('input-new-deck-name').value) {
window.alert(Localize.editor('PLEASE_INPUT_A_DECK_NAME'));
$('input-new-deck-name').focus();
return;
}
createDeck($('input-new-deck-name').value);
$('input-new-deck-name').value = '';
};
function createDeck (name,deck) {
if (!name) return false;
var idx = deckNames.indexOf(name);
if (idx >= 0) {
selectDeck(idx);
return;
}
deckManager.createDeck(name,deck || getCurrentDeck());
updateDeckList();
idx = deckNames.indexOf(name);
selectDeck(idx);
}
$('button-delete-deck').onclick = function (event) {
if (window.confirm(Localize.editor('CONFIRM_DELETE_DECK',deckName))) {
deleteDeck(deckName);
}
};
function deleteDeck (name) {
var idx = deckIndex;
deckManager.deleteDeck(name);
updateDeckList();
if (idx >= deckNames.length) {
idx = deckNames.length-1;
}
selectDeck(idx);
}
$('select-decks').onchange = function (event) {
selectDeck($('select-decks').selectedIndex);
};
$('button-rename').onclick = function (event) {
var newName = window.prompt(Localize.editor('DECK_NAME'),deckName);
if (!newName || newName === deckName) return;
if (inArr(newName,deckNames)) {
window.alert(Localize.editor('DECK_NAME_ALREADY_EXISTS',newName));
return;
}
renameDeck(newName);
}
function renameDeck (newName) {
var oldName = deckName;
createDeck(newName);
deleteDeck(oldName);
}
function selectDeck (idx) {
deckIndex = idx;
deckName = deckNames[idx];
loadDeck(deckName);
$('select-decks').selectedIndex = idx;
}
function getCurrentDeck () {
var deck = {};
deck.mainDeck = mainData.deckObjs.map(function (deckObj) {
return deckObj.info.pid;
});
deck.lrigDeck = lrigData.deckObjs.map(function (deckObj) {
return deckObj.info.pid;
});
return deck;
}
function dataToPids (data) {
return data.deckObjs.map(function (obj) {
return obj.info.pid;
});
}
function addCardByInfo (info,dontUpdateOrSave) {
var data;
if (info.cardType==='LRIG' || info.cardType==='ARTS' || info.cardType==='RESONA') {
data = lrigData;
} else {
data = mainData;
}
if (data.deckObjs.length >= data.limit) {
return;
}
data.deckObjs.push({
idx: data.deckObjs.length,
info: info,
img: null
});
if (!dontUpdateOrSave) {
updateDeck(data);
saveDeck(deckName);
}
}
function removeCardByIndex (data,idx,dontUpdateOrSave) {
data.zone.removeChild(data.deckObjs[idx].img);
data.deckObjs.splice(idx,1);
updateDeck(data);
if (!dontUpdateOrSave) saveDeck(deckName);
}
function removeAllCards (data) {
data.zone.innerHTML = '';
data.deckObjs.length = 0;
}
function updateDeck (data) {
defaultSort(data);
var pids = dataToPids(data);
var valid,elTitle;
if (data.deck === 'main') {
valid = deckManager.checkMainDeck(pids);
elTitle = $('main-deck-title');
$('main-deck-burst-count').textContent = deckManager.burstCount(pids);
} else {
valid = deckManager.checkLrigDeck(pids);
elTitle = $('lrig-deck-title');
}
if (valid) {
elTitle.classList.remove('invalid');
} else {
elTitle.classList.add('invalid');
}
elTitle = $('main-deck-mayus-room');
valid = deckManager.checkMayusRoom(dataToPids(mainData).concat(dataToPids(lrigData)));
if (valid) {
elTitle.classList.remove('invalid');
} else {
elTitle.classList.add('invalid');
}
data.deckObjs.forEach(function (obj,idx) {
var info = obj.info;
var img = obj.img;
if (!img) {
img = new Image();
img.src = getImageUrlByPid(info.pid);
img.alt = Localize.cardName(info);
img.onmousemove = showDetail.bind(null,info);
obj.img = img;
data.zone.appendChild(img);
}
img.onclick = removeCardByIndex.bind(null,data,idx,false);
img.style.left = ((idx%10)*WIDTH) + 'px';
img.style.top = (Math.floor(idx/10)*HEIGHT) + 'px';
},this);
}
function defaultSort (data) {
data.deckObjs.sort(function (aObj,bObj) {
var a = aObj.info;
var b = bObj.info;
var aIdx = aObj.idx;
var bIdx = bObj.idx;
if (a.cardType === 'LRIG') {
if (b.cardType !== 'LRIG') return -1;
if (b.level !== a.level) {
return a.level - b.level;
}
}
if (a.cardType === 'ARTS') {
if (b.cardType !== 'ARTS') return 1;
}
if (a.cardType === 'RESONA') {
if (b.cardType === 'LRIG') return 1;
if (b.cardType === 'ARTS') return -1;
if (b.level !== a.level) {
return a.level - b.level;
}
}
if (a.cardType === 'SIGNI') {
if (b.cardType !== 'SIGNI') return -1;
if (a.level !== b.level) {
return b.level - a.level;
}
if (a.power !== b.power) {
return a.power - b.power;
}
}
if (a.cardType === 'SPELL') {
if (b.cardType !== 'SPELL') return 1;
}
if (a.cid !== b.cid) {
return a.cid - b.cid
}
return aIdx - bIdx;
});
data.deckObjs.forEach(function (obj,idx) {
obj.idx = idx;
});
}
// ====== 导入导出开始 ======
$('button-import-export').onclick = function (event) {
show($('div-import-warp'));
$('textarea-import-export').value = '';
};
$('button-import-export-cancel').onclick = function (event) {
hideImpotExport();
};
function hideImpotExport () {
hide($('div-import-warp'));
}
$('button-text').onclick = function (event) {
var text = deckToText(getCurrentDeck());
// var doc = window.open().document;
// doc.title = deckName;
// doc.body.innerText = text;
$('textarea-import-export').value = text;
$('textarea-import-export').select();
}
function deckToText (deck) {
var text = '';
var decks = [
deck.lrigDeck,
deck.mainDeck.filter(function (pid) {
var info = CardInfo[pid];
return !(info.burstEffectTexts && info.burstEffectTexts.length);
}),
deck.mainDeck.filter(function (pid) {
var info = CardInfo[pid];
return info.burstEffectTexts && info.burstEffectTexts.length;
})
];
decks.forEach(function (deck,idx,arr) {
var lastName = '';
var count = 0;
var decks = [];
deck.forEach(function (pid,idx,arr) {
var info = CardInfo[pid];
var name = Localize.cardName(info);
if ((name !== lastName) && (idx !== 0)) {
text += count + ' ' + lastName + '\n';
lastName = name;
count = 1;
} else {
lastName = name;
count++;
}
if (idx === arr.length-1) {
text += count + ' ' + lastName + '\n';
}
},this);
if (idx !== arr.length-1) {
text += '——————————\n';
}
});
return text;
}
$('button-export').onclick = function (event) {
var filename = deckName + '.webxoss';
var json = deckToJson(getCurrentDeck());
download(filename,json);
};
$('button-export-code').onclick = function (event) {
var json = deckToJson(getCurrentDeck());
$('textarea-import-export').value = json;
$('textarea-import-export').select();
};
var download = (function () {
var a = newElement('a');
a.target = '_blank';
a.style.position = 'fixed';
a.style.width = '0';
a.style.height = '0';
a.style.overflow = 'hidden';
a.style.top = '0';
a.style.left = '0';
a.style.zIndex = '-1024';
a.style.opacity = '0';
document.body.appendChild(a);
return function download (name,text) {
a.href = 'data:application/octet-stream,' + encodeURI(text);
a.download = name;
a.click();
}
})();
function deckToJson (deck) {
var fileObj = {
format: 'WEBXOSS Deck',
version: '1',
content: deck
};
return JSON.stringify(fileObj);
}
$('input-file').onchange = function (event) {
var file = $('input-file').files[0];
$('input-file').value = null;
if (!file) return;
var name = file.name.replace(/\.webxoss$/,'');
if (inArr(name,deckNames)) {
window.alert(Localize.editor('DECK_NAME_ALREADY_EXISTS',name));
return;
}
parseFile(file,function (deck) {
$('input-file').value = null;
if (!deck) {
window.alert(Localize.editor('FAILED_TO_PARSE_FILE'));
return;
}
createDeck(name,deck);
hideImpotExport();
});
};
$('button-import-code').onclick = function (event) {
var json = $('textarea-import-export').value;
var deck = parseCode(json);
if (!deck) {
window.alert(Localize.editor('FAILED_TO_PARSE_CODE'));
} else {
var name = window.prompt(Localize.editor('DECK_NAME'));
if (!name) return;
if (inArr(name,deckNames)) {
window.alert(Localize.editor('DECK_NAME_ALREADY_EXISTS',name));
return;
}
createDeck(name,deck);
hideImpotExport();
}
};
function parseFile (file,callback) {
if (!FileReader || file.size > 1024) {
callback(null);
return;
}
var reader = new FileReader();
reader.onload = function (event) {
callback(parseCode(reader.result));
};
reader.readAsText(file);
}
function parseCode (json) {
try {
var obj = JSON.parse(json);
var legal =
(obj.format === 'WEBXOSS Deck') &&
(+obj.version === 1) &&
(obj.content.mainDeck.length <= 50) &&
(obj.content.lrigDeck.length <= 20);
if (legal) {
return obj.content;
} else {
return null;
}
} catch (e) {
return null;
}
}
// ====== 导入导出结束 ======
$('link-back-to-webxoss').onclick = function (event) {
if (window.opener && !window.opener.closed) {
event.preventDefault();
window.close();
return false;
}
}
search();
// });

90
DeckEditor/index.html Normal file
View file

@ -0,0 +1,90 @@
<!DOCTYPE html>
<!-- <html> -->
<html manifest="../webxoss.appcache">
<head>
<meta charset="UTF-8">
<title>DeckEditor</title>
<script>
global = window;
</script>
<script src="../CardInfo.js"></script>
<script src="../CardInfo_ru.js"></script>
<link rel="stylesheet" type="text/css" href="editor.css">
</head>
<body>
<section id="Detail">
<div id="detail-card-figure">
<img id="detail-card-image">
</div>
<div id="detail-card-data">
<div id="detail-card-wxid"></div>
<div id="detail-card-name"></div>
<div id="detail-card-limiting"></div>
<table id="detail-table">
<tbody id="detail-table-body"></tbody>
</table>
</div>
</section>
<section id="Deck">
<a id="link-back-to-webxoss" href="../">完成编辑</a>
<div id="div-deck">
<div>
<span id="label-select-deck">选择牌组:</span><select id="select-decks"></select><button id="button-delete-deck">删除</button><button id="button-rename">重命名</button>
</div>
<div id="div-new-deck">
<span id="label-new-deck">新建牌组:</span><input id="input-new-deck-name" type="text" placeholder="牌组名字"><button id="button-new-deck">新建</button><button id="button-copy-deck">复制</button><button id="button-import-export">导入/导出</button>
</div>
</div>
<div id="main-deck-text-bar">
<span id="main-deck-title">主卡组</span><a target="_blank" href="http://www.takaratomy.co.jp/products/wixoss/rule/rule_mayu_room.html" id="main-deck-mayus-room">茧的房间</a><span id="main-deck-burst"><span id="main-deck-burst-title">爆发:</span><span id="main-deck-burst-count"></span><span>/20</span></span>
</div>
<div id="main-deck-zone"></div>
<div id="lrig-deck-text-bar">
<span id="lrig-deck-title">LRIG卡组</span>
</div>
<div id="lrig-deck-zone"></div>
<div id="div-import-warp" style="display: none;">
<div id="div-import-export">
<div>
<span id="label-import-from-file">从文件导入:</span><input id="input-file" type="file">
</div>
<div>
<span id="label-export-to-file">导出到文件:</span><button id="button-export">导出</button>
</div>
<div>
<textarea id="textarea-import-export"></textarea>
</div>
<div id="div-import-footer">
<button id="button-text">显示文本</button><button id="button-export-code">显示代码</button><button id="button-import-code">导入代码</button><button id="button-import-export-cancel">取消</button>
</div>
</div>
</div>
</section>
<section id="Search">
<input id="search-input" placeholder="输入关键字以搜索" spellcheck="false" autocomplete="off" autocapitalize="none">
<div id="div-search-tips">
<a id="link-search-tips" href="./SearchTips.html" target="_blank">搜索技巧</a>
</div>
<div id="div-search-results">
<ol id="search-list"></ol>
<div id="search-show-more" style="display: none;">显示更多</div>
</div>
</section>
<script src="../Localize.min.js"></script>
<script src="../ImageAndDetail.min.js"></script>
<script src="./Deck.min.js"></script>
<script src="./DeckEditor.js"></script>
<!-- <script src="../lib/util.js"></script>
<script src="../Localize.js"></script>
<script src="../ImageFileCache.js"></script>
<script src="../ImageManager.js"></script>
<script src="../Detail.js"></script>
<script src="../DeckManager.js"></script>
<script src="./Rules.js"></script>
<script src="./Searcher.js"></script>
<script src="./editor.js"></script> -->
</body>
</html>

11
DeckEditor/tutorial.html Normal file
View file

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Tutorial</title>
</head>
<body>
</body>
</html>

207
DeckManager.js Normal file
View file

@ -0,0 +1,207 @@
'use strict';
function DeckManager () {
this.getDeckNames();
}
DeckManager.prototype.getDeckNames = function () {
var names = localStorage.getItem('deck_filenames');
this._deckNames = names? JSON.parse(names) : [];
return this._deckNames;
};
DeckManager.prototype._updateDeckNames = function () {
this._deckNames.sort();
localStorage.setItem('deck_filenames',JSON.stringify(this._deckNames));
};
DeckManager.prototype.createDeck = function (name,deck) {
if (inArr(name,this._deckNames)) return false;
this._deckNames.push(name);
this._updateDeckNames();
this.saveDeck(name,deck);
return true;
};
DeckManager.prototype.renameDeck = function (name,newName) {
if (!inArr(name,this._deckNames)) return false;
if (inArr(newName,this._deckNames)) return false;
var deck = this.loadDeck(name);
this.deleteDeck(name);
this.createDeck(newName,deck);
return true;
};
DeckManager.prototype.deleteDeck = function (name) {
if (!removeFromArr(name,this._deckNames)) return false;
this._updateDeckNames();
localStorage.removeItem('deck_file_'+name);
return true;
};
DeckManager.prototype.loadDeck = function (name) {
var deck = localStorage.getItem('deck_file_'+name);
if (!deck) return null;
return JSON.parse(deck);
};
DeckManager.prototype.saveDeck = function (name,deck) {
if (!inArr(name,this._deckNames)) return false;
localStorage.setItem('deck_file_'+name,JSON.stringify(deck));
return true;
};
DeckManager.prototype.checkMainDeck = function (pids) {
if (!isArr(pids)) return false;
if (pids.length !== 40) return false;
var infos = [];
for (var i = 0; i < pids.length; i++) {
var info = CardInfo[pids[i]];
if (!info) {
debugger;
return false
};
info = CardInfo[info.cid];
if (!info) {
debugger;
return false
};
infos.push(info);
}
if (infos.some(function (info) {
if (info.cardType === 'LRIG') return true;
if (info.cardType === 'ARTS') return true;
if (info.cardType === 'RESONA') return true;
})) return false;
if (this.burstCount(pids) !== 20) return false;
if (!this.checkDuplicate(pids)) return false;
return infos;
};
DeckManager.prototype.checkLrigDeck = function (pids) {
if (!isArr(pids)) return false;
if (pids.length > 10) return false;
var infos = [];
for (var i = 0; i < pids.length; i++) {
var info = CardInfo[pids[i]];
if (!info) return false;
info = CardInfo[info.cid];
if (!info) return false;
infos.push(info);
}
if (infos.some(function (info) {
if (info.cardType === 'SIGNI') return true;
if (info.cardType === 'SPELL') return true;
})) return false;
if (!infos.some(function (info) {
return (info.cardType === 'LRIG') && (info.level === 0);
})) return false;
if (!this.checkDuplicate(pids)) return false;
return infos;
};
DeckManager.prototype.checkMayusRoom = function (pids) {
var infos = [];
for (var i = 0; i < pids.length; i++) {
var info = CardInfo[pids[i]];
if (!info) return false;
info = CardInfo[info.cid];
if (!info) return false;
infos.push(info);
}
// 禁止 狐狸+修复 和 狐狸+Three out
if (infos.some(function (info) {
return info.cid === 33; // 狐狸
}) && infos.some(function (info) {
return (info.cid === 34) || (info.cid === 84); // 修复, three out
})) {
return false;
}
// 禁止 V・・C+共鸣・进军 和 V・・C+共鸣
if (infos.some(function (info) {
return info.cid === 1202; // V・・C
}) && infos.some(function (info) {
return (info.cid === 884) || (info.cid === 1369); // 共鸣・进军, 共鸣
})) {
return false;
}
// 禁止 Lock+割裂 和 Lock+精挖
if (infos.some(function (info) {
return info.cid === 534; // Lock
}) && infos.some(function (info) {
return (info.cid === 408) || (info.cid === 570); // 割裂, 精挖
})) {
return false;
}
// 禁止 Ar+魔术手
if (infos.some(function (info) {
return info.cid === 814; // Ar
}) && infos.some(function (info) {
return (info.cid === 1090); // 魔术手
})) {
return false;
}
// 禁止 双麻油
if (infos.some(function (info) {
return info.cid === 649; // 創世の巫女 マユ
}) && infos.some(function (info) {
return (info.cid === 1562); // 真名の巫女 マユ
})) {
return false;
}
// 限制
var limitMap = {
37: 2, // <忘得ぬ幻想 ヴァルキリー>
34: 2, // <修復>
178: 2, // <先駆の大天使 アークゲイン>
534: 1, // <ロック・ユー>
689: 1, // <>
474: 0, // <ノー・ゲイン>
23: 0, // <大器晩成>
};
for (var i = 0; i < infos.length; i++) {
var info = infos[i];
var cid = info.cid;
if (cid in limitMap) {
limitMap[cid]--;
if (limitMap[cid] < 0) return false;
}
}
return true;
};
DeckManager.prototype.checkDeck = function (deck,mayusRoom) {
var valid = this.checkMainDeck(deck.mainDeck) &&
this.checkLrigDeck(deck.lrigDeck);
if (!valid) return false;
if ((mayusRoom === false)) return true;
return this.checkMayusRoom(deck.mainDeck.concat(deck.lrigDeck));
};
DeckManager.prototype.burstCount = function (pids) {
var count = 0;
pids.forEach(function (pid) {
var info = CardInfo[pid];
info = CardInfo[info.cid];
if (info.burstEffectTexts && info.burstEffectTexts.length) {
count++;
}
});
return count;
};
DeckManager.prototype.checkDuplicate = function (pids) {
var bucket = {};
pids.forEach(function (pid) {
var info = CardInfo[pid];
if (info.cid in bucket) {
bucket[info.cid]++;
} else {
bucket[info.cid] = 1;
}
});
for (var cid in bucket) {
if (bucket[cid] > 4) return false;
}
return true;
};

151
Detail.js Normal file
View file

@ -0,0 +1,151 @@
'use strict';
window.CardDetail = (function () {
function $ (id) {
return document.getElementById(id);
}
function newElement (tag) {
var el = document.createElement(tag);
for (var i = 1; i < arguments.length; i++) {
el.classList.add(arguments[i]);
}
return el;
}
function KeyValue (prop,keyHidden) {
this.prop = prop;
this.keyHidden = keyHidden;
this.value = '';
this.eKey = newElement('td','key',prop);
this.eKey.textContent = Localize.propToKey(prop);
this.eValue = newElement('td','value',prop);
}
function CardDetail (imageManager) {
this.imageManager = imageManager;
this.eDetail = $('detail');
this.eImage = $('detail-card-image');
this.eWxid = $('detail-card-wxid');
this.eName = $('detail-card-name');
this.eLimiting = $('detail-card-limiting');
this.eTable = $('detail-table');
this.eTableBody = $('detail-table-body');
this.kvCardType = new KeyValue('cardType');
this.kvColor = new KeyValue('color');
this.kvLevel = new KeyValue('level');
this.kvClasses = new KeyValue('classes');
this.kvLimit = new KeyValue('limit');
this.kvPower = new KeyValue('power');
this.kvLimting = new KeyValue('limting');
this.kvGuard = new KeyValue('guard');
this.kvCost = new KeyValue('cost');
this.kvTimmings = new KeyValue('timmings');
this.kvEffects = new KeyValue('effects',true);
this.kvBurst = new KeyValue('burst',true);
this._pid = 0;
}
CardDetail.prototype.show = function (pid) {
if (!pid || this._pid === pid) return;
this._pid = pid;
this.eTable.setAttribute('lang',Localize.getLanguage());
this.eTableBody.innerHTML = '';
var info = CardInfo[pid];
this.eWxid.textContent = info.wxid;
this.eName.innerHTML = '';
var link = newElement('a');
link.target = '_blank';
link.href = 'http://www.takaratomy.co.jp/products/wixoss/card/card_list.php?card=card_detail&card_no=' + info.wxid;
link.textContent = Localize.cardName(info);
this.eName.appendChild(link);
this.eLimiting.textContent = Localize.limiting(info);
this.eImage.src = this.imageManager.getUrlByPid(pid);
this.kvColor.value = info.color.split('/').map(Localize.color).join('/');
this.kvCardType.value = Localize.cardType(info);
this.kvEffects.value = Localize.effectTexts(info);
if (inArr(info.cardType,['LRIG','SIGNI','RESONA'])) {
this.kvClasses.value = Localize.classes(info);
this.kvLevel.value = info.level;
}
if (inArr(info.cardType,['LRIG','SPELL','ARTS'])) {
this.kvCost.value = Localize.cost(info);
}
if (inArr(info.cardType,['SIGNI','SPELL'])) {
this.kvBurst.value = Localize.burstEffectTexts(info);
}
if (info.cardType === 'LRIG') {
if (info.limit >= 1024) {
this.kvLimit.value = '∞';
} else {
this.kvLimit.value = info.limit;
}
this.addKeyValue(
[this.kvCardType,this.kvColor],
[this.kvLevel,this.kvClasses],
[this.kvLimit,this.kvCost],
[this.kvEffects]
);
} else if (info.cardType === 'SIGNI') {
this.kvPower.value = info.power;
this.kvGuard.value = Localize.guard(info);
this.addKeyValue(
[this.kvCardType,this.kvColor],
[this.kvLevel,this.kvClasses],
[this.kvPower,this.kvGuard],
[this.kvEffects],
[this.kvBurst]
);
} else if (info.cardType === 'RESONA') {
this.kvPower.value = info.power;
this.kvGuard.value = Localize.guard(info);
this.addKeyValue(
[this.kvCardType,this.kvColor],
[this.kvLevel,this.kvClasses],
[this.kvPower,this.kvGuard],
[this.kvEffects]
);
} else if (info.cardType === 'SPELL') {
this.addKeyValue(
[this.kvCardType,this.kvColor],
[this.kvCost],
[this.kvEffects],
[this.kvBurst]
);
} else if (info.cardType === 'ARTS') {
this.kvTimmings.value = Localize.timmings(info);
this.addKeyValue(
[this.kvCardType,this.kvColor],
[this.kvCost],
[this.kvTimmings],
[this.kvEffects]
);
}
};
CardDetail.prototype.addKeyValue = function () {
for (var i = 0; i < arguments.length; i++) {
var rowArray = arguments[i];
var eRow = newElement('tr');
rowArray.forEach(function (kv) {
kv.eValue.textContent = kv.value;
kv.eValue.removeAttribute('colspan');
if (!kv.keyHidden) eRow.appendChild(kv.eKey);
eRow.appendChild(kv.eValue);
},this);
if (rowArray.length === 1) {
var kv = rowArray[0];
var colspan = kv.keyHidden? 4 : 3;
kv.eValue.setAttribute('colspan',colspan);
}
this.eTableBody.appendChild(eRow);
}
};
return CardDetail;
})();

453
Dialog.js Normal file
View file

@ -0,0 +1,453 @@
'use strict';
function Dialog (game) {
this.game = game;
this.searcher = new Searcher();
this.warpDiv = this.newElement('div');
this.dialogDiv = this.newElement('div');
this.titleDiv = this.newElement('div');
this.closeIcon = this.newElement('div');
this.bodyDiv = this.newElement('div');
this.footerDiv = this.newElement('div');
this.footSpan = this.newElement('span');
this.okBtn = this.newElement('button');
this.cancelBtn = this.newElement('button');
this.okBtn.textContent = Localize('common','OK');
this.cancelBtn.textContent = Localize('common','CANCEL');
this.closeIcon.onclick = this.close.bind(this);
this.warpDiv.classList.add('warp');
this.dialogDiv.classList.add('dialog');
this.titleDiv.classList.add('title');
this.closeIcon.classList.add('closeIcon');
this.bodyDiv.classList.add('body');
this.footerDiv.classList.add('footer');
this.okBtn.classList.add('okBtn');
this.cancelBtn.classList.add('cancelBtn');
this.warpDiv.appendChild(this.dialogDiv);
this.dialogDiv.appendChild(this.closeIcon);
this.dialogDiv.appendChild(this.titleDiv);
this.dialogDiv.appendChild(this.bodyDiv);
this.dialogDiv.appendChild(this.footerDiv);
this.footerDiv.appendChild(this.footSpan);
this.footerDiv.appendChild(this.okBtn);
this.footerDiv.appendChild(this.cancelBtn);
this.warpDiv.style.display = 'none';
game.stage.canvas.parentElement.appendChild(this.warpDiv);
}
Dialog.prototype.newElement = function (tag) {
return document.createElement(tag);
};
Dialog.prototype.newCardImg = function (pid) {
var img = this.newElement('img');
img.src = this.game.imageManager.getUrlByPid(pid);
return img;
};
Dialog.prototype.mouseover = function (el,pid) {
el.onmouseover = this.game.cardDetail.show.bind(this.game.cardDetail,pid);
};
Dialog.prototype.center = function (element) {
var parent = element.parentElement;
element.style.top = '0';
element.style.left = '0';
element.style.top = (parent.offsetHeight - element.offsetHeight)/2 + 'px';
element.style.left = (parent.offsetWidth - element.offsetWidth)/2 + 'px';
};
Dialog.prototype.pop = function (title,body,foot,canClose,callback,callbackCancel) {
var dialog = this;
// 构筑DOM
this.titleDiv.textContent = title;
this.footSpan.innerHTML = '';
if (foot) this.footSpan.appendChild(foot);
this.bodyDiv.innerHTML = '';
this.bodyDiv.style.width = '';
this.bodyDiv.appendChild(body);
this.closeIcon.style.display = canClose? '' : 'none';
if (!callback) {
this.okBtn.onclick = null;
this.footerDiv.style.display = 'none';
} else {
this.okBtn.onclick = function (event) {
dialog.close();
callback();
};
// this.okBtn.disabled = false;
this.footerDiv.style.display = '';
}
if (!callbackCancel) {
this.cancelBtn.onclick = null;
this.cancelBtn.style.display = 'none';
} else {
this.cancelBtn.onclick = function (event) {
dialog.close();
callbackCancel();
};
// this.cancelBtn.disabled = false;
this.cancelBtn.style.display = '';
}
// 渲染,但完全透明,用于计算元素尺寸和位置.
this.warpDiv.style.opacity = '0';
this.warpDiv.style.display = '';
// 加上滚动条宽度
// this.bodyDiv.style.width = 2*this.bodyDiv.offsetWidth - this.bodyDiv.clientWidth + 'px';
// 居中
this.center(this.dialogDiv);
// 显示
this.warpDiv.style.opacity = '1';
};
Dialog.prototype.close = function () {
this.okBtn.disabled = false;
this.warpDiv.style.display = 'none';
};
Dialog.prototype.showText = function (title,text,callback) {
var body = document.createTextNode(text);
this.pop(title,body,null,false,callback);
};
Dialog.prototype.confirm = function (title,text,callback) {
var body = document.createTextNode(text);
this.pop(title,body,null,false,callback.bind(null,true),callback.bind(null,false));
};
// 从多个文本选项选择一个.
Dialog.prototype.selectText = function (title,options,canClose,callback) {
var dialog = this;
var body = this.newElement('div');
options.forEach(function (option,idx) {
var opt = this.newElement('div');
opt.classList.add('option');
opt.textContent = option;
body.appendChild(opt);
opt.onclick = function (event) {
dialog.close();
callback(idx);
}
},this);
this.pop(title,body,null,canClose,null);
};
Dialog.prototype.selectNumber = function (title,min,max,defaultValue,canClose,callback) {
var dialog = this;
var body = this.newElement('div');
var sel = this.newElement('select');
for (var i = min; i <= max; i++) {
var opt = this.newElement('option');
opt.value = i;
opt.textContent = i;
sel.appendChild(opt);
}
sel.value = defaultValue;
body.appendChild(sel);
this.pop(title,body,null,canClose,function () {
callback(sel.value);
});
};
Dialog.prototype.showCards = function (title,cards,callback) {
var body = this.newElement('div');
if (!cards.length) {
body.textContent = Localize('common','NO_CARDS');
} else {
cards.forEach(function (card) {
var crd = this.newElement('div');
var img = this.newCardImg(card.pid);
crd.appendChild(img);
crd.classList.add('card');
this.mouseover(crd,card.pid);
body.appendChild(crd);
},this);
}
this.pop(title,body,null,false,callback);
};
Dialog.prototype.selectCardId = function (title,callback) {
var dialog = this;
var searcher = this.searcher;
var body = this.newElement('div');
body.style.textAlign = 'center';
var searchBar = this.newElement('input');
searchBar.setAttribute('placeholder','Search...');
searchBar.type = 'text';
searchBar.style.textAlign = 'center';
searchBar.style.width = '90%';
var container = this.newElement('div');
container.style.maxHeight = '450px';
container.style['overflow-y'] = 'auto';
container.style.textAlign = 'left';
var elShowMore = this.newElement('div');
elShowMore.textContent = 'Show More...';
elShowMore.style.color = 'blue';
elShowMore.style.cursor = 'pointer';
elShowMore.style.margin = '1em 0';
body.appendChild(searchBar);
body.appendChild(container);
body.appendChild(elShowMore);
var results = [];
var RESULTS_LENGTH = 9;
var _shown = 0;
elShowMore.onclick = showMore;
searchBar.onchange = searchBar.onkeyup = search;
function search () {
var q = searchBar.value;
results = searcher.search(q);
showResults();
};
search();
function showResults () {
_shown = 0;
container.innerHTML = '';
showMore();
}
function showMore () {
for (var i = 0; i < RESULTS_LENGTH; i++) {
var idx = _shown;
if (idx >= results.length) break;
var pid = results[idx].pid;
var opt = dialog.newElement('div');
var img = dialog.newCardImg(pid);
opt.appendChild(img);
opt.classList.add('card');
dialog.mouseover(opt,pid);
opt.onclick = function (pid) {
dialog.close();
callback(pid);
}.bind(null,pid);
container.appendChild(opt);
_shown++;
}
// if (_shown < results.length) {
// show(elShowMore);
// } else {
// hide(elShowMore);
// }
}
this.pop(title,body,null,false,null);
// 固定宽度
body.style.minWidth = body.offsetWidth + 'px';
searchBar.focus();
};
// 选择一张.
Dialog.prototype.selectCard = function (title,cards,canClose,callback) {
var dialog = this;
var body = this.newElement('div');
cards.forEach(function (card,idx) {
var opt = this.newElement('div');
var img = this.newCardImg(card.pid);
opt.appendChild(img);
opt.classList.add('card');
this.mouseover(opt,card.pid);
opt.onclick = function (event) {
dialog.close();
callback(idx);
}
body.appendChild(opt);
},this);
this.pop(title,body,null,canClose,null);
};
Dialog.prototype.selectCardAdvanced = function (title,cards,texts,canClose,careOrder,onchanged,callback,callbackCancel) {
if (!texts) texts = [];
var dialog = this;
var selectedIndexes = [];
var datas = [];
var body = this.newElement('div');
cards.forEach(function (card,idx) {
var opt = this.newElement('div');
var img = this.newCardImg(card.pid);
var txt = this.newElement('div');
txt.textContent = texts[idx] || '';
var msk = this.newElement('div');
var num = this.newElement('div');
opt.appendChild(img);
opt.appendChild(txt);
opt.appendChild(msk);
opt.appendChild(num);
opt.classList.add('card');
txt.classList.add('txt');
msk.classList.add('msk');
num.classList.add('num');
this.mouseover(opt,card.pid);
var data = {
opt: opt,
txt: txt,
msk: msk,
num: num,
};
datas[idx] = data;
opt.onclick = function (event) {
if (!opt.classList.contains('selected') && opt.classList.contains('disabled')) return;
if (opt.classList.contains('selected')) {
removeFromArr(idx,selectedIndexes);
} else {
selectedIndexes.push(idx);
}
opt.classList.toggle('selected');
handleSelectChanged();
};
body.appendChild(opt);
},this);
handleSelectChanged();
dialog.pop(title,body,null,canClose,callback.bind(null,selectedIndexes),callbackCancel);
datas.forEach(function (data) {
this.center(data.txt);
},this);
function handleSelectChanged () {
datas.forEach(function (data,idx) {
data.opt.classList.remove('disabled');
},this);
var ok = onchanged(selectedIndexes,disable);
dialog.okBtn.disabled = !ok;
if (careOrder) {
datas.forEach(function (data,idx) {
var order = selectedIndexes.indexOf(idx) + 1;
data.num.textContent = order || '';
dialog.center(data.num);
},this);
}
}
function disable (idx) {
datas[idx].opt.classList.add('disabled');
}
};
// 选择若干张.
Dialog.prototype.selectSomeCards = function (title,cards,arg,callback) {
var min = arg.min || 0;
// var max = arg.max || cards.length;
var max = arg.max;
if (!max) {
max = arg.targets? arg.targets.length : cards.length;
}
var texts = arg.texts;
var canClose = arg.canClose;
var careOrder = arg.careOrder;
var targets = arg.targets;
var callbackCancel;
if (min) {
callbackCancel = null;
} else {
callbackCancel = callback.bind(null,[]);
}
this.selectCardAdvanced(title,cards,texts,canClose,careOrder,function (selectedIndexes,disable) {
if (targets && targets.length) {
cards.forEach(function (card,idx) {
if (!inArr(card,targets)) disable(idx);
},this);
}
var len = selectedIndexes.length;
if (len >= max) {
cards.forEach(function (card,idx) {
disable(idx);
},this);
return true;
}
return len && (len >= min);
},function (selectedIndexes) {
if (targets && targets.length && (targets.length !== cards.length)) {
selectedIndexes = selectedIndexes.map(function (idx) {
return targets.indexOf(cards[idx]);
});
}
callback(selectedIndexes);
},callbackCancel);
};
Dialog.prototype.selectEner = function (title,cards,colors,cost,callback,onCancel) {
this.selectCardAdvanced(title,cards,null,false,false,onSelectChange,callback,onCancel);
function onSelectChange (selectedIndexes,disable) {
var need = {};
var total = 0;
['colorless','white','black','red','blue','green','multi'].forEach(function (color) {
need[color] = cost[color] || 0;
total += need[color];
},this);
if (selectedIndexes.length >= total) {
cards.forEach(function (card,idx) {
disable(idx);
},this);
return true;
}
var colorSets = [];
selectedIndexes.forEach(function (idx) {
var color = colors[idx];
if (color === 'multi') return;
if (isArr(color)) {
// 多重颜色,延迟考虑
colorSets.push(color);
return
}
if (need[color] > 0) {
need[color] -= 1;
} else {
need.colorless -= 1;
}
},this);
// 多重颜色
colorSets.forEach(function (colorSet) {
for (var i = 0; i < colorSet.length; i++) {
var color = colorSet[i];
if (need[color] > 0) {
need[color] -= 1;
return;
}
}
need.colorless -= 1;
});
if (need.colorless > 0) return false;
cards.forEach(function (card,idx) {
var color = colors[idx];
if (color === 'multi') return;
if (isArr(color)) {
if (color.some(function (color) {
return need[color] > 0;
})) return;
return disable(idx);
}
if (!need[color]) disable(idx);
},this);
}
};

88
FakeSocket.js Normal file
View file

@ -0,0 +1,88 @@
'use strict';
window.FakeSocket = (function () {
var sockets = [];
window.addEventListener('message',function (event) {
var win = event.source;
var name = event.data.name;
var data = event.data.data;
if ((name !== 'tick') && (name !== 'tock')) console.log(JSON.stringify(event.data));
for (var i = 0; i < sockets.length; i++) {
var socket = sockets[i];
if (socket._win === win) {
socket._doEmit(name,data);
return;
}
}
});
function FakeSocket (win) {
this._win = win;
this._listeners = {};
this.id = '<' + sockets.push(this) + '>';
this.io = {
reconnection: function () {},
opts: {
query: ''
}
}
}
FakeSocket.prototype.on = function (name,handler) {
if (!this._listeners[name]) this._listeners[name] = [];
this._listeners[name].push(handler);
};
FakeSocket.prototype.emit = function (name,data) {
this._win.postMessage({
name: name,
data: data
},'*');
};
FakeSocket.prototype._doEmit = function (name,data) {
var listeners = this._listeners[name];
if (listeners) {
listeners.forEach(function (listener) {
listener(data);
});
}
};
FakeSocket.prototype.disconnect = function () {
this.emit('disconnect');
};
FakeSocket.prototype.removeAllListeners = function (name) {
var listeners = this._listeners[name];
if (listeners) {
listeners.length = 0;
}
};
return FakeSocket;
})();

643
Game.js Normal file
View file

@ -0,0 +1,643 @@
'use strict';
function Game (io,audio,ongameover,spectating) {
this.io = io;
this.audio = audio;
this.ongameover = ongameover;
this.io.listener = function (data) {
console.log(data);
this.addMsgs(data);
this.update();
}.bind(this);
this.onidle = null;
// this._waiting = false;
// 老是清不干净,干脆粗暴地删掉元素.
this.gameDiv = document.getElementById('GameDiv');
this.canvasContainer = document.getElementById('BattleField');
this.canvasContainer.innerHTML = '';
this.canvas = document.createElement('canvas');
this.canvas.width = 576;
this.canvas.height = 734;
this.canvasContainer.appendChild(this.canvas);
this.stage = new createjs.Stage(this.canvas);
this.stage.enableDOMEvents(true);
this.stage.enableMouseOver(10);
this.dialog = new Dialog(this);
this.selector = new Selector(this,spectating);
this.objList = [];
this.background = new GameBackground(this);
this.stage.addChild(this.background);
this.cards = [];
this.cardLayer = new createjs.Container();
this.stage.addChild(this.cardLayer);
this.zones = [];
this.zoneLayer = new createjs.Container();
this.stage.addChild(this.zoneLayer);
this.msgQueue = [];
this._packageCount = 0;
// this._done = false;
this.imageManager = new ImageManager();
this.cardDetail = new CardDetail(this.imageManager);
this.initZones();
this.skip = false; // 跳过动画
}
// Game.prototype.destroy = function () {
// this.gameDiv.classList.remove('colored');
// ['white','black','red','blue','green'].forEach(function (color) {
// this.gameDiv.classList.remove('self-'+color);
// this.gameDiv.classList.remove('opponent-'+color);
// },this);
// this.stage.autoClear = true;
// this.stage.removeAllChildren();
// this.stage.update();
// this.stage.enableDOMEvents(false);
// this.stage.removeAllChildren();
// this.stage.removeAllEventListeners();
// };
// =========================
// Game.prototype.initZones
// 定义在ZonePosition.js
// =========================
Game.prototype.setSid = function (obj,sid) {
obj.sid = sid;
this.objList[sid] = obj;
};
Game.prototype.getObjBySid = function (sid) {
return this.objList[sid];
};
Game.prototype.addCard = function (card) {
this.cards.push(card);
this.cardLayer.addChild(card);
};
Game.prototype.addZone = function (zone) {
this.zones.push(zone);
this.zoneLayer.addChild(zone);
};
Game.prototype.handleInit = function (msg) {
this.setSid(this.player,msg.player);
this.setSid(this.opponent,msg.opponent);
function setZones (player,zones) {
this.setSid(player.mainDeck,zones.mainDeck);
this.setSid(player.lrigDeck,zones.lrigDeck);
this.setSid(player.handZone,zones.handZone);
this.setSid(player.lrigZone,zones.lrigZone);
this.setSid(player.signiZones[0],zones.signiZones[0]);
this.setSid(player.signiZones[1],zones.signiZones[1]);
this.setSid(player.signiZones[2],zones.signiZones[2]);
this.setSid(player.enerZone,zones.enerZone);
this.setSid(player.checkZone,zones.checkZone);
this.setSid(player.trashZone,zones.trashZone);
this.setSid(player.lrigTrashZone,zones.lrigTrashZone);
this.setSid(player.lifeClothZone,zones.lifeClothZone);
this.setSid(player.excludedZone,zones.excludedZone);
zones.mainDeckCards.forEach(function (sid) {
var card = new Card(this,false);
this.setSid(card,sid);
card.move(0,player.mainDeck,true,false,false);
},this);
zones.lrigDeckCards.forEach(function (sid,i) {
var card = new Card(this,true);
this.setSid(card,sid);
card.move(zones.lrigDeckCardIds[i],player.lrigDeck,true,false,false);
},this);
}
setZones.call(this,this.player,msg.playerZones);
setZones.call(this,this.opponent,msg.opponentZones);
return true;
};
Game.prototype.handleSetColor = function (msg) {
// this.gameDiv.classList.add('colored');
document.body.setAttribute('self',msg.selfColor);
document.body.setAttribute('opponent',msg.opponentColor);
var zoneTextColorMap = {
'white': 'black',
'black': 'yellow',
'red': 'white',
'blue': 'yellow',
'green': 'white'
};
this.zones.forEach(function (zone) {
var color = zone.opposite? zoneTextColorMap[msg.opponentColor] : zoneTextColorMap[msg.selfColor];
zone.setTextColor(color);
},this);
var audio = this.audio;
audio.bgmFadeOut(function () {
audio.playBgm(msg.selfColor);
});
return true;
};
Game.prototype.handlePackedMsgStart = function () {
// this.audio.playBgm();
this._packageCount++;
return true;
};
Game.prototype.handlePackedMsgEnd = function () {
this._packageCount--;
return true;
};
Game.prototype.handleMoveCard = function (msg) {
var card = this.getObjBySid(msg.card);
var zone = this.getObjBySid(msg.zone);
card.move(msg.pid,zone,msg.up,msg.faceup,msg.bottom);
return false;
};
Game.prototype.handleUpCard = function (msg) {
var card = this.getObjBySid(msg.card);
card.up();
return false;
};
Game.prototype.handleDownCard = function (msg) {
var card = this.getObjBySid(msg.card);
card.down();
return false;
};
Game.prototype.handleFaceupCard = function (msg) {
var card = this.getObjBySid(msg.card);
card.pid = msg.pid;
card.faceup();
return false;
};
Game.prototype.handleFacedownCard = function (msg) {
var card = this.getObjBySid(msg.card);
card.facedown();
return false;
};
Game.prototype.handleShuffle = function (msg) {
msg.cards.forEach(function (sid) {
var card = this.getObjBySid(sid);
card.pid = 0;
},this);
return true;
};
Game.prototype.handleSelect = function (msg) {
console.log(msg);
if (this.skip) return true;
this.selector.addMsg(msg);
return true;
};
Game.prototype.handlePayEner = function (msg) {
console.log('handlePayEner',msg);
if (this.skip) return true;
var cards = msg.cards.map(function (sid) {
return this.getObjBySid(sid);
},this);
var game = this;
var onCancel = null;
if (msg.cancelable) {
onCancel = function () {
game.input('PAY_ENER',null);
}
}
this.dialog.selectEner(Localize('buttonTitle','PAY_ENER'),cards,msg.colors,msg,function (selectedIndexes) {
game.input('PAY_ENER',selectedIndexes);
},onCancel);
return true;
};
Game.prototype.handleShowCards = function (msg) {
if (this.skip) return true;
var cards = msg.cards.map(function (sid,idx) {
var card = this.getObjBySid(sid);
card.pid = msg.pids[idx];
return card;
},this);
var game = this;
var label = msg.label || 'CONFIRM';
this.dialog.showCards(Localize('buttonTitle',label),cards,function () {
game.input('OK');
});
return true;
};
Game.prototype.handleShowCardsById = function (msg) {
if (this.skip) return true;
var cards = msg.ids.map(function (pid,idx) {
return {pid: pid};
},this);
var game = this;
var label = msg.label || 'CONFIRM';
this.dialog.showCards(Localize('buttonTitle',label),cards,function () {
game.input('OK');
});
return true;
};
Game.prototype.handleShowColors = function (msg) {
if (this.skip) return true;
var colors = msg.colors.map(function (color) {
return Localize.color(color);
});
var game = this;
this.dialog.showText(Localize('buttonTitle','COLOR'),colors.join(','),function () {
game.input('OK');
});
return true;
};
Game.prototype.handleShowTypes = function (msg) {
if (this.skip) return true;
var types = msg.types.map(function (type) {
return Localize('cardType',type);
});
var game = this;
this.dialog.showText(Localize('prop','cardType'),types.join(','),function () {
game.input('OK');
});
return true;
};
Game.prototype.handleShowEffects = function (msg) {
if (this.skip) return true;
var text = msg.effects.map(function (desc) {
return Localize.desc(desc);
}).join('\n');
var game = this;
this.dialog.showText(Localize('gameText','SHOW_EFFECTS_DIALOG_TITLE'),text,function () {
game.input('OK');
});
return true;
};
Game.prototype.handleShowText = function (msg) {
if (this.skip) return true;
var text = '';
if (msg.type === 'number') {
text = msg.content;
} else {
debugger;
}
var game = this;
this.dialog.showText(Localize.labelToDialogTitle(msg.title),text,function () {
game.input('OK');
});
return true;
};
Game.prototype.handleInformCards = function (msg) {
msg.cards.forEach(function (sid,idx) {
var card = this.getObjBySid(sid);
card.pid = msg.pids[idx];
},this);
return true;
};
// Game.prototype.handlePower = function (msg) {
// msg.cards.forEach(function (sid,idx) {
// var card = this.getObjBySid(sid);
// card.zone.power = msg.powers[idx];
// },this);
// return true;
// };
Game.prototype.handleCardStates = function (msg) {
var signiInfos = msg.signiInfos;
var lrigInfos = msg.lrigInfos;
var zoneInfos = msg.zoneInfos;
signiInfos.forEach(function (signiInfo) {
var card = this.getObjBySid(signiInfo.card);
card.zone.power = signiInfo.power;
card.removeStates();
card.addStates(signiInfo.states);
},this);
lrigInfos.forEach(function (lrigInfo) {
var card = this.getObjBySid(lrigInfo.card);
card.removeStates();
card.addStates(lrigInfo.states);
},this);
if (zoneInfos) { // 向前兼容
zoneInfos.forEach(function (zoneInfo) {
var zone = this.getObjBySid(zoneInfo.zone);
zone.removeStates();
zone.addStates(zoneInfo.states);
},this);
}
return true;
};
Game.prototype.handleActivate = function (msg) {
var card = this.getObjBySid(msg.card);
card.shine();
return false;
};
Game.prototype.handleCardSelected = function (msg) {
var card = this.getObjBySid(msg.card);
card.flash();
return false;
};
Game.prototype.handleSelectNumber = function (msg) {
if (this.skip) return true;
var label = msg.label;
var min = msg.min;
var max = msg.max;
var defaultValue = msg.defaultValue || 0;
var title = Localize.labelToDialogTitle(label);
// var options = [];
// var n = min;
// while (n <= max) {
// options.push(n++);
// }
// var game = this;
// this.dialog.selectText(title,options,false,function (idx) {
// game.input(label,min+idx);
// });
var game = this;
this.dialog.selectNumber(title,min,max,defaultValue,false,function (num) {
game.input(label,num);
});
return true;
};
Game.prototype.handleSelectText = function (msg) {
if (this.skip) return true;
var label = msg.label;
var title = Localize.labelToDialogTitle(label);
var type = msg.type || 'gameText';
var options = msg.texts.map(function (text) {
return Localize(type,text);
},this);
var game = this;
this.dialog.selectText(title,options,false,function (idx) {
game.input(label,idx);
});
return true;
};
Game.prototype.handleSelectCardId = function (msg) {
if (this.skip) return true;
try {
var game = this;
var label = msg.label;
var title = Localize.labelToDialogTitle(label);
this.dialog.selectCardId(title,function (pid) {
game.input(label,pid);
});
} catch (e) {
window.alert(e.name + e.message);
}
// console.log('game.input(' + label + ',pid)');
// debugger;
return true;
};
Game.prototype.handleConfirm = function (msg) {
if (this.skip) return true;
var title = Localize.labelToDialogTitle('CONFIRM');
var text = Localize('gameText',msg.text);
var game = this;
this.dialog.confirm(title,text,function (answer) {
game.input('OK',answer);
});
return true;
};
// Game.prototype.handleWaitForOpponent = function (msg) {
// if (this._waiting) {
// this.buttonZone.setText(Localize.waitingMsg());
// } else {
// this._waiting = true;
// this.buttonZone.setText(Localize.waitingMsg(msg.operation));
// }
// this.background.setWaiting(true);
// return true;
// };
// Game.prototype.handleRockPaperScissors = function (msg) {
// var title = Localize('ROCK_PAPER_SCISSORS');
// var options = [
// Localize('ROCK'),
// Localize('PAPER'),
// Localize('SCISSORS')
// ];
// var game = this;
// this.dialog.selectText(title,options,false,function (idx) {
// game.input('ROCK_PAPER_SCISSORS',idx);
// });
// return true;
// };
Game.prototype.handleWin = function (msg) {
this.win();
};
Game.prototype.win = function (surrender) {
// var gameCount = sessionStorage.getItem('game count') || 0;
// gameCount++;
// sessionStorage.setItem('game count',gameCount);
// this.audio.playBgm();
// this.dialog.showText('WIN',text,function () {
// this.supportWebxoss(this.ongameover);
// }.bind(this));
this.ongameover(true,surrender,this.getMessagePacks());
};
// Game.prototype.supportWebxoss = function (callback) {
// if (this.shouldPopSupport()) {
// var title = 'SUPPORT WEBXOSS';
// var body = this.dialog.newElement('div');
// var link = this.dialog.newElement('a');
// link.target = '_blank';
// link.style.color = 'red';
// link.style.textDecoration = 'underline';
// link.href = Localize('index','SUPPORT_URL');
// link.textContent = 'help';
// link.onclick = function () {
// localStorage.setItem('support','webxoss~');
// return true;
// }
// body.appendChild(document.createTextNode('WEBXOSS needs your '));
// body.appendChild(link);
// body.appendChild(document.createTextNode('!'));
// body.style.color = 'green';
// this.dialog.pop(title,body,false,callback);
// } else {
// callback();
// }
// };
// Game.prototype.shouldPopSupport = function () {
// if (localStorage.getItem('support') === 'webxoss~') return false;
// var gameCount = sessionStorage.getItem('game count') || 0;
// if (gameCount >= 4) return true;
// return false;
// };
Game.prototype.handleLose = function (msg) {
this.lose();
};
Game.prototype.lose = function (surrender) {
// var gameCount = sessionStorage.getItem('game count') || 0;
// gameCount++;
// sessionStorage.setItem('game count',gameCount);
// this.audio.playBgm();
// this.dialog.showText('LOSE',text,this.ongameover);
this.ongameover(false,surrender,this.getMessagePacks());
};
Game.prototype.getMessagePacks = function () {
// var datas = this.io.getDatas();
// return {
// format: 'WEBXOSS Replay',
// version: '1',
// content: {
// clietVersion: this.version,
// messagePacks: this.io.getDatas();
// }
// };
return this.io.getDatas();
};
Game.prototype.handleMsg = function (msg) {
if (!msg) return false;
var handlerMap = {
'INIT': this.handleInit,
'SET_COLOR': this.handleSetColor,
'PACKED_MSG_START': this.handlePackedMsgStart,
'PACKED_MSG_END': this.handlePackedMsgEnd,
'MOVE_CARD': this.handleMoveCard,
'UP_CARD': this.handleUpCard,
'DOWN_CARD': this.handleDownCard,
'FACEUP_CARD': this.handleFaceupCard,
'FACEDOWN_CARD': this.handleFacedownCard,
'SHUFFLE': this.handleShuffle,
'SELECT': this.handleSelect,
'PAY_ENER': this.handlePayEner,
'SHOW_CARDS': this.handleShowCards,
'SHOW_CARDS_BY_ID': this.handleShowCardsById,
'SHOW_COLORS': this.handleShowColors,
'SHOW_TYPES': this.handleShowTypes,
'SHOW_EFFECTS': this.handleShowEffects,
'SHOW_TEXT': this.handleShowText,
'INFORM_CARDS': this.handleInformCards,
// 'POWER': this.handlePower,
'CARD_STATES': this.handleCardStates,
'ACTIVATE': this.handleActivate,
'CARD_SELECTED': this.handleCardSelected,
'SELECT_NUMBER': this.handleSelectNumber,
'SELECT_TEXT': this.handleSelectText,
'SELECT_CARD_ID': this.handleSelectCardId,
'CONFIRM': this.handleConfirm,
// 'ROCK_PAPER_SCISSORS': this.handleRockPaperScissors,
// 'WAIT_FOR_OPPONENT': this.handleWaitForOpponent,
'WIN': this.handleWin,
'LOSE': this.handleLose
};
var handler = handlerMap[msg.type];
if (handler) {
return handler.call(this,msg.content) || this.skip;
}
console.warn(msg);
window.alert('Unknown message type: "' + msg.type + '" .');
return true;
};
Game.prototype.handleMsgQueue = function () {
if (!this.msgQueue.length) return;
// this._waiting = false;
this.buttonZone.setText('');
this.background.setWaiting(false);
var done = true;
var msg;
while (msg = this.msgQueue.shift()) {
if (!this.handleMsg(msg)) done = false;
if (!done && !this._packageCount) break;
}
if (!this.msgQueue.length) {
this.handleIdle();
}
};
Game.prototype.handleIdle = function () {
this.selector.removeButtons();
this.selector.showButtons();
this.selector.autoPop();
if (!this.selector.msgs.length) {
this.buttonZone.setText(Localize.waitingMsg());
this.background.setWaiting(true);
}
if (this.onidle) this.onidle();
};
Game.prototype.addMsgs = function (msgs) {
this.selector.clear();
this.msgQueue = this.msgQueue.concat(msgs);
if (!msgs.length) {
this.handleIdle();
}
};
Game.prototype.input = function (label,data) {
if (data === undefined) {
data = [];
}
this.removeButtons();
this.io.send({
label: label,
input: data
});
this.background.setWaiting(true);
};
Game.prototype.removeButtons = function () {
this.cards.forEach(function (card) {
card.removeButtons();
},this);
this.zones.forEach(function (zone) {
zone.removeButtons();
},this);
// this.dialog.close();
};
Game.prototype.update = function () {
var changed = false;
concat(this.cards,this.zones,this.background).forEach(function (obj) {
if (obj.update()) changed = true;
},this);
if (changed) {
this.cardLayer.sortChildren(function (a,b) {
return a.zIndex - b.zIndex;
});
this.stage.update();
} else {
this.handleMsgQueue();
}
};

91
GameAudio.js Normal file
View file

@ -0,0 +1,91 @@
'use strict';
function GameAudio () {
this.bgm = document.getElementById('audio-bgm');
this.soundEffect = document.getElementById('audio-sound-effect');
this.bgmCheckBox = document.getElementById('checkbox-bgm');
this.seCheckBox = document.getElementById('checkbox-sound-effect');
this.bgmDisabled = false;
this.seDisabled = false;
this.lastPlay = '';
this.map = {
'white': 'WhiteAng',
'black': 'DarkFßen',
'red': 'reLEIdEN',
'blue': 'Süblueß',
'green': 'GreenWi',
'NevWorry': 'NevWorry',
'main': 'Love Your Enemies.W',
'Battle': 'バトル!'
}
this.loadSettings();
}
GameAudio.prototype.loadSettings = function () {
if (localStorage.getItem('bgm') === 'disabled') {
this.bgmDisabled = true;
}
if (localStorage.getItem('sound effect') === 'disabled') {
this.seDisabled = true;
}
this.bgmCheckBox.checked = !this.bgmDisabled;
this.seCheckBox.checked = !this.seDisabled;
};
GameAudio.prototype.disableBgm = function (flag) {
if (flag) {
this.bgmDisabled = true;
localStorage.setItem('bgm','disabled');
this.bgm.pause();
this.bgm.src = '';
} else {
this.bgmDisabled = false;
localStorage.setItem('bgm','enabled');
this.bgm.src = this.lastPlay;
this.bgm.play();
}
};
GameAudio.prototype.disableSoundEffect = function (flag) {
if (flag) {
this.seDisabled = true;
localStorage.setItem('sound effect','disabled');
} else {
this.bgmDisabled = false;
localStorage.setItem('sound effect','enabled');
}
};
GameAudio.prototype.bgmFadeOut = function (callback) {
var bgm = this.bgm;
var timer = setInterval(function () {
if (bgm.volume <= 0.2) {
bgm.volume = 0;
clearInterval(timer);
callback();
return;
}
bgm.volume -= 0.2;
},200);
};
GameAudio.prototype.playBgm = function (code) {
if (!code) {
this.bgm.src = '';
this.lastPlay = '';
return;
}
var src = './background/' + this.map[code] + '.mp3';
if (this.lastPlay === src) return;
this.lastPlay = src;
if (this.bgmDisabled) return;
this.bgm.src = src;
this.bgm.volume = 1;
this.bgm.play();
};
GameAudio.prototype.playSoundEffect = function (name) {
if (this.seDisabled) return;
this.soundEffect.src = './background/' + name + '.mp3';
this.soundEffect.play();
};

68
GameBackground.js Normal file
View file

@ -0,0 +1,68 @@
'use strict';
function GameBackground (game) {
createjs.Container.prototype.initialize.call(this);
this.game = game;
this.width = game.stage.canvas.width;
this.height = game.stage.canvas.height;
this.changed = true;
// this.imageWhite = this.newImage('./background/white.png');
// this.imageBlack = this.newImage('./background/black.png');
// this.imageGreen = this.newImage('./background/green.png');
// this.imageBlue = this.newImage('./background/blue.png');
// this.imageRed = this.newImage('./background/red.png');
// this.selfBackground = new createjs.Bitmap('');
// this.opponentBackground = new createjs.Bitmap('');
// this.selfBackground.x = 0;
// this.selfBackground.y = 367;
// this.opponentBackground.regX = 576;
// this.opponentBackground.regY = 368;
// this.opponentBackground.rotation = 180;
this.mask = new createjs.Shape();
// this.addChild(this.selfBackground,this.opponentBackground,this.mask);
this.addChild(this.mask);
}
GameBackground.prototype = new createjs.Container();
GameBackground.prototype.constructor = GameBackground;
// GameBackground.prototype.newImage = function (src) {
// var img = new Image();
// img.src = src;
// return img;
// };
// GameBackground.prototype.setColor = function (selfColor,opponentColor) {
// var imageMap = {
// 'white': this.imageWhite,
// 'black': this.imageBlack,
// 'green': this.imageGreen,
// 'blue': this.imageBlue,
// 'red': this.imageRed
// }
// this.selfBackground.image = imageMap[selfColor];
// this.opponentBackground.image = imageMap[opponentColor];
// this.changed = true;
// };
GameBackground.prototype.setWaiting = function (waiting) {
if (waiting) {
this.mask.graphics
.clear()
.beginFill('rgba(0,0,0,0.25)')
.drawRect(0,0,this.width,this.height);
} else {
this.mask.graphics.clear();
}
this.changed = true;
};
GameBackground.prototype.update = function () {
var changed = this.changed;
this.changed = false;
return changed;
};

78
IO.js Normal file
View file

@ -0,0 +1,78 @@
'use strict';
/*
服务器 -> 客户端:
msg = {
buffer: [{
id: id,
data: []
}]
}
客户端 -> 服务器
msg = {
id: id,
data: {
label: label,
input: []
}
}
*/
function IO (socket) {
this.socket = socket;
this.listener = null;
this.sendingMsg = null; // 上次发送的信息,用于重连时重新发送.
this.datas = []; // 收到的数据.
this.id = 0;
// For test
this.inputBlocked = false;
this.outputBlocked = false;
this.socket.removeAllListeners('gameMessage');
this.socket.on('gameMessage',this.receiveGameMessage.bind(this));
};
IO.prototype.receiveGameMessage = function (msg) {
if (this.inputBlocked) return;
msg.buffer.forEach(function (buf) {
if (buf.id < this.datas.length) return;
if (buf.id !== this.datas.length) {
console.error('buf.id !== this.datas.length');
return;
}
this.sendingMsg = null;
this.datas.push(buf.data);
if (this.listener) {
this.listener(buf.data);
}
},this);
};
IO.prototype.send = function (data) {
this.id++;
this.sendingMsg = {
id: this.id,
data: data
};
if (this.outputBlocked) return;
this.socket.emit('gameMessage',this.sendingMsg);
};
IO.prototype.resend = function () {
if (!this.sendingMsg) return;
this.socket.emit('gameMessage',this.sendingMsg);
};
IO.prototype.getDatas = function () {
return this.datas;
};
// For test
IO.prototype.toggleIn = function () {
return this.inputBlocked = !this.inputBlocked;
};
IO.prototype.toggleOut = function () {
return this.outputBlocked = !this.outputBlocked;
};

1
ImageAndDetail.min.js vendored Normal file

File diff suppressed because one or more lines are too long

161
ImageFileCache.js Normal file
View file

@ -0,0 +1,161 @@
'use strict';
window.ImageFileCache = (function () {
function checkIndexedDBSupport (callback) {
var db = null;
function done(supportIndexedDB,supportBlob) {
if (db) db.close();
callback(!!supportIndexedDB,!!supportBlob);
}
if (!window.indexedDB) return done();
if (!window.IDBKeyRange) return done();
if (!window.IDBOpenDBRequest) return done();
// shit iOS
// var iOS = !!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform);
// if (iOS) return done();
// ios 8
var open = indexedDB.open('test indexedDB support',1);
if (!('onupgradeneeded' in open)) return done();
open.onupgradeneeded = function (event) {
db = this.result;
db.createObjectStore('one');
db.createObjectStore('two',{keyPath: 'key'});
};
open.onerror = function (event) {
done();
};
open.onsuccess = function (event) {
db = this.result;
db.onerror = function (event) {
done();
};
var transaction;
try {
transaction = db.transaction(['one','two'],'readwrite');
} catch (e) {
return done();
}
var req = transaction.objectStore('two').put({key: 1});
req.onsuccess = function (event) {
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
if (!window.Blob || !window.URL || (xhr.responseType !== 'blob')) {
db.close();
return done(true,false);
} else {
var blob = new Blob(['text'], {type: 'text/plain'});
try {
req = transaction.objectStore('one').put(blob,'blob');
transaction.onabort = function (event) {
event.stopPropagation();
done(true,false);
};
transaction.onerror = function (event) {
event.stopPropagation();
done(true,false);
};
transaction.oncomplete = function (event) {
return done(true,true);
};
} catch (e) {
return done(true,false);
}
}
};
};
}
function get (url,type,callback,err) {
var xhr = new XMLHttpRequest();
xhr.responseType = type;
xhr.onload = function (e) {
if (xhr.status !== 200) {
err(xhr,e);
} else {
callback(xhr,e);
}
};
xhr.onerror = function (e) {
err(xhr,e);
};
xhr.open('GET',url,true);
xhr.send();
}
function cache (pid,blob) {
if (!ImageFileCache.supportBlob) return;
if (pid in urlMap) return;
var url = window.URL.createObjectURL(blob);
urlMap[pid] = url;
var open = indexedDB.open('card images',1);
open.onupgradeneeded = function (event) {
this.result.createObjectStore('images');
};
open.onsuccess = function (event) {
var db = this.result;
db.transaction(['images'],'readwrite').objectStore('images').add(blob,pid);
};
}
var urlMap = {};
var fetchingMap = {};
var ImageFileCache = {
supportIndexedDB: false,
supportBlob: false
};
var supportObjectURL = false; // 在360浏览器上,ObjectURL可能不能用.
// 检查支持并读取缓存
checkIndexedDBSupport(function (supportIndexedDB,supportBlob) {
ImageFileCache.supportIndexedDB = supportIndexedDB;
ImageFileCache.supportBlob = supportBlob;
if (!supportBlob) return;
var open = indexedDB.open('card images',1);
open.onupgradeneeded = function (event) {
this.result.createObjectStore('images');
};
open.onsuccess = function (event) {
var db = this.result;
var checked = false;
db.transaction(['images']).objectStore('images').openCursor().onsuccess = function (event) {
var cursor = this.result;
if (!cursor) return;
var pid = cursor.key;
var blob = cursor.value;
var url = window.URL.createObjectURL(blob);
if (!checked) {
checked = true;
var img = new Image();
img.onload = function () {
supportObjectURL = true;
console.log('supportObjectURL');
};
img.src = url;
}
urlMap[pid] = url;
cursor.continue();
};
};
});
ImageFileCache.getUrlByPid = function (pid) {
if (!supportObjectURL) return '';
return urlMap[pid] || '';
};
ImageFileCache.fetchAndCache = function (pid,url) {
if (!ImageFileCache.supportBlob) return;
if (fetchingMap[pid]) return;
fetchingMap[pid] = true;
get(url,'blob',function (xhr,e) {
var blob = xhr.response;
cache(pid,blob);
},function (xhr,e) {
console.log('Failed to load "' + url + '"');
fetchingMap[pid] = false;
});
};
return ImageFileCache;
})();

59
ImageManager.js Normal file
View file

@ -0,0 +1,59 @@
'use strict';
window.ImageManager = (function () {
function newImage (src) {
var img = new Image();
img.src = src;
return img;
}
// ImageManager
function ImageManager (dir) {
this.dir = dir || './';
// 缓存
this.black = newImage(this.dir + 'images/WIXOSS_BLACK.jpg');
this.white = newImage(this.dir + 'images/WIXOSS_WHITE.jpg');
this.noimage = newImage(this.dir + 'images/noimage.jpg');
this.imageCache = {};
this.stateImageMap = {
'frozen': newImage(this.dir + 'background/frozen.png'),
'charm': newImage(this.dir + 'background/charm.png'),
'lancer': newImage(this.dir + 'background/lancer.png'),
'doubleCrash': newImage(this.dir + 'background/doubleCrash.png'),
'locked': newImage(this.dir + 'background/locked.png'),
'assassin': newImage(this.dir + 'background/assassin.png')
};
}
ImageManager.prototype.getUrlByPid = function (pid) {
var url = window.ImageFileCache.getUrlByPid(pid);
if (url) return url;
url = this.dir + 'images/' + ('0000' + pid).slice(-4) + '.jpg';
window.ImageFileCache.fetchAndCache(pid,url);
return url;
};
ImageManager.prototype.getImageByPid = function (pid) {
if (!pid) return this.noimage;
if (pid in this.imageCache) {
return this.imageCache[pid];
} else {
var src = this.getUrlByPid(pid);
var img = newImage(src);
this.imageCache[pid] = img;
return img;
}
};
ImageManager.prototype.getBackImage = function (isWhite) {
return isWhite? this.white : this.black;
};
ImageManager.prototype.getStateImage = function (state) {
return this.stateImageMap[state];
};
return ImageManager;
})();

2106
Localize.js Normal file

File diff suppressed because it is too large Load diff

2
Localize.min.js vendored Normal file

File diff suppressed because one or more lines are too long

83
MessageBox.js Normal file
View file

@ -0,0 +1,83 @@
'use strict';
function MessageBox () {
this.eWarp = document.getElementById('msgbox-warp');
this.eWin = document.getElementById('msgbox-window');
this.eMsg = document.getElementById('msgbox-msg');
this.eInput = document.getElementById('msgbox-input');
this.ePreset = document.getElementById('msgbox-preset');
this.eOk = document.getElementById('msgbox-button-ok');
this.eCancel = document.getElementById('msgbox-button-cancel');
}
MessageBox.prototype.alert = function (msg,callback) {
this.eWin.className = 'alert';
this.eMsg.textContent = msg;
this.eWarp.classList.add('shown');
this.eOk.focus();
this.eOk.onclick = function (event) {
this.close();
if (callback) callback();
}.bind(this);
};
MessageBox.prototype.confirm = function (msg,callback) {
this.eWin.className = 'confirm';
this.eMsg.textContent = msg;
this.eWarp.classList.add('shown');
this.eCancel.focus();
this.eOk.onclick = function (event) {
this.close();
if (callback) callback(true);
}.bind(this);
this.eCancel.onclick = function (event) {
this.close();
if (callback) callback(false);
}.bind(this);
};
MessageBox.prototype.prompt = function (msg,value,callback) {
if (arguments.length === 2) {
value = '';
callback = arguments[1];
}
this.eWin.className = 'prompt';
this.eMsg.textContent = msg;
this.eInput.value = value;
this.eWarp.classList.add('shown');
this.eInput.select();
var close = function (value) {
this.close();
if (callback) callback(value || '');
}.bind(this);
this.eOk.onclick = function (event) {
close(this.eInput.value);
}.bind(this);
this.eCancel.onclick = function (event) {
close();
}.bind(this);
this.eInput.onkeypress = function (event) {
var keyCode = event.keyCode || event.which || event.charCode;
if (keyCode !== 13) return;
close(this.eInput.value);
}.bind(this);
};
MessageBox.prototype.preset = function (label) {
this.eWin.className = 'preset';
var children = this.ePreset.children;
for (var i = 0; i < children.length; i++) {
var child = children[i];
child.style.display = 'none';
}
document.getElementById('preset-'+label).style.display = '';
this.eWarp.classList.add('shown');
};
MessageBox.prototype.close = function () {
this.eWarp.classList.remove('shown');
this.eWin.className = '';
};

1100
RoomManager.js Normal file

File diff suppressed because it is too large Load diff

281
Selector.js Normal file
View file

@ -0,0 +1,281 @@
'use strict';
function Selector (game,spectating) {
this.game = game;
this.msgs = [];
this.selectedIndexes = [];
this._autoPopMsg = null;
this.spectating = spectating;
}
// Selector.prototype.getMsgByLabel = function (label) {
// for (var i = 0; i < this.msgs.length; i++) {
// var msg = this.msgs[i];
// if (msg.label === label) {
// return msg;
// }
// }
// debugger;
// return null;
// };
Selector.prototype.addMsg = function (msg) {
this.msgs.push(this.parseMsg(msg));
};
Selector.prototype.parseMsg = function (msg) {
var newMsg = {
label: msg.label,
min: msg.min,
max: msg.max,
careOrder: msg.careOrder
};
newMsg.title = Localize.labelToTitle(msg.label);
newMsg.cards = msg.options.map(function (sid) {
return this.game.getObjBySid(sid);
},this);
newMsg.extraCards = msg.extraCards.map(function (sid,idx) {
var card = this.game.getObjBySid(sid);
card.pid = msg.extraPids[idx];
return card;
},this);
if (!newMsg.extraCards.length) {
newMsg.extraCards = newMsg.cards.slice();
}
newMsg.descriptions = msg.descriptions.map(function (desc) {
return Localize.desc(desc);
},this);
return newMsg;
};
// Selector.prototype.parseDesc = function (desc) {
// var arr = desc.split('-');
// var pid = arr[0];
// var type = arr[1];
// var idx = arr[2];
// var info = CardInfo[pid];
// return Localize.desc(info,type,idx);
// };
Selector.prototype.showButtons = function () {
this.msgs.forEach(function (msg) {
var cards = msg.extraCards;
if (!cards.length) {
this.game.buttonZone.addButton(Localize.noOptions(msg.label),this.selectBind(msg));
} else if (msg.label === 'USE_ACTION_EFFECT') {
var datas = [];
msg.cards.forEach(function (card,idx) {
var buttonCarrier;
var description = msg.descriptions[idx];
if (card.shouldUseDialog()) {
if (card.zone.constructor === TileZone) {
buttonCarrier = this.game.buttonZone;
} else {
buttonCarrier = card.zone;
}
} else {
buttonCarrier = card;
}
var data;
for (var i = 0; i < datas.length; i++) {
var data= datas[i];
if (data.buttonCarrier === buttonCarrier) {
data.cards.push(card);
data.descriptions.push(description);
data.indexes.push(idx);
return;
};
}
datas.push({
buttonCarrier: buttonCarrier,
cards: [card],
descriptions: [description],
indexes: [idx]
});
// card.addButton(msg.title,function () {
// var descriptions = [];
// var indexMap = [];
// msg.descriptions.forEach(function (description,idx) {
// if (msg.cards[idx] === card) {
// indexMap.push(idx);
// descriptions.push(description);
// }
// },this);
// if (descriptions.length <= 1) {
// this.send(msg,[indexMap[0]]);
// } else {
// this.game.dialog.selectText(msg.title,descriptions,true,function (idx) {
// this.send(msg,[indexMap[idx]]);
// }.bind(this));
// }
// }.bind(this));
},this);
datas.forEach(function (data) {
data.buttonCarrier.addButton(msg.title,function () {
if ((data.cards.length === 1) && (data.buttonCarrier.constructor === Card)) {
this.send(msg,[data.indexes[0]]);
} else {
var arg = {
min: 1,
max: 1,
texts: data.descriptions,
canClose: true,
careOrder: false,
targets: data.cards
}
this.game.dialog.selectSomeCards(msg.title,data.cards,arg,function (selectedIndexes) {
var i = selectedIndexes[0];
this.send(msg,[data.indexes[i]]);
}.bind(this));
}
}.bind(this));
},this);
} else {
var useDialog = msg.descriptions.length || cards.some(function (card) {
return card.shouldUseDialog();
},this);
if (useDialog) {
this._autoPopMsg = msg;
var zone = cards[0].zone;
var sameZone = cards.every(function (card) {
return card.zone === zone;
},this);
if (!sameZone || zone.constructor === TileZone) {
zone = this.game.buttonZone;
} else if (msg.label === 'GROW') {
// 根据 有k 的反馈,把成长按钮放到LRIG上.
zone = this.game.player.lrigZone;
}
zone.addButton(msg.title,this.popDialogBind(msg));
if (!msg.min) {
this.game.buttonZone.addButton(Localize.giveUp(msg.label),this.sendBind(msg));
}
} else {
this.showMsgButtons(msg);
}
}
},this);
};
Selector.prototype.select = function (msg,idx) {
if (this.spectating) return;
if (isNum(idx)) {
this.selectedIndexes.push(idx);
}
if (this.selectedIndexes.length >= msg.max) {
this.send(msg);
return;
}
this.removeButtons();
this.showMsgButtons(msg);
};
Selector.prototype.selectBind = function (msg,idx) {
return this.select.bind(this,msg,idx);
};
Selector.prototype.unselect = function (msg,idx) {
if (this.spectating) return;
removeFromArr(idx,this.selectedIndexes);
this.removeButtons();
if (this.selectedIndexes.length) {
this.showMsgButtons(msg);
} else {
this.showButtons();
}
};
Selector.prototype.unselectBind = function (msg,idx) {
return this.unselect.bind(this,msg,idx);
};
Selector.prototype.showMsgButtons = function (msg) {
msg.cards.forEach(function (card,idx) {
if (inArr(idx,this.selectedIndexes)) {
card.addButton('-'+msg.title,this.unselectBind(msg,idx));
} else {
card.addButton(msg.title,this.selectBind(msg,idx));
}
},this);
if (this.selectedIndexes.length >= msg.min) {
var txt;
if (this.selectedIndexes.length) {
txt = Localize('buttonTitle','END_SELECT');
} else {
txt = Localize.giveUp(msg.label);
}
this.game.buttonZone.addButton(txt,this.sendBind(msg));
}
};
Selector.prototype.send = function (msg,selectedIndexes) {
if (this.spectating) return;
if (!isArr(selectedIndexes)) {
selectedIndexes = this.selectedIndexes;
}
this.game.input(msg.label,selectedIndexes.slice());
console.log('game.input("'+msg.label+'",['+selectedIndexes.toString()+']);');
this.clear();
};
Selector.prototype.sendBind = function (msg) {
return this.send.bind(this,msg);
};
Selector.prototype.popDialog = function (msg) {
var title = Localize.labelToDialogTitle(msg.label);
var cards = msg.extraCards;
// if (msg.min === 1 && msg.max === 1) {
// if (msg.descriptions.length) {
// var sample = cards[0];
// var sameCard = cards.every(function (card) {
// return card === sample;
// },this);
// if (sameCard) {
// this.game.dialog.selectText(title,msg.descriptions,true,function (idx) {
// this.send(msg,[idx]);
// }.bind(this));
// return;
// }
// } else {
// this.game.dialog.selectCard(title,msg.cards,true,function (idx) {
// this.send(msg,[idx]);
// }.bind(this));
// return;
// }
// }
var arg = {
min: msg.min,
max: msg.max,
texts: msg.descriptions,
canClose: true,
careOrder: msg.careOrder,
targets: msg.cards
};
this.game.dialog.selectSomeCards(title,cards,arg,this.sendBind(msg));
};
Selector.prototype.popDialogBind = function (msg) {
return this.popDialog.bind(this,msg);
};
Selector.prototype.removeButtons = function () {
this.game.removeButtons();
};
Selector.prototype.autoPop = function () {
if (this.msgs.length === 1 && this._autoPopMsg) {
if (this._autoPopMsg.label === 'SPELL_CUT_IN') return;
this.popDialog(this._autoPopMsg);
}
};
Selector.prototype.clear = function () {
this.removeButtons();
this.game.dialog.close();
this.selectedIndexes.length = 0;
this.msgs.length = 0;
this._autoPopMsg = null;
};

8
StateBitmap.js Normal file
View file

@ -0,0 +1,8 @@
'use strict';
function StateBitmap (url) {
var bitmap = new createjs.Bitmap(url);
bitmap.width = Card.WIDTH;
bitmap.height = Card.HEIGHT;
return bitmap;
}

104
Style.js Normal file
View file

@ -0,0 +1,104 @@
function Style (defaultStyle) {
this.transitingStyle = {};
for (var prop in defaultStyle) {
this.transitingStyle[prop] = new TransitingValue(defaultStyle[prop]);
}
this.changed = true;
this.checkSkip = null;
}
Style.linear = function (t) {
return t;
};
Style.prototype.transit = function (prop,value,duration,timing) {
if (this.checkSkip && this.checkSkip()) duration = 0;
this.transitingStyle[prop].transitTo(value,duration,timing);
this.changed = true;
};
Style.prototype.set = function (prop,value) {
this.transit(prop,value);
};
Style.prototype.getComputedStyle = function () {
var computedStyle = {};
for (var prop in this.transitingStyle) {
computedStyle[prop] = this.transitingStyle[prop].getValue();
if (!this.transitingStyle[prop].isDone()) this.changed = true;
}
return computedStyle;
};
Style.prototype.isChanged = function () {
var changed = this.changed;
this.changed = false;
return changed;
};
function TransitingValue (endValue) {
this.changeTime = 0;
this.startValue = endValue;
this.endValue = endValue;
this.duration = 0;
this.timing = TransitingValue.cubic;
this.done = false;
}
TransitingValue.linear = function (t) {
return t;
};
// TransitingValue.cos = function (t) {
// return (1-Math.cos(t*Math.PI))/2;
// };
TransitingValue.cubic = function (t) {
var s = 1-t;
return 1-s*s*s;
};
TransitingValue.prototype.now = function () {
return Date.now();
};
TransitingValue.prototype.transitTo = function (value,duration,timing) {
this.startValue = this.getValue();
this.endValue = value;
this.duration = duration || 0;
this.timing = timing || TransitingValue.cubic;
this.changeTime = this.now();
this.done = false;
};
TransitingValue.prototype.getValue = function () {
var currentTime = this.now();
var startTime = this.changeTime;
var endTime = startTime + this.duration*1000;
if (currentTime < startTime) {
return this.startValue;
}
if (currentTime >= endTime || this.startValue === this.endValue) {
this.done = true;
return this.endValue;
}
if (!isNum(this.endValue)) {
return this.startValue;
}
var factor = this.timing((currentTime-startTime)/(endTime-startTime));
return this.startValue + factor*(this.endValue-this.startValue);
};
TransitingValue.prototype.isDone = function () {
return this.done;
};

52
TextDialog.js Normal file
View file

@ -0,0 +1,52 @@
'use strict';
function TextDialog (game) {
this.game = game;
}
TextDialog.prototype.newElement = function (tag) {
return document.createElement(tag);
};
TextDialog.prototype.selectOne = function (title,texts,callback) {
var dialog = this;
var ol = this.newElement('div');
texts.forEach(function (text,idx) {
var li = this.newElement('div');
li.textContent = text;
ol.appendChild(li);
li.onclick = function (event) {
dialog.close();
callback(idx);
}
},this);
this.titleDiv.textContent = title;
this.bodyDiv.innerHTML = '';
this.bodyDiv.appendChild(ol);
this.okButton.hidden = true;
this.pop(title);
};
// TextDialog.prototype.selectOne = function (title,texts,callback) {
// var ol = this.newElement('div');
// texts.forEach(function (text) {
// var li = this.newElement('div');
// var label = this.newElement('label');
// var checkbox = this.newElement('input');
// checkbox.type = 'checkbox';
// var txt = document.createTextNode(text);
// label.appendChild(checkbox);
// label.appendChild(txt);
// li.appendChild(label);
// ol.appendChild(li);
// },this);
// this.titleDiv.textContent = title;
// this.bodyDiv.innerHTML = '';
// this.bodyDiv.appendChild(ol);
// };

284
Zone.js Normal file
View file

@ -0,0 +1,284 @@
'use strict';
function Zone (cfg) {
createjs.Container.prototype.initialize.call(this);
this.game = cfg.game;
this.name = cfg.name;
this.x = cfg.x;
this.y = cfg.y;
this.showAmount = !!cfg.showAmount;
this._amount = 0;
this.opposite = !!cfg.opposite;
this.cards = [];
this.changed = true;
this.text = new createjs.Text('','bold 12px monospace');
this.text.textAlign = 'center';
// this.text.textBaseline = 'middle';
this.addChild(this.text);
if (this.opposite) {
this.rotation = 180;
this.text.rotation = 180;
}
this.game.addZone(this);
}
Zone.prototype = new createjs.Container();
Zone.prototype.constructor = Zone;
Zone.prototype.sortCards = function () {
var len = this.cards.length;
this.cards.forEach(function (card,i) {
card.style.set('zIndex',len-i);
},this);
};
Zone.prototype.addCard = function (card,bottom) {
if ((this.name !== 'SigniZone') || (this.name !== 'LrigZone')) {
card.removeStates();
}
if (bottom) {
this.cards.push(card);
} else {
this.cards.unshift(card);
}
this.sortCards();
this.updateCardPosition();
};
Zone.prototype.removeCard = function (card,bottom) {
removeFromArr(card,this.cards);
this.sortCards();
this.updateCardPosition();
};
Zone.prototype.getCardIndex = function (card) {
return this.cards.indexOf(card);
};
Zone.prototype.updateCardPosition = function () {};
Zone.prototype.update = function () {
if (this.showAmount && (this._amount !== this.cards.length)) {
this._amount = this.cards.length;
var txt = (this._amount === 0)? '' : this._amount;
this.setText(txt);
}
var changed = this.changed;
this.changed = false;
return changed;
};
Zone.prototype.addButton = function () {};
Zone.prototype.removeButtons = function () {};
Zone.prototype.setText = function (txt) {
this.changed = true;
if (!txt) {
this.text.visible = false;
return;
}
this.text.visible = true;
this.text.text = txt;
};
Zone.prototype.setTextColor = function (color) {
this.changed = true;
if (this.name === 'EnerZone') {
this.text.color = 'white';
} else {
this.text.color = color;
}
};
Zone.prototype.shouldUseDialog = function () {
return false;
};
function StackZone (cfg) {
Zone.apply(this,arguments);
this.showPower = !!cfg.showPower;
this.checkable = !!cfg.checkable;
this.changed = true;
this._power = 0;
this.power = 0;
if (cfg.centerText) {
this.text.textBaseline = 'middle';
} else {
this.text.textBaseline = 'top';
this.text.y = Card.HEIGHT/2;
if (this.opposite) {
this.text.rotation = 180;
this.text.textBaseline = 'bottom';
}
}
// 状态层
this.stateLayer = new createjs.Container();
this.stateShape = new createjs.Shape();
this.stateLayer.addChild(this.stateShape);
this.addChild(this.stateLayer);
this.buttonLayer = new ButtonList();
this.buttonLayer.rotation = -this.rotation;
this.addChild(this.buttonLayer);
if (this.checkable) {
this.viewCardsButton = new Button(Localize('buttonTitle','VIEW'),function () {
this.game.dialog.showCards(Localize('buttonTitle','VIEW'),this.cards,function () {});
}.bind(this));
this.viewCardsButton.alpha = 0.8;
}
}
StackZone.prototype = Object.create(Zone.prototype);
StackZone.prototype.constructor = StackZone;
StackZone.prototype.updateCardPosition = function () {
var covered = false;
this.cards.forEach(function (card,idx) {
card.moveTo(this.x,this.y,covered,!!idx);
if (card.x === this.x && card.y === this.y) {
covered = true;
}
},this);
};
StackZone.prototype.addButton = function (txt,onclick/*,card*/) {
var btn = new Button(txt,onclick.bind(this,this));
this.buttonLayer.addButton(btn);
};
StackZone.prototype.removeButtons = function () {
this.buttonLayer.removeAllButtons();
if (this.checkable && this.cards.length>1) {
this.buttonLayer.addButton(this.viewCardsButton);
}
};
StackZone.prototype.addViewCardsButton = function () {
this.buttonLayer.addButton(this.viewCardsButton);
};
Zone.prototype.addStates = function (states) {
this.changed = true;
this.stateLayer.visible = true;
states.forEach(function (state) {
var g = this.stateShape.graphics;
var w = Card.WIDTH;
var h = Card.HEIGHT;
if (state === 'powerDown') {
g.f('rgba(164,96,222,0.3)').r(-w/2,-h/2,w,h);
} else if (state === 'disabled') {
g.f('rgba(0,0,0,0.7)').r(-w/2,-h/2,w,h);
}
},this);
};
Zone.prototype.removeStates = function () {
this.changed = true;
this.stateLayer.visible = false;
this.stateShape.graphics.clear();
};
StackZone.prototype.update = function () {
var changed = this.buttonLayer.update() || this.changed;
if (this.showPower) {
if (this._amount !== this.cards.length) {
this._amount = this.cards.length;
this.power = this._power = 0;
this.setText('');
}
if (this._power !== this.power) {
this._power = this.power;
this.setText(this._power);
}
} else {
if (Zone.prototype.update.call(this)) {
changed = true;
}
}
this.changed = false;
return changed;
};
function TileZone (cfg) {
Zone.apply(this,arguments); // 这里设置了 x,y ,表示原点坐标.
this.up = cfg.up; // true/false: 卡片竖置/横置
this.horizontal = cfg.horizontal; // true/false: 水平/竖直分布.
this.center = cfg.center; // true/false: 居中/左对齐
this.width = cfg.width; // 区域的最大"宽度". (若该区域为竖直分布,则为高度)
this.spacing = cfg.spacing; // 卡与卡的边距.
this.showAmount = !!cfg.showAmount;
if (this.horizontal) {
this.text.textBaseline = 'middle';
if (this.opposite) {
this.text.textAlign = 'left';
} else {
this.text.textAlign = 'right';
}
} else {
this.text.textAlign = 'center';
if (this.opposite) {
this.text.textBaseline = 'top';
} else {
this.text.textBaseline = 'bottom';
}
}
}
TileZone.prototype = Object.create(Zone.prototype);
TileZone.prototype.constructor = TileZone;
TileZone.prototype.updateCardPosition = function () {
if (!this.cards.length) return;
var s = this.spacing;
var w; // 卡片在轴上所占的"宽度"
if (this.horizontal)
w = this.up? Card.WIDTH : Card.HEIGHT;
else
w = this.up? Card.HEIGHT : Card.WIDTH;
var base; // 首张卡的位置
var delta; // 卡与卡中心间隔.
var factor = this.opposite? -1 : 1; // 倒转修正系数
if ((w+s)*this.cards.length-s <= this.width) {
// 可以容纳.
delta = w+s;
if (this.center) {
base = -delta*(this.cards.length-1)/2;
} else {
base = w/2;
}
} else {
// 容纳不下,挤压
delta = (this.width-w)/(this.cards.length-1);
if (this.center) {
base = w/2 - this.width/2;
} else {
base = w/2;
}
}
// 在zone.cards中,索引为0的卡表示在"顶部"
// 因此,卡片在轴上排列时,索引大的排前面,小的排后面.
var len = this.cards.length;
if (this.horizontal) {
this.cards.forEach(function (card,i) {
var j = len - i - 1;
card.moveTo(this.x + factor*(base+j*delta),this.y);
},this);
} else {
this.cards.forEach(function (card,i) {
var j = len - i - 1;
card.moveTo(this.x,this.y + factor*(base+j*delta));
},this);
}
};

244
ZonePosition.js Normal file
View file

@ -0,0 +1,244 @@
'use strict';
Game.prototype.initZones = function () {
this.player = {
enerZone: new TileZone({
game: this,
name: 'EnerZone',
x: 92,
y: 377,
up: false,
horizontal: false,
center: false,
width: 259,
spacing: 5,
showAmount: true
}),
signiZones: [
new StackZone({
game: this,
name: 'SigniZone',
x: 190,
y: 421,
showPower: true
}),
new StackZone({
game: this,
name: 'SigniZone',
x: 288,
y: 421,
showPower: true
}),
new StackZone({
game: this,
name: 'SigniZone',
x: 386,
y: 421,
showPower: true
}),
],
mainDeck: new StackZone({
game: this,
name: 'MainDeck',
x: 471.5,
y: 421,
showAmount: true
}),
lrigDeck: new StackZone({
game: this,
name: 'LrigDeck',
x: 544.5,
y: 421,
showAmount: true,
checkable: true
}),
checkZone: new StackZone({
game: this,
name: 'CheckZone',
x: 190,
y: 519
}),
lrigZone: new StackZone({
game: this,
name: 'LrigZone',
x: 288,
y: 519,
showAmount: true
}),
trashZone: new StackZone({
game: this,
name: 'TrashZone',
x: 471.5,
y: 519,
showAmount: true,
checkable: true
}),
lrigTrashZone: new StackZone({
game: this,
name: 'LrigTrashZone',
x: 544.5,
y: 519,
showAmount: true,
checkable: true
}),
lifeClothZone: new TileZone({
game: this,
name: 'LifeClothZone',
x: 158.5,
y: 604.5,
up: false,
horizontal: true,
center: false,
width: 357,
spacing: -Card.HEIGHT*2/3,
showAmount: true
}),
excludedZone: new StackZone({
game: this,
name: 'ExcludedZone',
x: 617.5,
y: 421
}),
handZone: new TileZone({
game: this,
name: 'HandZone',
x: 576/2,
y: 690,
up: true,
horizontal: true,
center: true,
width: 576,
spacing: 8
})
};
this.opponent = {
enerZone: new TileZone({
game: this,
name: 'EnerZone',
x: 576-92,
y: 734-377,
opposite: true,
up: false,
horizontal: false,
center: false,
width: 259,
spacing: 5,
showAmount: true
}),
signiZones: [
new StackZone({
game: this,
name: 'SigniZone',
x: 576-190,
y: 734-421,
showPower: true,
opposite: true
}),
new StackZone({
game: this,
name: 'SigniZone',
x: 576-288,
y: 734-421,
showPower: true,
opposite: true
}),
new StackZone({
game: this,
name: 'SigniZone',
x: 576-386,
y: 734-421,
showPower: true,
opposite: true
}),
],
mainDeck: new StackZone({
game: this,
name: 'MainDeck',
x: 576-471.5,
y: 734-421,
showAmount: true,
opposite: true
}),
lrigDeck: new StackZone({
game: this,
name: 'LrigDeck',
x: 576-544.5,
y: 734-421,
showAmount: true,
opposite: true
}),
lrigZone: new StackZone({
game: this,
name: 'LrigZone',
x: 576-288,
y: 734-519,
showAmount: true,
opposite: true
}),
checkZone: new StackZone({
game: this,
name: 'CheckZone',
x: 576-190,
y: 734-519,
opposite: true
}),
trashZone: new StackZone({
game: this,
name: 'TrashZone',
x: 576-471.5,
y: 734-519,
showAmount: true,
opposite: true,
checkable: true
}),
lrigTrashZone: new StackZone({
game: this,
name: 'LrigTrashZone',
x: 576-544.5,
y: 734-519,
showAmount: true,
opposite: true,
checkable: true
}),
lifeClothZone: new TileZone({
game: this,
name: 'LifeClothZone',
x: 576-158.5,
y: 734-604.5,
opposite: true,
up: false,
horizontal: true,
center: false,
width: 357,
spacing: -Card.HEIGHT*2/3,
showAmount: true
}),
excludedZone: new StackZone({
game: this,
name: 'ExcludedZone',
x: 576-617.5,
y: 734-421,
opposite: true
}),
handZone: new TileZone({
game: this,
name: 'HandZone',
x: 576-576/2,
y: 734-690,
opposite: true,
up: true,
horizontal: true,
center: true,
width: 576,
spacing: 8
})
};
this.buttonZone = new StackZone({
game: this,
name: 'ButtonZone',
x: 386,
y: 519,
centerText: true
});
};

54
about.html Normal file
View file

@ -0,0 +1,54 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>关于我们</title>
<style>
body, html {
font-family: 'Microsoft YaHei','WenQuanYi Zen Hei',sans-serif;
background-image: url('background/bg.png');
}
h2 {
border-left: black solid 3px;
padding-left: 3px;
margin-left: -0.5em;
}
section {
margin-left: 1em;
margin-bottom: 1em;
}
a {
color: blue;
text-decoration: none;
}
</style>
</head>
<body>
<h1>关于我们</h1>
<section>
<p><a href="http://webxoss.com/" target="_blank">WEBXOSS</a>是一个自由,免费,无广告,多语言,跨平台,全自动的WIXOSS在线对战平台.</p>
<p>请在WEBXOSS中尽情战斗吧!</p>
<p>OPEN!!</p>
</section>
<h1 id="contact">联系我们</h1>
<section>
<p>如果你在游戏中发现了BUG或者对WEBXOSS有任何意见和建议随时欢迎联系我们哦~</p>
<p>邮箱:<a href="mailto:webxoss@gmail.com" target="_blank">webxoss@gmail.com</a></p>
<p>推特:<a href="https://twitter.com/WEBXOSS" target="_blank">@WEBXOSS</a></p>
</section>
<h1 id="support">帮助我们</h1>
<section>
<del style="color: red; font-weight: bold; text-decoration: line-through;">WEBXOSS需要您的帮助!</del>
</section>
<section>
<p>WEBXOSS已不再接受捐赠开发维护费用将由我们自己承担。</p>
<p>不用担心WEBXOSS仍将保持免费、无广告而我们也会继续做到最好。</p>
<p>尽情享受战斗吧!</p>
</section>
</body>
</html>

54
about_en.html Normal file
View file

@ -0,0 +1,54 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>ABOUT US</title>
<style>
body, html {
font-family: Arial,Helvetica,sans-serif;
background-image: url('background/bg.png');
}
h2 {
border-left: black solid 3px;
padding-left: 3px;
margin-left: -0.5em;
}
section {
margin-left: 1em;
margin-bottom: 1em;
}
a {
color: blue;
text-decoration: none;
}
</style>
</head>
<body>
<h1>ABOUT WEBXOSS</h1>
<section>
<p><a href="http://webxoss.com/" target="_blank">WEBXOSS</a> is a free, no-ads, multilingual, cross-platform and full automated<br>online Battle-Field for WIXOSS.</p>
<p>Enjoy the game!</p>
<p>OPEN!!</p>
</section>
<h1 id="contact">CONCAT US</h1>
<section>
<p>If you find any bug or have any suggestion for WEBXOSS, please concat us:</p>
<p>E-Mail:<a href="mailto:webxoss@gmail.com" target="_blank">webxoss@gmail.com</a></p>
<p>Twitter:<a href="https://twitter.com/WEBXOSS" target="_blank">@WEBXOSS</a></p>
</section>
<h1 id="support">SUPPORT US</h1>
<section>
<del style="color: red; font-weight: bold; text-decoration: line-through;">WEBXOSS needs your help!</del>
</section>
<section>
<p>WEBXOSS no more accepts donations. We will pay on ourself.</p>
<p>Don't worry, WEBXOSS is still free and no-ads. And we will keep our hard work.</p>
<p>Enjoy battling!</p>
</section>
</body>
</html>

BIN
background/DarkFßen.mp3 Normal file

Binary file not shown.

BIN
background/GreenWi.mp3 Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
background/Grid.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
background/Grid.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
background/Grid2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
background/JoinRoom.mp3 Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
background/NevWorry.mp3 Normal file

Binary file not shown.

BIN
background/Süblueß.mp3 Normal file

Binary file not shown.

BIN
background/WhiteAng.mp3 Normal file

Binary file not shown.

BIN
background/assassin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 761 B

BIN
background/bg.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

BIN
background/bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
background/black.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 685 B

BIN
background/blackGrid.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
background/blue.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 722 B

BIN
background/blueGrid.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
background/charm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 519 B

BIN
background/doubleCrash.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 884 B

BIN
background/frozen.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
background/green.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 728 B

BIN
background/greenGrid.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
background/lancer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 578 B

BIN
background/loading.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

BIN
background/locked.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
background/reLEIdEN.mp3 Normal file

Binary file not shown.

BIN
background/red.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 756 B

BIN
background/redGrid.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
background/white.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 689 B

BIN
background/whiteGrid.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
background/バトル!.mp3 Normal file

Binary file not shown.

BIN
background/击溃.mp3 Normal file

Binary file not shown.

BIN
background/双重击溃.mp3 Normal file

Binary file not shown.

BIN
background/房间.mp3 Normal file

Binary file not shown.

BIN
background/技艺.mp3 Normal file

Binary file not shown.

BIN
background/攻击.mp3 Normal file

Binary file not shown.

BIN
background/枪兵攻击.mp3 Normal file

Binary file not shown.

Binary file not shown.

BIN
background/被击溃.mp3 Normal file

Binary file not shown.

BIN
background/魔法.mp3 Normal file

Binary file not shown.

50
build.js Normal file
View file

@ -0,0 +1,50 @@
'use strict'
const map = {
'Localize.min.js': ['Localize.js'],
'ImageAndDetail.min.js': [
'ImageManager.js',
'Detail.js',
'ImageFileCache.js',
],
'webxoss.js': [
'./lib/util.js',
'MessageBox.js',
'IO.js',
'Card.js',
'CardBitmap.js',
'StateBitmap.js',
'Style.js',
'Zone.js',
'Game.js',
'ZonePosition.js',
'Button.js',
'ButtonList.js',
'Selector.js',
'Dialog.js',
'GameBackground.js',
'FakeSocket.js',
'GameAudio.js',
'ChatManager.js',
'RoomManager.js',
],
// DeckEditor
'./DeckEditor/Deck.min.js': [
'./lib/util.js',
'./DeckManager.js',
'./DeckEditor/Rules.js',
'./DeckEditor/Searcher.js',
],
'./DeckEditor/DeckEditor.js': [
'./lib/util.js',
'./DeckEditor/editor.js',
],
}
const fs = require('fs')
const uglify = require('uglify-js')
Object.keys(map).forEach(key => {
let code = uglify.minify(map[key]).code
fs.writeFileSync(key, code)
console.log(`${key} done.`)
})

964
css.css Normal file
View file

@ -0,0 +1,964 @@
html {
margin: 0;
padding: 0;
}
body {
position: relative;
font-family: 'Microsoft YaHei','WenQuanYi Zen Hei',sans-serif;
margin: 0;
padding: 0;
/*height: 739px;*/
min-width: 500px;
background-image: url('background/bg.png');
}
table, th, td {
border: 1px solid black;
padding: 0;
}
table {
border-collapse: collapse;
border-spacing: 0px;
}
ol,ul,li {
margin: 0;
padding: 0;
list-style-type: none;
}
#Logo {
text-align: center;
font-size: 60px;
}
#subtitle {
text-align: center;
font-size: small;
/*cursor: pointer;*/
}
/*#notice-match {
display: none;
}
#subtitle.match #notice-match {
display: block;
font-weight: bold;
color: blue !important;
cursor: pointer;
}
#subtitle.match #link-version {
display: none;
}
#preset-match {
line-height: 1.5;
}
#preset-match .head {
font-size: 120%;
font-weight: bold;
text-align: center;
}
#preset-match ol {
padding-left: 1em;
}
#preset-match ol,
#preset-match li {
list-style-type: decimal;
}*/
/*[id^="preset-match"] .list {
margin: 1em 0;
}
[id^="preset-match"] .info {
transition: all 0.5s;
}
[id^="preset-match"] .info:hover {
background-color: #eee;
}
[id^="preset-match"] em {
color: blue;
font-style: normal;
}
[id^="preset-match"] a {
color: blue !important;
}
[id^="preset-match"] .region {
font-weight: bold;
}
[id^="preset-match"] .contact {
padding-left: 2em;
}*/
/* here */
#link-support-webxoss {
display: none;
}
#subtitle.support-webxoss #link-support-webxoss {
display: block;
color: blue !important;
text-decoration: underline;
}
#subtitle.support-webxoss #link-version {
display: none;
}
#link-version {
color: inherit;
text-decoration: none;
}
#div-online-counter {
position: absolute;
right: 10px;
top: 10px;
font-size: small;
text-align: right;
}
#span-latency.warn {
color: red;
}
#msgbox-warp {
display: none;
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: 10;
background-color: rgba(255, 255, 255, 0.8);
text-align: center;
overflow: auto;
}
#msgbox-warp.shown {
display: block;
}
#msgbox-window {
position: relative;
min-width: 200px;
display: inline-block;
top: 10%;
left: 0;
right: 0;
margin: auto;
background-color: white;
box-shadow: 0px 0px 15px rgba(0,0,0,0.7);
text-align: left;
padding: 1em 2em;
}
/*#msgbox-warp.shown #msgbox-window {
top: 10%;
opacity: 1;
transition: all 0.3s ease-out;
}*/
#msgbox-body {
margin: 1em 0;
}
#msgbox-msg {
white-space: pre;
}
#msgbox-input {
box-sizing: border-box;
width: 100%;
min-width: 200px;
}
#msgbox-foot {
text-align: right;
}
#msgbox-preset {
display: none;
}
#msgbox-window.alert #msgbox-input,
#msgbox-window.alert #msgbox-button-cancel {
display: none;
}
#msgbox-window.confirm #msgbox-input {
display: none;
}
#msgbox-window.prompt #msgbox-msg::after {
content: ":";
}
#msgbox-window.preset #msgbox-body,
#msgbox-window.preset #msgbox-foot {
display: none;
}
#msgbox-window.preset #msgbox-preset {
display: block;
}
/* 录像 */
#replay-list {
min-width: 20em;
height: 10em;
border: #aaa 1px solid;
margin: 0.5em 0;
overflow: auto;
}
#replay-list > li {
cursor: pointer;
transition: color 0.3s;
}
#replay-list > li:hover {
color: blue;
}
/* 重连 */
#wait-for-reconnect-foot {
text-align: right;
}
.waiting {
font-style: italic;
margin: 0.5em 0;
}
.waiting::before {
content: url("./background/loading.gif");
}
/* 代理 */
#span-set-proxy {
cursor: pointer;
}
#proxy-head {
text-align: center;
font-size: 120%;
font-weight: bold;
}
#proxy-body {
width: 400px;
/* margin-left: 1em; */
text-indent: 1em;
}
#proxy-foot {
text-align: right;
}
#div-notice {
visibility: hidden;
position: absolute;
top: 0;
left: 0;
max-width: 0;
transition: all 1.5s;
overflow: hidden;
white-space: nowrap;
font-size: 80%;
color: #aaa;
background-color: #f0f0f0;
}
#div-notice.shown {
padding: 0 1em;
max-width: 280px;
visibility: visible;
}
#div-notice.green {
color: gray;
background-color: #86F986;
}
#link-edit-deck {
color: blue;
text-decoration: none;
}
#Hall {
margin-left: 1em;
}
#div-hall-body {
width: 480px;
position: relative;
}
/*#div-rooms {
width: 480px;
}*/
#room-list {
padding-left: 2em;
height: 20em;
overflow: auto;
border: black solid 1px;
}
#div-options {
position: absolute;
right: 0;
bottom: 0;
}
.checkbox {
display: none;
}
.checkbox + span {
font-size: 85%;
}
.checkbox + span {
color: #FF005D;
}
.checkbox + span::before {
content: '\2718';
}
.checkbox:checked + span {
color: green;
}
.checkbox:checked + span::before {
content: '\2714';
}
#select-language {
border: none;
}
#room-list > li {
cursor: pointer;
}
#room-list > li.password-required::before {
/*color: #888;*/
content: '\1F512';
}
#room-list,#room-list > li {
list-style-type: decimal;
}
#room-list > li.live::before {
color: red;
content: '\25CFLive ';
}
#room-list > li.no-mayus-room::after {
color: red;
content: ' Mayu\'s Room';
text-decoration: line-through;
}
#span-play-replay {
position: absolute;
right: 0;
color: blue;
cursor: pointer;
}
#table-create-room,
#table-create-room td {
border: none;
}
#span-leave-room {
font-weight: bold;
color: #009;
cursor: pointer;
}
#container-live {
position: absolute;
top: 0;
right: 0;
}
/*
#room-name {
color: #f2442b;
}
#room-host-nickname {
color: #77d25b;
}
#room-guest-nickname {
color: #888;
}
#room-guest-nickname.ready {
color: #6c9af8;
}
*/
#Room {
text-align: center;
}
#container-room {
display: inline-block;
text-align: left;
position: relative;
}
#table-room {
border-collapse: separate;
border-spacing: 2px;
}
#table-room,
#table-room th,
#table-room td {
border: none;
}
#table-room td {
position: relative;
}
#table-room td[colspan] {
text-align: center;
}
#td-vs {
width: 60px;
text-align: center;
font-size: 180%;
font-weight: bold;
color: white;
text-shadow: black 0 0 5px;
}
.nickname {
display: block;
position: relative;
box-sizing: border-box;
width: 170px;
border-left: solid;
padding-left: 4px;
height: 25px;
line-height: 25px;
transition: background-color 0.8s,border-color 0.8s;
}
.nickname::before {
display: block;
position: absolute;
width: 100%;
height: 100%;
left: 0;
background-color: white;
content: ' ';
opacity: 0;
transition: opacity 0.3s;
}
.nickname.clickable {
cursor: pointer;
}
.nickname.clickable:hover::before {
opacity: 0.2;
}
#room-name {
color: #f2442b;
font-size: 150%;
font-weight: bold;
}
#room-host-nickname {
color: #77d25b;
background-color: #fff;
}
#room-guest-nickname {
color: #888;
background-color: #DDD;
}
#room-guest-nickname.ready {
color: #6c9af8;
background-color: #fff;
}
[id^=host-spectator-] {
color: #fff;
background-color: hsl(106, 80%, 80%);
border-color: #77d25b;
}
[id^=guest-spectator-] {
color: white;
background-color: hsl(220, 100%, 85%);
border-color: #6c9af8;
}
.nickname.locked {
color: #222;
background-color: #DDD;
border-color: #666;
font-weight: bold;
}
.nickname.locked::after {
content: 'Locked';
font-style: italic;
}
[id^=checkbox-spectator-] {
visibility: hidden;
position: absolute;
right: 0;
top: 0;
bottom: 0;
margin: auto;
width: 15px;
height: 15px;
opacity: 0;
}
#Room[data-position=guest] [id^=checkbox-spectator-] {
visibility: visible;
}
td:hover [id^=checkbox-spectator-] {
opacity: 1;
}
@media handheld {
[id^=checkbox-spectator-] {
opacity: 1;
}
}
@media (pointer:coarse) {
[id^=checkbox-spectator-] {
opacity: 1;
}
}
#GameDiv {
position: absolute;
width: 100%;
/*background-color: gray;*/
}
.game-background {
position: absolute;
width: 100%;
height: 367px;
/*opacity: 0;*/
/*transition: opacity 1s;*/
min-width: 1096px;
}
body {
transition: background-color 1s;
}
body[self] .game-background {
animation: fade-in 1s;
-webkit-animation: fade-in 1s;
-moz-animation: fade-in 1s;
-ms-animation: fade-in 1s;
-o-animation: fade-in 1s;
}
@keyframes fade-in {
0% {opacity: 0;}
100% {opacity: 1;}
}
@-webkit-keyframes fade-in {
0% {opacity: 0;}
100% {opacity: 1;}
}
@-moz-keyframes fade-in {
0% {opacity: 0;}
100% {opacity: 1;}
}
@-ms-keyframes fade-in {
0% {opacity: 0;}
100% {opacity: 1;}
}
@-o-keyframes fade-in {
0% {opacity: 0;}
100% {opacity: 1;}
}
body[self] table,
body[self] th,
body[self] td {
border-color: white;
}
body[self] #detail {
color: white;
text-shadow: black 0 0 4px;
}
#background-opponent {
transform: rotate(180deg);
-webkit-transform: rotate(180deg);
-moz-transform: rotate(180deg);
-ms-transform: rotate(180deg);
-o-transform: rotate(180deg);
}
#background-self {
top: 367px;
}
#background-opponent-grid {
width: 836px;
transform: rotate(180deg);
-webkit-transform: rotate(180deg);
-moz-transform: rotate(180deg);
-ms-transform: rotate(180deg);
-o-transform: rotate(180deg);
transform-origin: 548px;
-webkit-transform-origin: 548px;
-moz-transform-origin: 548px;
-ms-transform-origin: 548px;
-o-transform-origin: 548px;
background-repeat: no-repeat;
background-image: url('background/Grid.webp');
}
#background-self-grid {
top: 367px;
width: 836px;
background-repeat: no-repeat;
background-image: url('background/Grid.webp');
}
body[self=white] #background-self,
body[opponent=white] #background-opponent {
background-image: url('background/white.jpg');
}
body[self=white] #background-self-grid,
body[opponent=white] #background-opponent-grid {
background-image: url('background/whiteGrid.jpg');
}
body[self=black] #background-self,
body[opponent=black] #background-opponent {
background-image: url('background/black.jpg');
}
body[self=black] #background-self-grid,
body[opponent=black] #background-opponent-grid {
background-image: url('background/blackGrid.jpg');
}
body[self=red] #background-self,
body[opponent=red] #background-opponent {
background-image: url('background/red.jpg');
}
body[self=red] #background-self-grid,
body[opponent=red] #background-opponent-grid {
background-image: url('background/redGrid.jpg');
}
body[self=blue] #background-self,
body[opponent=blue] #background-opponent {
background-image: url('background/blue.jpg');
}
body[self=blue] #background-self-grid,
body[opponent=blue] #background-opponent-grid {
background-image: url('background/blueGrid.jpg');
}
body[self=green] #background-self,
body[opponent=green] #background-opponent {
background-image: url('background/green.jpg');
}
body[self=green] #background-self-grid,
body[opponent=green] #background-opponent-grid {
background-image: url('background/greenGrid.jpg');
}
body[self] {
background-image: none;
}
body[self=white] {
background-color: #E7FAFE;
}
body[self=black] {
background-color: #250034;
}
body[self=red] {
background-color: #C00B00;
}
body[self=blue] {
background-color: #00A0FE;
}
body[self=green] {
background-color: #004101;
}
#detail {
position: absolute;
padding: 0 5px;
box-sizing: border-box;
width: 260px;
font-size: 16px;
}
#detail-card-figure {
height: 349px;
}
#detail-card-image {
border-radius: 4%;
}
#detail-card-data {
height: 385px;
overflow-y: auto;
overflow-x: hidden;
position: relative;
}
#detail-card-wxid {
font-size: small;
}
#detail-card-name {
font-weight: bold;
}
#detail-card-name > a {
color: inherit;
text-decoration: none;
}
#detail-card-name > a:active {
color: red;
}
#detail-card-limiting {
position: absolute;
top: 0;
right: 0.5em;
}
#detail-table {
width: 100%;
white-space: pre-line;
}
#detail-table[lang="ru"] {
word-break: break-all;
}
.key {
width: 2.5em;
text-align: center;
}
#BattleField {
left: 260px;
position: absolute;
font-size: 18px;
}
#BattleField .warp {
position: absolute;
left: 0px;
top: 0px;
width: 100%;
height: 100%;
/* background-color: rgba(255,255,255,0.75); */
}
#BattleField .dialog {
position: absolute;
left: 0px;
top: 0px;
background-color: white;
box-shadow: 0px 0px 38px rgba(0,0,0,0.5);
}
#BattleField .title {
white-space: pre;
}
#BattleField .body {
margin: 0 18px;
max-width: 428px;
max-height: 573px;
/*min-width: 120px;*/
overflow: auto;
}
#BattleField .title, #BattleField .footer {
font-weight: bold;
text-align: center;
margin: 5px;
}
#BattleField .card {
width: 126px;
height: 176px;
overflow: hidden;
display: inline-block;
position: relative;
margin: 5px;
user-select: none;
-webkit-user-select: none;
cursor: default;
}
#BattleField .card .txt, #BattleField .card .msk, #BattleField .card .num, #BattleField .card .img {
position: absolute;
top: 0;
left: 0;
}
#BattleField .card img {
width: 100%;
height: 100%;
}
#BattleField .card .txt {
max-width: 90%;
font-size: 0.8em;
font-weight: bold;
background-color: rgba(255, 255, 255, 0.8);
box-shadow: black 0 0 5px;
padding: 0.5em 0;
}
#BattleField .card .num {
color: white;
font-size: 80px;
font-weight: bold;
text-shadow: black 0 0 10px;
}
#BattleField .card .msk {
width: 100%;
height: 100%;
}
#BattleField .card:hover .msk {
background-color: white;
opacity: 0.1;
}
#BattleField .card.disabled .msk {
background-color: black;
opacity: 0.5;
}
#BattleField .card.selected .msk {
background-color: white;
opacity: 0.5;
}
#BattleField .option {
margin: 0.5em;
transition: color 0.3s;
cursor: pointer;
}
#BattleField .option:hover {
color: blue;
}
#BattleField button {
margin: 8px;
}
#BattleField .closeIcon {
position: absolute;
top: 0;
right: 0;
width: 1em;
height: 1em;
opacity: 0.5;
background-size: cover;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAVElEQVR42mNgGLLgKBAfAmJ+JDF+qNhRfBoPAvF/ID4F1cAPZf+HasYJQApPImk+hWYQA7GaidbEgOY8kmxDdt5JYjUfwhI4J4kJnGN4ouPYEE1iAM+rIqZ75+i/AAAAAElFTkSuQmCC);
}
#BattleField .closeIcon:hover {
opacity: 1;
}
#BattleField .dialog select {
width: 100px;
}
#BattleField .dialog .replay {
color: blue;
font-weight: normal;
font-size: 80%;
text-decoration: underline;
}
#div-replay-controls {
position: absolute;
left: 846px;
}
#div-surrender {
position: absolute;
left: 841px;
top: 5px;
width: 200px;
z-index: 5;
font-size: 80%;
cursor: pointer;
}
#div-replay-return {
text-align: right;
}
body:not(.gaming) #Chat {
position: absolute;
left: 0;
right: 0;
margin: auto;
width: 410px;
}
body.gaming {
height: 739px;
min-width: 1145px;
}
body.gaming #Chat {
position: absolute;
left: 845px;
bottom: 20px;
width: 300px;
}
body.gaming #chat-dialogue {
min-height: initial;
max-height: 670px;
}
#chat-dialogue {
min-height: 100px;
max-height: 250px;
overflow-y: auto;
-ms-overflow-style: none;
}
#chat-dialogue::-webkit-scrollbar {
display: none;
}
/* #chat-dialogue > div {
padding: 0.4em 0;
}
#chat-dialogue > div.self {
text-align: right;
}
#chat-dialogue > div.opponent {
text-align: left;
} */
#chat-dialogue > div.sys {
text-align: center;
font-size: 80%;
padding: 0;
}
/* #chat-dialogue > div > span {
display: inline-block;
max-width: 85%;
padding: 0.2em 0.6em;
white-space: pre-wrap;
word-wrap: break-word;
}
#chat-dialogue > div.self > span {
border-right: solid #959595 2px;
background-color: #F8F8F8;
box-shadow: #CCC 0px 1px 2px;
}
#chat-dialogue > div.opponent > span {
border-left: solid #00AEFF 2px;
background-color: #E5F6FF;
box-shadow: #99DFFF 0px 1px 2px;
} */
/* div */
#chat-dialogue div {
float: left;
clear: both;
max-width: 80%;
border-left: solid 2px;
margin: 3px 0;
padding: 0 5px;
border-color: #FF8000;
background-color: #FFFFB3;
}
#chat-dialogue div.opponent {
float: right;
border-color: #00AEFF;
background-color: #E5F6FF;
}
#chat-dialogue div.spectator {
border-color: #959595;
background-color: #F8F8F8;
}
/* name */
#chat-dialogue .name::after {
content: ':';
}
#chat-dialogue .name {
display: block;
font-size: 80%;
font-weight: bold;
color: hsl(276, 60%, 63%);
}
#chat-dialogue .opponent .name {
color: #3BC20A;
}
#chat-dialogue .spectator .name {
font-weight: normal;
color: #959595;
}
/* content */
#chat-dialogue .content {
padding-left: 1em;
white-space: pre-wrap;
word-wrap: break-word;
}
/* input */
#chat-input-bar {
width: 100%;
height: 25px;
}
#chat-input {
width: 100%;
height: 100%;
box-sizing: border-box;
margin: 0;
background: rgba(0,0,0,0);
border: none;
border-bottom: #ddd solid 1px;
outline: none;
}
#chat-input:focus {
border-bottom: #bbb solid 1px;
}
body[self=white] #chat-input,
body[opponent=white] #div-surrender {
color: black;
}
body[self=black] #chat-input,
body[opponent=black] #div-surrender {
color: yellow;
}
body[self=red] #chat-input,
body[opponent=red] #div-surrender {
color: white;
}
body[self=blue] #chat-input,
body[opponent=blue] #div-surrender {
color: yellow;
}
body[self=green] #chat-input,
body[opponent=green] #div-surrender {
color: white;
}
#div-about {
text-align: center;
font-size: small;
margin-top: 1em;
}
#div-about > a {
margin: 0 0.5em;
color: #aaa;
text-decoration: none
}

258
index.html Normal file
View file

@ -0,0 +1,258 @@
<!DOCTYPE html>
<html manifest="webxoss.appcache">
<!-- <html> -->
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>WEBXOSS | Beta</title>
<meta name="description" content="WEBXOSS is a free, no-ads, multilingual, cross-platform and full automated online Battle-Field for WIXOSS.">
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<script>
var global = window;
if (window.location != window.parent.location) window.parent.location = window.location;
</script>
<link rel="stylesheet" type="text/css" href="./css.css" />
</head>
<body>
<div id="Hall">
<div id="Logo">WEBXOSS</div>
<div id="subtitle"><a id="link-support-webxoss" href="about.html#support" target="_blank"></a><a id="link-version" href="versions.txt" target="_blank">&lt;隐忍火蝶&gt;</a></div>
<div id="div-notice">
<span id="span-notice"></span>
</div>
<div id="div-online-counter">
<span id="span-set-proxy">设置代理</span><br><span id="label-online-counter">Online:</span> <span id="span-online-counter"></span><br>
<span id="label-latency">Latency:</span><span id="span-latency"></span>
</div>
<div id="div-hall-body">
<div id="div-nickname">
<span id="label-nickname">您的昵称:</span><input id="input-nickame" type="text" placeholder="昵称" maxlength="10"> <a id="link-edit-deck" href="./DeckEditor/">编辑卡组</a>
</div>
<div id="div-rooms">
<div style="position: relative;">
<div id="div-options">
<label><input id="checkbox-bgm" type="checkbox" class="checkbox"><span id="label-bgm">音乐</span></label>
<label><input id="checkbox-sound-effect" type="checkbox" class="checkbox" checked="checked"><span id="label-sound-effect">音效</span></label>
<select id="select-language">
<option value="en">English</option>
<option value="zh_CN">简体中文</option>
<option value="zh_TW">繁體中文</option>
<option value="jp">日本語</option>
<option value="ru">русский</option>
<option value="it">Italiano</option>
</select>
</div>
<span id="label-room-list">房间列表:</span>
</div>
<ol id="room-list"></ol>
<span id="span-play-replay">录像回放</span>
</div>
<div id="div-create-room">
<table id="table-create-room">
<tbody>
<tr>
<td><span id="label-create-room">创建房间:</span></td>
<td><input id="input-room-name" type="text" placeholder="房间名字" maxlength="15"></td>
<td><label><input id="checkbox-mayus-room" type="checkbox" class="checkbox" checked="checked"><span id="label-mayus-room">茧的房间</span></label></td>
</tr>
<tr>
<td><span id="label-create-room-password">房间密码:</span></td>
<td><input id="input-create-room-password" type="text" maxlength="15"></td>
<td><button id="button-create-room">创建</button></td>
</tr>
</tbody>
</table>
<!-- <div>
<span id="label-create-room">创建房间:</span> <input id="input-room-name" type="text" placeholder="房间名字" maxlength="20"> <button id="button-password">密码</button> <button id="button-create-room">创建</button>
</div>
<div>
<span id="label-create-room-password">密码:</span> <input id="input-create-room-password" type="text" maxlength="15">
</div> -->
</div>
</div>
<div id="div-about">
<a id="link-copyright">©2015 WEBXOSS.COM</a><a id="link-about" href="about.html" target="_blank">关于我们</a><a id="link-service" href="service.html" target="_blank">服务条款</a><a id="link-disclaimer" href="service.html#disclaimer" target="_blank">免责声明</a><a id="link-supporters" href="supporters.html" target="_blank" style="color: #697;">捐赠者名单</a>
</div>
</div>
<div id="Room" style="display: none;"><div id="container-room">
<div>
<span id="span-leave-room">&lt;&lt;返回</span>
</div>
<div>
<label id="container-live"><input id="input-live" type="checkbox" checked><span id="label-live">直播</span></label>
</div>
<table id="table-room">
<tbody>
<tr>
<td colspan="100"><span id="room-name"></span></td>
</tr>
<tr>
<td><span id="room-host-nickname" class="nickname"></span></td>
<td rowspan="6" id="td-vs"><span>VS</span></td>
<td><span id="room-guest-nickname" class="nickname"></span></td>
</tr>
<tr>
<td><span id="host-spectator-0" class="nickname"></span></td>
<td><span id="guest-spectator-0" class="nickname"></span> <input type="checkbox" id="checkbox-spectator-0"></td>
</tr>
<tr>
<td><span id="host-spectator-1" class="nickname"></span></td>
<td><span id="guest-spectator-1" class="nickname"></span> <input type="checkbox" id="checkbox-spectator-1"></td>
</tr>
<tr>
<td><span id="host-spectator-2" class="nickname"></span></td>
<td><span id="guest-spectator-2" class="nickname"></span> <input type="checkbox" id="checkbox-spectator-2"></td>
</tr>
<tr>
<td><span id="host-spectator-3" class="nickname"></span></td>
<td><span id="guest-spectator-3" class="nickname"></span> <input type="checkbox" id="checkbox-spectator-3"></td>
</tr>
<tr>
<td><span id="host-spectator-4" class="nickname"></span></td>
<td><span id="guest-spectator-4" class="nickname"></span> <input type="checkbox" id="checkbox-spectator-4"></td>
</tr>
<tr>
<td colspan="100">
<select id="select-decks"></select> <button id="button-start-game">Oben!</button>
<label id="container-ready"><input id="input-ready" type="checkbox"><span id="label-ready">准备</span></label>
</td>
</tr>
</tbody>
<!-- <ul id="list-host-spectators"></ul> -->
<!-- <ul id="list-guest-spectators"></ul> -->
</table>
</div></div>
<div id="GameDiv" style="display: none;">
<div class="game-background" id="background-opponent"></div>
<div class="game-background" id="background-opponent-grid"></div>
<div class="game-background" id="background-self"></div>
<div class="game-background" id="background-self-grid"></div>
<div id="detail">
<div id="detail-card-figure">
<img id="detail-card-image">
</div>
<div id="detail-card-data">
<div id="detail-card-wxid"></div>
<div id="detail-card-name"></div>
<div id="detail-card-limiting"></div>
<table id="detail-table">
<tbody id="detail-table-body"></tbody>
</table>
</div>
</div>
<div id="BattleField">
<!-- <canvas id="BattleFieldCanvas" width="576" height="734"></canvas> -->
</div>
<div id="div-replay-controls" style="display: none;">
<button id="button-replay-step">下一步</button> <button id="button-replay-auto">自动播放</button>
</div>
<div id="div-surrender">
<span id="span-surrender">投降</span>
<span id="span-leave-game">离开</span>
</div>
</div>
<div id="Chat" style="display: none;">
<div id="chat-dialogue"></div>
<div id="chat-input-bar">
<input id="chat-input" type="text" placeholder="在此处聊天" maxlength="256">
</div>
</div>
<div id="msgbox-warp">
<div id="msgbox-window">
<div id="msgbox-head">
<span id="msgbox-title"></span>
</div>
<div id="msgbox-preset">
<div id="preset-replay">
<div>
<div id="label-replay-list">录像列表:</div>
<ol id="replay-list"></ol>
</div>
<div><span id="label-replay-file">录像文件:</span> <input id="input-replay-file" type="file" accept=".wxrep"></div>
<div id="div-replay-return"><button id="button-replay-return">返回</button></div>
</div>
<div id="preset-reconnect">
<div><span id="reconnect-title">连接已断开!</span></div>
<div class="waiting"><span id="reconnect-retry">正在重新连接...</span></div>
</div>
<div id="preset-wait-for-reconnect">
<div><span id="wait-for-reconnect-title">对手的连接已断开!</span></div>
<div class="waiting"><span id="wait-for-reconnect-retry">正在等待重新连接...</span></div>
<div id="wait-for-reconnect-foot"><button id="wait-for-reconnect-button-drop">放弃等待</button></div>
</div>
<div id="preset-proxy">
<div id="proxy-head"><span id="proxy-title">设置代理</span></div>
<div id="proxy-body"><span id="proxy-description">使用代理后,你与WEBXOSS服务器之间的数据将由代理服务器转发. 通常情况下,你不必设置代理,但如果掉线情况频繁发生,代理可能会有所帮助.</span></div>
<div id="proxy-foot">
<select id="select-proxy">
<option id="proxy-noproxy" value="">不使用代理</option>
<option id="proxy-cloudflare" value="cloudflare.webxoss.com">CloudFlare</option>
<option id="proxy-incapsula" value="incapsula.webxoss.com">Incapsula</option>
<option id="proxy-provide" value="provide">提供代理服务器...</option>
</select>
<button id="proxy-button-ok">确定</button>
</div>
</div>
<div id="preset-warn">
<div>
<span>
You are visiting this site from an untrusted domain.<br>
Please go to <a href="http://webxoss.com/">webxoss.com</a> instead.<br>
<br>
If you are the administrator of this domain,<br>
please contact us via webxoss@gmail.com
<span>
</div>
</div>
</div>
<div id="msgbox-body">
<div><span id="msgbox-msg"></span></div>
<div><input type="text" id="msgbox-input"></div>
</div>
<div id="msgbox-foot">
<div><button id="msgbox-button-ok">确定</button> <button id="msgbox-button-cancel">取消</button></div>
</div>
</div>
</div>
<audio id="audio-bgm" loop></audio>
<audio id="audio-sound-effect" autoplay></audio>
<script src="./lib/easeljs-0.8.2.min.js" defer="defer"></script>
<script src="./socket.io.min.js" defer="defer"></script>
<script src="./CardInfo.js" defer="defer"></script>
<script src="./CardInfo_ru.js" defer="defer"></script>
<script src="./Localize.min.js" defer="defer"></script>
<script src="./ImageAndDetail.min.js" defer="defer"></script>
<script src="./DeckEditor/Deck.min.js" defer="defer"></script>
<script src="./webxoss.js" defer="defer"></script>
<!-- <script src="./Localize.js" defer="defer"></script>
<script src="./ImageFileCache.js" defer="defer"></script>
<script src="./ImageManager.js" defer="defer"></script>
<script src="./Detail.js" defer="defer"></script>
<script src="./lib/util.js" defer="defer"></script>
<script src="./MessageBox.js" defer="defer"></script>
<script src="./IO.js" defer="defer"></script>
<script src="./Card.js" defer="defer"></script>
<script src="./CardBitmap.js" defer="defer"></script>
<script src="./StateBitmap.js" defer="defer"></script>
<script src="./Style.js" defer="defer"></script>
<script src="./Zone.js" defer="defer"></script>
<script src="./Game.js" defer="defer"></script>
<script src="./ZonePosition.js" defer="defer"></script>
<script src="./Button.js" defer="defer"></script>
<script src="./ButtonList.js" defer="defer"></script>
<script src="./Selector.js" defer="defer"></script>
<script src="./Dialog.js" defer="defer"></script>
<script src="./GameBackground.js" defer="defer"></script>
<script src="./FakeSocket.js" defer="defer"></script>
<script src="./ChatManager.js" defer="defer"></script>
<script src="./GameAudio.js" defer="defer"></script>
<script src="./DeckManager.js" defer="defer"></script>
<script src="./DeckEditor/Rules.js" defer="defer"></script>
<script src="./DeckEditor/Searcher.js" defer="defer"></script>
<script src="./RoomManager.js" defer="defer"></script> -->
</body>
</html>

16
lib/createjs-2013.12.12.min.js vendored Normal file

File diff suppressed because one or more lines are too long

17
lib/createjs-2015.11.26.min.js vendored Normal file

File diff suppressed because one or more lines are too long

14
lib/easeljs-0.8.2.min.js vendored Normal file

File diff suppressed because one or more lines are too long

55
lib/util.js Normal file
View file

@ -0,0 +1,55 @@
'use strict';
var concat = Array.prototype.concat.bind([]);
var toArr = function (obj) {
if (!obj) return [];
if (typeof obj === 'string') return [];
return Array.prototype.slice.call(obj,0);
};
var isArr = Array.isArray;
var inArr = function (item,arr) {
return (toArr(arr).indexOf(item) != -1);
};
var removeFromArr = function (item,arr) {
var idx = arr.indexOf(item);
if (idx < 0) {
return false;
} else {
arr.splice(idx,1);
return true;
}
}
var isStr = function (v) {
return (typeof v === 'string');
};
var isObj = function (v) {
return v && (typeof v === 'object') && !isArr(v);
};
var isNum = function (v) {
return (typeof v === 'number');
};
var isFunc = function (v) {
return (typeof v === 'function');
};
var pEach = function (arr,func,thisp) {
return arr.reduce(function (chain,item) {
return chain.then(function () {
return func(item);
});
},Promise.resolve());
}
function callConstructor(constructor) {
var factoryFunction = constructor.bind.apply(constructor,arguments);
return new factoryFunction();
}
function applyToConstructor(constructor,argArray) {
// var args = [null].concat(argArray);
var args = concat(null,toArr(argArray));
var factoryFunction = constructor.bind.apply(constructor,args);
return new factoryFunction();
}
function nextTick (callback) {
setTimeout(callback,0);
}

19
package.json Normal file
View file

@ -0,0 +1,19 @@
{
"name": "webxoss-client",
"version": "1.0.0",
"description": "Web client for WEBXOSS",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"webxoss",
"wixoss",
"tcg"
],
"author": "WEBXOSS",
"license": "MIT",
"devDependencies": {
"uglify-js": "^2.7.3"
}
}

38
service.html Normal file
View file

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>WEBXOSS服务条款</title>
<style>
body, html {
font-family: 'Microsoft YaHei','WenQuanYi Zen Hei',sans-serif;
background-image: url('background/bg.png');
}
</style>
</head>
<body>
<h1>WEBXOSS服务条款</h1>
<h2>总则</h2>
<p>
在使用WEBXOSS前,您务必阅读并遵守《WEBXOSS服务条款》(以下简称"本条款").<br>
一旦您使用了WEBXOSS所提供的服务,即视为您已了解并完全同意本服务条款各项内容.<br>
若您对本条款有任何异议,请停止使用WEBXOSS的各项服务.
</p>
<h2>1. 服务内容</h2>
<p>
WEBXOSS为用户提供在线对战服务,同时保留变更,中断或终止部分服务的权利.
</p>
<h2 id="disclaimer">2. 免责声明</h2>
<p>
WEBXOSS仅提供WIXOSS的在线对战平台,<br>
其中所使用的卡牌图片,卡牌信息,及WIXOSS的游戏规则由©<a href="http://www.takaratomy.co.jp/products/wixoss/" target="_blank">TOMY</a>所有,中文版的卡牌信息由©<a href="http://www.china-beetle.com/" target="_blank">甲壳虫</a>所有.<br>
卡片的英文信息来自<a href="http://selector-wixoss.wikia.com/" target="_blank">wikia</a>.
</p>
<h2>3. 服务条款的完善和修改</h2>
<p>
WEBXOSS保留随时修改服务条款的权利,用户在使用WEBXOSS时,有必要对最新的服务条款进行仔细阅读和重新确认,<br>
当发生有关争议时,请以最新的服务条款为准.
</p>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show more