Initial commit
162
Civilization.js
Normal file
|
@ -0,0 +1,162 @@
|
|||
class Civilization {
|
||||
constructor(player, game, x, y, addEnvironment = true, addTownhall = true) {
|
||||
this.player = player;
|
||||
this.game = game;
|
||||
this.origin = {x: x, y: y};
|
||||
this.uuid = guid();
|
||||
|
||||
this.color = player.color;
|
||||
this.balance = 10;
|
||||
|
||||
if(addEnvironment) {
|
||||
console.log(x, y);
|
||||
this.spawn();
|
||||
}
|
||||
|
||||
console.log(addTownhall);
|
||||
if(addTownhall)
|
||||
this.townhall = new Townhall(this, this.game);
|
||||
}
|
||||
|
||||
spawn() {
|
||||
const field = this.game.field;
|
||||
field[this.origin.x][this.origin.y].overtake(this);
|
||||
field[this.origin.x][this.origin.y].getAdjacentTiles().forEach(tile => tile.overtake(this));
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.game.field.forEach(col => {
|
||||
col.forEach(tile => {
|
||||
if(tile.claimedBy === this) {
|
||||
tile.claimedBy = null;
|
||||
|
||||
if(tile.object) {
|
||||
if(tile.object instanceof Warrior)
|
||||
tile.object.kill();
|
||||
else if(tile.object instanceof Building)
|
||||
tile.object.destroy();
|
||||
else if(tile.object instanceof Tree)
|
||||
tile.object.cutDown();
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
getBalanceChange() {
|
||||
let balanceChange = this.getSize();
|
||||
|
||||
balanceChange += 4 * this.getFarms().length;
|
||||
|
||||
for(let tower of this.getTowers()) {
|
||||
balanceChange -= tower.costsPerRound;
|
||||
}
|
||||
|
||||
for(let troop of this.getTroops()) {
|
||||
balanceChange -= troop.costsPerRound;
|
||||
}
|
||||
|
||||
balanceChange -= this.getTreeCount();
|
||||
|
||||
return balanceChange;
|
||||
}
|
||||
|
||||
getCivilizationTiles() {
|
||||
const tiles = [];
|
||||
|
||||
for(let x = 0; x < this.game.fieldSize.x; x++) {
|
||||
for(let y = 0; y < this.game.fieldSize.y; y++) {
|
||||
if(!!this.game.field[x][y].claimedBy && this.game.field[x][y].claimedBy.uuid === this.uuid) {
|
||||
tiles.push(this.game.field[x][y]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tiles;
|
||||
}
|
||||
|
||||
getFarms() {
|
||||
return Object.values(this.game.buildings).filter(building => building instanceof Farm && building.civilization === this);
|
||||
}
|
||||
|
||||
getSize() {
|
||||
return this.getCivilizationTiles().length;
|
||||
}
|
||||
|
||||
getTowers() {
|
||||
return [];
|
||||
}
|
||||
|
||||
getTreeCount() {
|
||||
return this.getCivilizationTiles().filter(tile => !!tile.object && tile.object instanceof Tree).length;
|
||||
}
|
||||
|
||||
getTroops() {
|
||||
return Object.values(this.game.warriors).filter(warrior => warrior.civilization === this);
|
||||
}
|
||||
|
||||
processRound() {
|
||||
this.balance += this.getBalanceChange();
|
||||
|
||||
if(this.balance <= 0) {
|
||||
this.getTroops().forEach(troop => {
|
||||
troop.kill();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
select(selected) {
|
||||
this.isSelected = selected;
|
||||
|
||||
// TODO: Update UI
|
||||
}
|
||||
|
||||
split() {
|
||||
let tiles = this.getCivilizationTiles();
|
||||
const initialSize = tiles.length;
|
||||
|
||||
console.log('hallo welt', tiles.length, tiles[0].getAdjacentCivilizationTiles(this).length);
|
||||
|
||||
if(tiles.length === tiles[0].getAdjacentCivilizationTiles(this).length + 1 && !!this.townhall)
|
||||
return;
|
||||
|
||||
const oldIndex = game.civilizations[this.player.uuid].findIndex(civ => civ.uuid === this.uuid);
|
||||
game.civilizations[this.player.uuid].splice(oldIndex, 1);
|
||||
if(tiles.length === 0)
|
||||
return;
|
||||
|
||||
const newCivs = [];
|
||||
|
||||
if(!!this.townhall)
|
||||
this.townhall.destroy();
|
||||
while(tiles.length > 0) {
|
||||
console.log('hey');
|
||||
|
||||
const civTiles = [tiles[0], ...tiles[0].getAdjacentCivilizationTiles(this)];
|
||||
tiles = tiles.filter(tile => !civTiles.includes(tile));
|
||||
|
||||
const availableTiles = civTiles.filter(tile => !tile.object);
|
||||
let newOrigin = civTiles[Math.floor(Math.random() * civTiles.length)];
|
||||
if(availableTiles.length > 0) {
|
||||
newOrigin = availableTiles[Math.floor(Math.random() * availableTiles.length)];
|
||||
}
|
||||
console.log(availableTiles.length > 0);
|
||||
const newCivilization = game.addCivilization(this.player, newOrigin.pos.x, newOrigin.pos.y, this.constructor !== CivilizationAI, false, availableTiles.length > 0);
|
||||
newCivs.push(newCivilization);
|
||||
|
||||
newCivilization.balance = Math.round(this.balance * (civTiles.length / initialSize));
|
||||
|
||||
civTiles.forEach(tile => {
|
||||
tile.claimedBy = newCivilization;
|
||||
if(!!tile.object && (tile.object instanceof Warrior || tile.object instanceof Tower)) {
|
||||
tile.object.civilization = newCivilization;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
newCivs.forEach(civ => {
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
}
|
14
CivilizationAI.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
class CivilizationAI extends Civilization {
|
||||
constructor(player, game, x, y, addEnvironment, addTownhall) {
|
||||
super(player, game, x, y, addEnvironment, addTownhall);
|
||||
}
|
||||
|
||||
processRound() {
|
||||
const availableWarriors = Object.values(this.game.warriors).filter(warrior => warrior.civilization === this);
|
||||
availableWarriors.forEach(warrior => {
|
||||
|
||||
});
|
||||
|
||||
super.processRound();
|
||||
}
|
||||
}
|
550
Game.js
Normal file
|
@ -0,0 +1,550 @@
|
|||
/**
|
||||
* @property {Node} btnTroops
|
||||
* @property {Node} btnFarmer
|
||||
* @property {Node} btnHoplite
|
||||
* @property {Node} btnServant
|
||||
* @property {Node} btnKnight
|
||||
* @property {Node} btnTowers
|
||||
* @property {Node} btnFarm
|
||||
* @property {Node} btnTowerSmall
|
||||
* @property {Node} btnTowerBig
|
||||
*/
|
||||
class Game {
|
||||
constructor() {
|
||||
this.fieldSize = {x: 20, y: 10};
|
||||
this.scale = 1;
|
||||
this.deltaX = 0;
|
||||
this.deltaY = 0;
|
||||
|
||||
this.warriors = {};
|
||||
this.buildings = {};
|
||||
this.field = [];
|
||||
this.civilizations = {};
|
||||
this.trees = {};
|
||||
|
||||
this.round = 1;
|
||||
|
||||
this.hexagonSize = {width: 27.85714285714286, padding: 32.16760145166612, offset: 6.9285714285};
|
||||
this.initializeElements();
|
||||
this.resize();
|
||||
this.createHexagons();
|
||||
|
||||
this.registerEventListener();
|
||||
}
|
||||
|
||||
initializeElements() {
|
||||
this.elements = {};
|
||||
|
||||
this.container = body.createChild('div', 'gameContainer');
|
||||
this.gameStats = this.container.createChild('div', 'gameStats');
|
||||
this.viewContainer = this.container.createChild('div', 'viewContainer');
|
||||
this.view = this.viewContainer.createChild('div', 'view');
|
||||
this.playground = this.view.createChild('ul', 'playground');
|
||||
this.btns = this.container.createChild('div', 'controls');
|
||||
|
||||
this.elements.roundParent = this.gameStats.createChild('h3', ['gameStat', 'left'], 'Round: ');
|
||||
this.elements.round = this.elements.roundParent.createChild('span', 'count', '1');
|
||||
|
||||
this.elements.balanceParent = this.gameStats.createChild('h3', ['gameStat', 'center']);
|
||||
this.elements.balance = this.elements.balanceParent.createChild('span', 'count');
|
||||
this.elements.balanceChange = this.elements.balanceParent.createChild('span', ['count', 'text-grey']);
|
||||
|
||||
this.elements.player = this.gameStats.createChild('h3', ['gameStat', 'right'], 'Player ' + player.color.toUpperCase());
|
||||
|
||||
this.nextRoundBtn = this.btns.createChild('button', ['btn', 'btn-primary', 'btn-nextRound'], 'Next Round');
|
||||
this.nextRoundBtn.addEventListener('click', () => {
|
||||
this.nextRound()
|
||||
});
|
||||
|
||||
[this.btnTroops, this.btnFarmer, this.btnHoplite, this.btnServant, this.btnKnight] = createTroopsBtn(this.btns);
|
||||
|
||||
[this.btnFarmer, this.btnHoplite, this.btnServant, this.btnKnight].forEach((btn, index) => {
|
||||
const types = [Farmer, Hoplite, Servant, Knight];
|
||||
this.addBtnClickListener(btn, types[index], 'addWarrior');
|
||||
});
|
||||
|
||||
[this.btnTowers, this.btnFarm, this.btnTowerSmall, this.btnTowerBig] = createTowersBtn(this.btns);
|
||||
|
||||
[this.btnFarm, this.btnTowerSmall, this.btnTowerBig].forEach((btn, index) => {
|
||||
const types = [Farm, SmallTower, BigTower];
|
||||
this.addBtnClickListener(btn, types[index], 'addBuilding');
|
||||
});
|
||||
}
|
||||
|
||||
addBtnClickListener(btn, className, functionName) {
|
||||
btn.addEventListener('click', () => {
|
||||
if (!!this.selectedCivilization) {
|
||||
const newObj = {
|
||||
type: 'new',
|
||||
class: className,
|
||||
function: functionName,
|
||||
civilization: this.selectedCivilization
|
||||
};
|
||||
|
||||
if (!!this.draggingElement &&
|
||||
!!this.selectedObject &&
|
||||
this.selectedObject.type === 'new' &&
|
||||
this.selectedObject.class === newObj.class &&
|
||||
this.selectedObject.civilization === newObj.civilization)
|
||||
return;
|
||||
|
||||
this.deselectObject();
|
||||
this.draggingElement = className.getDraggingObject(this);
|
||||
this.selectedObject = newObj;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addBuilding(x, y, civilization, type) {
|
||||
const tile = this.field[x][y];
|
||||
|
||||
console.log(tile, type);
|
||||
|
||||
if (type.initialCosts > civilization.balance)
|
||||
return false;
|
||||
|
||||
console.log('creatio');
|
||||
if (!tile.claimedBy || tile.claimedBy.player !== civilization.player) // Is non-friendly territory
|
||||
return false;
|
||||
|
||||
if (tile.object !== null)
|
||||
return false;
|
||||
|
||||
console.log('success');
|
||||
|
||||
const building = new type(x, y, this, civilization);
|
||||
|
||||
tile.object = building;
|
||||
this.buildings[building.uuid] = building;
|
||||
return true;
|
||||
}
|
||||
|
||||
addCivilization(player, x, y, realPlayer = false, addEnvironment = true, addTownhall = true) {
|
||||
if (!this.civilizations[player.uuid])
|
||||
this.civilizations[player.uuid] = [];
|
||||
|
||||
const CivilizationType = realPlayer ? Civilization : CivilizationAI;
|
||||
const newCivilization = new CivilizationType(player, this, x, y, addEnvironment, addTownhall);
|
||||
|
||||
this.civilizations[player.uuid].push(newCivilization);
|
||||
return newCivilization;
|
||||
}
|
||||
|
||||
addTree(x, y) {
|
||||
if (!!this.field[x][y].object)
|
||||
return;
|
||||
|
||||
const tree = new Tree(x, y, this);
|
||||
this.trees[tree.uuid] = tree;
|
||||
}
|
||||
|
||||
addWarrior(x, y, civilization, type) {
|
||||
const tile = this.field[x][y];
|
||||
|
||||
if (type.initialCosts > civilization.balance)
|
||||
return false;
|
||||
|
||||
const warrior = new type(x, y, this, civilization);
|
||||
const player = civilization.player;
|
||||
|
||||
if (!!tile.claimedBy && tile.claimedBy.player === player) { // Is friendly territory
|
||||
if (!!tile.object) { // Is object on the tile?
|
||||
if (tile.object instanceof Tree) {
|
||||
tile.object.cutDown();
|
||||
} else if (tile.object instanceof Warrior) {
|
||||
if (tile.object.rank <= 4 - warrior.rank) {
|
||||
tile.object.merge(warrior);
|
||||
}
|
||||
return false;
|
||||
} else if (tile.object instanceof Building) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else { // Is hostile territory
|
||||
if (!tile.getAdjacentTiles().find(adjTile => adjTile.claimedBy === civilization)) { // There isn't any tile of the given civilization nearby
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Check adjacent tiles
|
||||
|
||||
if (!!tile.object) {
|
||||
if (tile.object instanceof Tree) {
|
||||
tile.object.cutDown();
|
||||
} else if (tile.object instanceof Warrior) {
|
||||
if (tile.object.rank < warrior.rank) {
|
||||
tile.object.kill();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if (tile.object instanceof Building) {
|
||||
if (tile.object.rank < warrior.rank) {
|
||||
tile.object.destroy();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.warriors[warrior.uuid] = warrior;
|
||||
tile.object = warrior;
|
||||
tile.overtake(civilization);
|
||||
return true;
|
||||
}
|
||||
|
||||
createHexagons() {
|
||||
for (let x = 0; x < this.fieldSize.x; x++) {
|
||||
this.field.push([]);
|
||||
}
|
||||
|
||||
const margin = Math.round(this.hexagonSize.width * .1);
|
||||
|
||||
for (let y = 0; y < this.fieldSize.y; y++) {
|
||||
for (let x = 0; x < this.fieldSize.x; x++) {
|
||||
const listItem = this.playground.createChild('li');
|
||||
|
||||
listItem.style.width = this.hexagonSize.width + "px";
|
||||
listItem.style.marginRight = margin + 'px';
|
||||
listItem.style.paddingBottom = this.hexagonSize.padding + "px";
|
||||
|
||||
if (y % 2 !== 0) {
|
||||
if (x === 0)
|
||||
listItem.style.marginLeft = margin / 2 + 'px';
|
||||
|
||||
listItem.style.marginTop = listItem.style.marginBottom = -2 * margin + "px";
|
||||
listItem.classList.add('even-row');
|
||||
}
|
||||
|
||||
listItem.createChild('div', ['hexagon', 'grey']);
|
||||
|
||||
this.field[x][y] = new Tile(this, listItem, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
calculateHexagonSize() {
|
||||
const paddingRatio = this.hexagonSize.padding / this.hexagonSize.width;
|
||||
const offsetRatio = this.hexagonSize.offset / this.hexagonSize.width;
|
||||
|
||||
this.container.style.width = '';
|
||||
|
||||
const widthPercentage = (100 / this.fieldSize.x * .9);
|
||||
let width = Math.round(widthPercentage / 100 * this.container.clientWidth);
|
||||
if (width % 2 !== 0)
|
||||
width -= 1;
|
||||
this.hexagonSize.width = width;
|
||||
|
||||
this.hexagonSize.padding = this.hexagonSize.width * paddingRatio;
|
||||
this.hexagonSize.offset = this.hexagonSize.width * offsetRatio;
|
||||
}
|
||||
|
||||
increaseRound() {
|
||||
this.round++;
|
||||
this.elements.round.innerText = this.round;
|
||||
}
|
||||
|
||||
nextRound() {
|
||||
// TODO: Simulate other players
|
||||
|
||||
// Lets the trees breed
|
||||
Object.values(this.trees).forEach(tree => {
|
||||
tree.breed();
|
||||
});
|
||||
|
||||
// Handles finances of the civilizations
|
||||
Object.values(this.civilizations).forEach(civilizations => {
|
||||
civilizations.forEach(civilization => {
|
||||
civilization.processRound();
|
||||
});
|
||||
});
|
||||
|
||||
// Sets all warriors waiting
|
||||
Object.values(this.warriors).forEach(warrior => {
|
||||
if (warrior.civilization.player === player) {
|
||||
warrior.setNeedsAction(true);
|
||||
}
|
||||
});
|
||||
|
||||
this.increaseRound();
|
||||
this.updateBalance();
|
||||
}
|
||||
|
||||
registerEventListener() {
|
||||
let mouseStartX;
|
||||
let mouseStartY;
|
||||
let startX;
|
||||
let startY;
|
||||
|
||||
this.view.onmousedown = e => {
|
||||
mouseStartX = e.clientX;
|
||||
mouseStartY = e.clientY;
|
||||
|
||||
startX = parseInt(this.view.style.left.slice(0, this.view.style.left.length - 2)) || 0;
|
||||
startY = parseInt(this.view.style.top.slice(0, this.view.style.top.length - 2)) || 0;
|
||||
};
|
||||
|
||||
this.view.onmousemove = e => {
|
||||
if (!!mouseStartX && !!mouseStartY) {
|
||||
let deltaX = e.clientX - mouseStartX + startX;
|
||||
let deltaY = e.clientY - mouseStartY + startY;
|
||||
|
||||
this.repositionView(deltaX, deltaY);
|
||||
}
|
||||
};
|
||||
|
||||
this.view.onmouseup = e => {
|
||||
mouseStartX = undefined;
|
||||
mouseStartY = undefined;
|
||||
};
|
||||
|
||||
this.viewContainer.onwheel = e => {
|
||||
const prevScale = this.scale;
|
||||
if (e.deltaY < 0) {
|
||||
this.scale *= 1.1;
|
||||
} else {
|
||||
this.scale /= 1.1;
|
||||
}
|
||||
this.scale = Math.max(this.scale, 1);
|
||||
this.scale = Math.min(this.scale, 10);
|
||||
const deltaScale = this.scale / prevScale;
|
||||
this.view.style.transform = `scale(${this.scale})`;
|
||||
|
||||
if (!!this.draggingElement) {
|
||||
let dragging = this.draggingElement.childNodes[0];
|
||||
dragging.style.width = deltaScale * dragging.clientWidth + 'px';
|
||||
dragging.style.height = deltaScale * dragging.clientHeight + 'px';
|
||||
}
|
||||
|
||||
let centerY = -(this.view.clientHeight - this.viewContainer.clientHeight);
|
||||
if (this.view.clientHeight < this.viewContainer.clientHeight) {
|
||||
centerY /= 2;
|
||||
}
|
||||
this.view.classList.add('zooming');
|
||||
this.repositionView(this.deltaX * deltaScale, centerY - (centerY - this.deltaY) * deltaScale);
|
||||
setTimeout(() => this.view.classList.remove('zooming'), 200);
|
||||
};
|
||||
|
||||
document.onmousemove = (ev) => {
|
||||
if (!!this.draggingElement) {
|
||||
const draggingContainer = this.draggingElement;
|
||||
const dragging = draggingContainer.childNodes[0];
|
||||
|
||||
draggingContainer.style.left = ev.clientX + 'px';
|
||||
draggingContainer.style.top = ev.clientY + 'px';
|
||||
|
||||
if (!!prevMouseEvent) {
|
||||
clearTimeout(timeout);
|
||||
const vel = (ev.clientX - prevMouseEvent.clientX) / (ev.timeStamp - prevMouseEvent.timeStamp);
|
||||
|
||||
let rotation = Math.min(Math.max(vel * 80, -80), 80);
|
||||
|
||||
dragging.style.transform = 'rotateZ(' + rotation + 'deg)';
|
||||
|
||||
timeout = setTimeout(() => {
|
||||
dragging.style.transform = 'rotateZ(0deg)';
|
||||
}, 50);
|
||||
}
|
||||
|
||||
prevMouseEvent = ev;
|
||||
}
|
||||
};
|
||||
|
||||
this.container.addEventListener('contextmenu', (ev) => ev.preventDefault());
|
||||
|
||||
window.addEventListener('resize', () => this.resize());
|
||||
}
|
||||
|
||||
repositionView(deltaX, deltaY) {
|
||||
const maxDeltaX = (this.view.clientWidth * this.scale - this.viewContainer.clientWidth) / 2;
|
||||
const maxDeltaTop = this.view.clientHeight * (this.scale - 1) / 2;
|
||||
let maxDeltaBottom = (this.view.clientHeight - this.viewContainer.clientHeight);
|
||||
if (this.view.clientHeight < this.viewContainer.clientHeight) {
|
||||
maxDeltaBottom /= 2;
|
||||
}
|
||||
|
||||
deltaX = Math.min(deltaX, maxDeltaX);
|
||||
deltaX = Math.max(deltaX, -maxDeltaX);
|
||||
deltaY = Math.min(deltaY, maxDeltaTop);
|
||||
deltaY = Math.max(deltaY, -maxDeltaBottom);
|
||||
|
||||
this.deltaX = deltaX;
|
||||
this.deltaY = deltaY;
|
||||
|
||||
this.view.style.left = deltaX + 'px';
|
||||
this.view.style.top = deltaY + 'px';
|
||||
}
|
||||
|
||||
resize() {
|
||||
this.calculateHexagonSize();
|
||||
|
||||
const containerWidth = Math.round(this.hexagonSize.width / .9) * this.fieldSize.x;
|
||||
this.container.style.width = containerWidth + 'px';
|
||||
|
||||
const margin = Math.round(this.hexagonSize.width * .1);
|
||||
this.field.forEach((col, x) => {
|
||||
col.forEach((tile, y) => {
|
||||
tile.element.style.width = this.hexagonSize.width + "px";
|
||||
tile.element.style.marginRight = margin + 'px';
|
||||
tile.element.style.paddingBottom = this.hexagonSize.padding + "px";
|
||||
|
||||
if (y % 2 !== 0) {
|
||||
if (x === 0)
|
||||
tile.element.style.marginLeft = margin / 2 + 'px';
|
||||
|
||||
tile.element.style.marginTop = tile.element.style.marginBottom = -2 * margin + "px";
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// this.view.style.width = containerWidth + this.hexagonSize.width / 2 + 'px';
|
||||
this.view.style.height = (this.hexagonSize.width / 1.01 * this.scale) * this.fieldSize.y + 'px';
|
||||
|
||||
this.field.forEach(col => {
|
||||
col.forEach(tile => {
|
||||
if (!!tile.object) {
|
||||
tile.object.adjustPosition();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this.repositionView(0, 0);
|
||||
}
|
||||
|
||||
selectCivilization(civilization) {
|
||||
if (!!this.selectedCivilization)
|
||||
this.selectedCivilization.select(false);
|
||||
|
||||
if (!!civilization && civilization.player === player) {
|
||||
this.selectedCivilization = civilization;
|
||||
civilization.select(true);
|
||||
this.btnTroops.classList.remove('disabled');
|
||||
this.btnTowers.classList.remove('disabled');
|
||||
} else {
|
||||
this.selectedCivilization = null;
|
||||
this.btnTroops.classList.add('disabled');
|
||||
this.btnTroops.classList.remove('isActive');
|
||||
this.btnTowers.classList.add('disabled');
|
||||
this.btnTowers.classList.remove('isActive');
|
||||
}
|
||||
|
||||
this.updateBalance();
|
||||
}
|
||||
|
||||
selectWarrior(warrior) {
|
||||
this.selectedObject = warrior;
|
||||
}
|
||||
|
||||
updateBalance() {
|
||||
if (!!this.selectedCivilization && this.selectedCivilization.player === player) {
|
||||
const balanceChange = this.selectedCivilization.getBalanceChange();
|
||||
|
||||
this.elements.balance.innerText = this.selectedCivilization.balance;
|
||||
this.elements.balanceChange.innerText = balanceChange > 0 ? '+' + balanceChange : balanceChange;
|
||||
} else {
|
||||
this.elements.balance.innerText = null;
|
||||
this.elements.balanceChange.innerText = null;
|
||||
}
|
||||
}
|
||||
|
||||
deselectObject() {
|
||||
if (!!this.selectedObject) {
|
||||
this.selectedObject.isSelected = false;
|
||||
}
|
||||
this.selectedObject = null;
|
||||
|
||||
if (!!this.draggingElement) {
|
||||
this.draggingElement.parentNode.removeChild(this.draggingElement);
|
||||
}
|
||||
this.draggingElement = null;
|
||||
}
|
||||
}
|
||||
|
||||
class CivilizationNN extends Civilization {
|
||||
constructor(player, game, x, y) {
|
||||
super(player, game, x, y, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
class GameNN extends Game {
|
||||
constructor(nn) {
|
||||
super();
|
||||
|
||||
this.ga = nn || new GeneticAlgorithm(10, 4, this);
|
||||
for (let i = 0; i < this.ga.maxUnits; i++) {
|
||||
let x;
|
||||
let y;
|
||||
let isGood = false;
|
||||
while(!isGood) {
|
||||
x = this.ga.random(0, 19);
|
||||
y = this.ga.random(0, 9);
|
||||
|
||||
if (!this.field[x][y].getAdjacentTiles().find(tile => !!tile.claimedBy))
|
||||
isGood = true;
|
||||
}
|
||||
|
||||
const civ = this.addCivilization(new Player('green'), x, y);
|
||||
civ.index = i;
|
||||
}
|
||||
this.initialCivs = this.civilizations;
|
||||
|
||||
this.fotze = true;
|
||||
|
||||
if(!nn) {
|
||||
this.ga.reset();
|
||||
this.ga.createPopulation();
|
||||
}
|
||||
}
|
||||
|
||||
addCivilization(player, x, y) {
|
||||
if (!this.civilizations[player.uuid])
|
||||
this.civilizations[player.uuid] = [];
|
||||
|
||||
const newCivilization = new CivilizationNN(player, this, x, y);
|
||||
|
||||
this.civilizations[player.uuid].push(newCivilization);
|
||||
|
||||
if(x < this.fieldSize.x - 1)
|
||||
this.addWarrior(x + 1, y, newCivilization, Farmer);
|
||||
|
||||
return newCivilization;
|
||||
}
|
||||
|
||||
nextRound() {
|
||||
if(Object.values(this.civilizations).length === 0) {
|
||||
this.ga.evolvePopulation();
|
||||
this.ga.iteration++;
|
||||
}
|
||||
|
||||
Object.values(this.civilizations).forEach(civilizations => {
|
||||
civilizations.forEach(civilization => {
|
||||
civilization.fitness = civilization.getSize();
|
||||
civilization.score = this.round;
|
||||
this.ga.activateBrain(civilization);
|
||||
});
|
||||
});
|
||||
|
||||
super.nextRound();
|
||||
|
||||
if(this.round === 1000) {
|
||||
this.ga.evolvePopulation();
|
||||
this.ga.iteration++;
|
||||
|
||||
Object.values(this.civilizations).forEach(player => {
|
||||
player.forEach(civ => {
|
||||
civ.destroy();
|
||||
});
|
||||
});
|
||||
this.civilizations = this.initialCivs;
|
||||
Object.values(this.civilizations).forEach(player => {
|
||||
player.forEach(civ => {
|
||||
civ.spawn();
|
||||
});
|
||||
});
|
||||
this.round = 0;
|
||||
}
|
||||
setTimeout(() => {
|
||||
if (this.fotze)
|
||||
this.nextRound();
|
||||
}, 50);
|
||||
}
|
||||
}
|
62
GameElement.js
Normal file
|
@ -0,0 +1,62 @@
|
|||
class GameElement {
|
||||
constructor(x, y, game, asset, setObject = true) {
|
||||
this.game = game;
|
||||
this.x = Math.min(x, this.game.fieldSize.x - 1);
|
||||
this.y = Math.min(y, this.game.fieldSize.y - 1);
|
||||
this.uuid = guid();
|
||||
|
||||
this.createElement(asset);
|
||||
|
||||
if(setObject)
|
||||
this.game.field[x][y].object = this;
|
||||
}
|
||||
|
||||
adjustPosition() {
|
||||
const tile = this.game.field[this.x][this.y].element;
|
||||
this.displayWidth = Math.round(this.game.hexagonSize.width * .9);
|
||||
let offsetX = tile.offsetLeft;
|
||||
let offsetY = tile.offsetTop;
|
||||
|
||||
if (this.y % 2 !== 0)
|
||||
offsetX += Math.round(this.displayWidth * .5);
|
||||
|
||||
this.element.style.width = this.displayWidth + 'px';
|
||||
this.element.style.left = offsetX + 'px';
|
||||
this.element.style.top = offsetY + 'px';
|
||||
}
|
||||
|
||||
createElement(asset) {
|
||||
this.element = GameElement.createElement(asset, (this.constructor.name).toLowerCase());
|
||||
this.adjustPosition();
|
||||
this.game.view.appendChild(this.element);
|
||||
}
|
||||
|
||||
static createElement(asset, className) {
|
||||
const element = document.createElement('img');
|
||||
element.src = 'assets/' + asset;
|
||||
element.ondragstart = () => {
|
||||
return false
|
||||
};
|
||||
element.ondragend = () => {
|
||||
return false
|
||||
};
|
||||
element.classList.add('gameElement', className);
|
||||
return element;
|
||||
}
|
||||
|
||||
static getDraggingObject(game, className, image) {
|
||||
const draggingContainer = body.createChild('div', 'draggingContainer');
|
||||
const dragging = GameElement.createElement(image, className);
|
||||
dragging.classList.add('dragging');
|
||||
dragging.style.width = game.hexagonSize.width * game.scale * 1.1 + 'px';
|
||||
dragging.style.height = game.hexagonSize.padding * game.scale * 1.1 + 'px';
|
||||
draggingContainer.appendChild(dragging);
|
||||
|
||||
return draggingContainer;
|
||||
}
|
||||
|
||||
update() {
|
||||
this.adjustPosition();
|
||||
this.game.updateBalance();
|
||||
}
|
||||
}
|
202
NeuralNetwork.js
Normal file
|
@ -0,0 +1,202 @@
|
|||
class GeneticAlgorithm {
|
||||
constructor(maxUnits, topUnits, game) {
|
||||
this.maxUnits = maxUnits;
|
||||
this.topUnits = topUnits;
|
||||
|
||||
if(this.maxUnits < this.topUnits)
|
||||
this.topUnits = maxUnits;
|
||||
|
||||
this.population = [];
|
||||
|
||||
this.SCALE_FACTOR = 200;
|
||||
|
||||
this.game = game;
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.iteration = 1;
|
||||
this.mutateRate = 1;
|
||||
|
||||
this.bestPopulation = 0;
|
||||
this.bestFitness = 0;
|
||||
this.bestScore = 0;
|
||||
}
|
||||
|
||||
createPopulation() {
|
||||
this.population.slice(0, this.population.length);
|
||||
|
||||
for(let i = 0; i < this.maxUnits; i++) {
|
||||
const newUnit = new synaptic.Architect.Perceptron(6 * this.game.fieldSize.x * this.game.fieldSize.y, 35, 5);
|
||||
newUnit.index = i;
|
||||
newUnit.fitness = 0;
|
||||
newUnit.score = 0;
|
||||
newUnit.isWinner = false;
|
||||
|
||||
this.population.push(newUnit);
|
||||
}
|
||||
}
|
||||
|
||||
activateBrain(civilization) {
|
||||
// TODO: Inputs
|
||||
const inputs = [civilization.balance, civilization.getBalanceChange()];
|
||||
for(let x = 0; x < this.game.fieldSize.x; x++) {
|
||||
for(let y = 0; y < this.game.fieldSize.y; y++) {
|
||||
const tile = this.game.field[x][y];
|
||||
|
||||
const isFriendly = tile.claimedBy === civilization ? 1 : 0;
|
||||
const hasObject = !!tile.object ? 1 : 0;
|
||||
const canAttack = tile.object instanceof Warrior ? 1 : 0;
|
||||
const rank = !!tile.object ? tile.object.rank / 4 : 0;
|
||||
|
||||
inputs.push(x, y, isFriendly, hasObject, canAttack, rank);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(civilization.index);
|
||||
const outputs = this.population[civilization.index].activate(inputs);
|
||||
|
||||
console.log(outputs);
|
||||
const type = outputs[0];
|
||||
const x = Math.round(outputs[1] * (this.game.fieldSize.x - 1));
|
||||
const y = Math.round(outputs[2] * (this.game.fieldSize.y - 1));
|
||||
const tile = this.game.field[x][y];
|
||||
|
||||
if(type < .5) {
|
||||
if(tile.object && tile.claimedBy === civilization && tile.object instanceof Warrior) {
|
||||
const deltaX = Math.round(outputs[3] * 4 - 2) / 4;
|
||||
const deltaY = Math.round(outputs[4] * 4 - 2) / 4;
|
||||
|
||||
const targetX = x + deltaX;
|
||||
const targetY = y + deltaY;
|
||||
|
||||
tile.object.move(targetX, targetY);
|
||||
|
||||
console.log(deltaX, deltaY);
|
||||
}
|
||||
} else {
|
||||
const itemType = outputs[3];
|
||||
console.log('hey');
|
||||
|
||||
if(itemType > .5) {
|
||||
console.log('ho');
|
||||
if(!tile.getAdjacentTiles().find(tile => tile.claimedBy === civilization))
|
||||
return;
|
||||
|
||||
const troopType = Math.round(outputs[4] * 3);
|
||||
const troopTypes = [Farmer, Hoplite, Servant, Knight];
|
||||
this.game.addWarrior(x, y, civilization, troopTypes[troopType]);
|
||||
} else {
|
||||
if(tile.claimedBy !== civilization)
|
||||
return;
|
||||
|
||||
const buildingType = Math.round(outputs[4] * 2);
|
||||
const buildingTypes = [Farm, SmallTower, BigTower];
|
||||
console.log('ho2', buildingType, tile);
|
||||
this.game.addBuilding(x, y, civilization, buildingTypes[buildingType]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
evolvePopulation() {
|
||||
const winners = this.selection();
|
||||
|
||||
if(this.mutateRate === 1 && winners[0].fitness < 0) {
|
||||
this.createPopulation();
|
||||
} else {
|
||||
this.mutateRate = .2;
|
||||
}
|
||||
|
||||
for(let i = this.topUnits; i < this.maxUnits; i++) {
|
||||
let parentA, parentB, offspring;
|
||||
|
||||
if(i === this.topUnits) {
|
||||
parentA = winners[0].toJSON();
|
||||
parentB = winners[1].toJSON();
|
||||
offspring = this.crossOver(parentA, parentB);
|
||||
} else if(i < this.maxUnits - 2) {
|
||||
parentA = this.getRandomUnit(winners).toJSON();
|
||||
parentB = this.getRandomUnit(winners).toJSON();
|
||||
offspring = this.crossOver(parentA, parentB);
|
||||
} else {
|
||||
offspring = this.getRandomUnit(winners).toJSON();
|
||||
}
|
||||
|
||||
offspring = this.mutation(offspring);
|
||||
|
||||
const newUnit = synaptic.Network.fromJSON(offspring);
|
||||
newUnit.index = this.population[i].index;
|
||||
newUnit.fitness = 0;
|
||||
newUnit.score = 0;
|
||||
newUnit.isWinner = false;
|
||||
|
||||
this.population[i] = newUnit;
|
||||
}
|
||||
|
||||
if(winners[0].fitness > this.bestFitness) {
|
||||
this.bestPopulation = this.iteration;
|
||||
this.bestFitness = winners[0].fitness;
|
||||
this.bestScore = winners[0].score;
|
||||
}
|
||||
|
||||
this.population.sort((unitA, unitB) => unitA.index - unitB.index);
|
||||
}
|
||||
|
||||
selection() {
|
||||
const sortedPopulation = this.population.sort((unitA, unitB) => unitB.fitness - unitA.fitness);
|
||||
|
||||
for(let i = 0; i < this.topUnits; i++) {
|
||||
this.population[i].isWinner = true;
|
||||
}
|
||||
|
||||
return sortedPopulation.slice(0, this.topUnits);
|
||||
}
|
||||
|
||||
crossOver(parentA, parentB) {
|
||||
const cutPoint = this.random(0, parentA.neurons.length - 19);
|
||||
|
||||
for(let i = cutPoint; i < parentA.neurons.length; i++) {
|
||||
const biasFromParentA = parentA.neurons[i]['bias'];
|
||||
parentA.neurons[i]['bias'] = parentB.neurons[i]['bias'];
|
||||
parentB.neurons[i]['bias'] = biasFromParentA;
|
||||
}
|
||||
|
||||
return this.random(0, 1) === 1 ? parentA : parentB;
|
||||
}
|
||||
|
||||
mutation(offspring) {
|
||||
for(let i = 0; i < offspring.neurons.length; i++) {
|
||||
offspring.neurons[i]['bias'] = this.mutate(offspring.neurons[i]['bias']);
|
||||
}
|
||||
|
||||
for(let i = 0; i < offspring.connections.length; i++) {
|
||||
offspring.connections[i]['weight'] = this.mutate(offspring.connections[i]['weight']);
|
||||
}
|
||||
|
||||
return offspring;
|
||||
}
|
||||
|
||||
mutate(gene) {
|
||||
if(Math.random() < this.mutateRate) {
|
||||
gene *= 1 + ((Math.random() - .5) * 3 + (Math.random() - .5));
|
||||
}
|
||||
|
||||
return gene;
|
||||
}
|
||||
|
||||
random(min, max) {
|
||||
return Math.floor(Math.random() * (max - min + 1) + min);
|
||||
}
|
||||
|
||||
getRandomUnit(array) {
|
||||
return array[this.random(0, array.length - 1)];
|
||||
}
|
||||
|
||||
normalize(value, max) {
|
||||
if(value < -max)
|
||||
value = -max;
|
||||
else if(value > max)
|
||||
value = max;
|
||||
|
||||
return value / max;
|
||||
}
|
||||
}
|
104
Tile.js
Normal file
|
@ -0,0 +1,104 @@
|
|||
class Tile {
|
||||
constructor(game, element, x, y) {
|
||||
this.game = game;
|
||||
this.element = element;
|
||||
this.pos = {x: x, y: y};
|
||||
this.claimedBy = null;
|
||||
this.object = null;
|
||||
|
||||
this.addClickListener();
|
||||
}
|
||||
|
||||
addClickListener() {
|
||||
this.element.addEventListener('click', () => this.clickHandler());
|
||||
}
|
||||
|
||||
clickHandler() {
|
||||
// Checks whether there is an object selected at the moment and if it belongs to the human player
|
||||
if (!!this.game.selectedObject && this.game.selectedObject.civilization.player === player) {
|
||||
// Checks whether this tile is a possible destination for the object based on the object type
|
||||
if (this.game.selectedObject instanceof Warrior) {
|
||||
this.game.selectedObject.move(this.pos.x, this.pos.y);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.game.selectedObject.type === 'new') {
|
||||
const obj = this.game.selectedObject;
|
||||
const allowMoveAfterCreation = this.claimedBy === obj.civilization && this.object === null;
|
||||
const result = this.game[obj.function](this.pos.x, this.pos.y, obj.civilization, obj.class);
|
||||
if (!result) {
|
||||
this.game.deselectObject();
|
||||
return;
|
||||
}
|
||||
if (!allowMoveAfterCreation) {
|
||||
this.object.setNeedsAction(false);
|
||||
}
|
||||
} else {
|
||||
this.object = this.game.selectedObject;
|
||||
}
|
||||
|
||||
this.object.x = this.pos.x;
|
||||
this.object.y = this.pos.y;
|
||||
|
||||
this.game.deselectObject();
|
||||
|
||||
this.object.update();
|
||||
} else if(this.object instanceof Warrior) {
|
||||
this.object.onClick();
|
||||
}
|
||||
|
||||
this.game.selectCivilization(this.claimedBy);
|
||||
}
|
||||
|
||||
getAdjacentTiles() {
|
||||
const surroundings = [
|
||||
[-1, 0],
|
||||
[0, -1],
|
||||
[0, 1],
|
||||
[1, 0],
|
||||
];
|
||||
|
||||
if (this.pos.y % 2 === 0) {
|
||||
surroundings.push([-1, -1], [-1, 1]);
|
||||
} else {
|
||||
surroundings.push([1, -1], [1, 1]);
|
||||
}
|
||||
|
||||
const adjacentTiles = [];
|
||||
surroundings.forEach(modification => {
|
||||
const x = this.pos.x + modification[0];
|
||||
const y = this.pos.y + modification[1];
|
||||
if (x >= 0 && x < this.game.fieldSize.x && y >= 0 && y < this.game.fieldSize.y)
|
||||
adjacentTiles.push(this.game.field[x][y]);
|
||||
});
|
||||
|
||||
return adjacentTiles;
|
||||
}
|
||||
|
||||
getAdjacentCivilizationTiles(civilization, excluded = []) {
|
||||
const surroundings = this.getAdjacentTiles().filter(tile => !excluded.includes(tile));
|
||||
const matches = [];
|
||||
surroundings.forEach(tile => {
|
||||
if(!!tile.claimedBy && tile.claimedBy.uuid === civilization.uuid) {
|
||||
matches.push(tile);
|
||||
matches.push(...tile.getAdjacentCivilizationTiles(civilization, [this, ...surroundings, ...matches, ...excluded]));
|
||||
}
|
||||
});
|
||||
return matches;
|
||||
}
|
||||
|
||||
getAvailableAdjacentTiles() {
|
||||
return this.getAdjacentTiles().filter(tile => !tile.object);
|
||||
}
|
||||
|
||||
overtake(civilization) {
|
||||
const prevColor = this.getColor();
|
||||
this.claimedBy = civilization;
|
||||
this.element.childNodes[0].classList.remove(prevColor);
|
||||
this.element.childNodes[0].classList.add(this.getColor());
|
||||
}
|
||||
|
||||
getColor() {
|
||||
return this.claimedBy != null ? this.claimedBy.color : 'grey';
|
||||
}
|
||||
}
|
247
Warrior.js
Normal file
|
@ -0,0 +1,247 @@
|
|||
class Warrior extends GameElement {
|
||||
constructor(x, y, game, civilization, asset) {
|
||||
super(x, y, game, asset, false);
|
||||
|
||||
this.civilization = civilization;
|
||||
this.isSelected = false;
|
||||
|
||||
this.civilization.balance -= this.constructor.initialCosts;
|
||||
|
||||
this.setNeedsAction(true);
|
||||
}
|
||||
|
||||
onClick() {
|
||||
if (this.civilization.player !== player)
|
||||
return;
|
||||
|
||||
this.game.selectCivilization(this.civilization);
|
||||
|
||||
if (!this.game.selectedObject && this.needsAction) {
|
||||
this.isSelected = true;
|
||||
this.game.selectWarrior(this);
|
||||
|
||||
this.game.draggingElement = this.constructor.getDraggingObject(this.game);
|
||||
}
|
||||
}
|
||||
|
||||
static getDraggingObject(game, className = 'farmer', image = 'knight.svg') {
|
||||
return super.getDraggingObject(game, className, image);
|
||||
}
|
||||
|
||||
getPossibleTiles(player) {
|
||||
return this.checkSurroundings(this.x, this.y, player);
|
||||
}
|
||||
|
||||
merge(warrior) {
|
||||
this.rank += warrior.rank || 1;
|
||||
this.rank = Math.min(this.rank, 4);
|
||||
|
||||
warrior.kill();
|
||||
this.kill();
|
||||
|
||||
const ranks = [Farmer, Hoplite, Servant, Knight];
|
||||
|
||||
this.civilization.balance += warrior.constructor.initialCosts + this.constructor.initialCosts;
|
||||
this.game.addWarrior(this.x, this.y, this.civilization, ranks[this.rank - 1]);
|
||||
// TODO: Switch image
|
||||
}
|
||||
|
||||
move(x, y) {
|
||||
if (!this.needsAction)
|
||||
return false;
|
||||
|
||||
if (!this.isPossibleDestination(x, y, this.civilization.player))
|
||||
return false;
|
||||
|
||||
this.game.field[this.x][this.y].object = null;
|
||||
const tile = this.game.field[x][y];
|
||||
|
||||
if (!tile.claimedBy || tile.claimedBy.player === this.civilization.player) { // Friendly territory
|
||||
if (tile.object instanceof Tree) { // Tree
|
||||
tile.object.cutDown();
|
||||
} else if (tile.object instanceof Warrior) { // Obstacle is a friendly warrior
|
||||
tile.object.merge(this);
|
||||
this.game.deselectObject();
|
||||
return true;
|
||||
}
|
||||
} else { // Hostile territory
|
||||
// Check surrounding tiles
|
||||
if(tile.getAdjacentTiles().find(adjTile => adjTile.claimedBy !== this.civilization &&
|
||||
!!adjTile.object &&
|
||||
(adjTile.object instanceof Warrior || adjTile.object instanceof Tower) &&
|
||||
adjTile.object.rank >= this.rank)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tile.object instanceof Tree) { // No object/troop on tile
|
||||
tile.object.cutDown();
|
||||
} else if (tile.object instanceof Warrior) { // Obstacle is an hostile warrior
|
||||
if (tile.object.rank < this.rank) { // Hostile warrior/tower is weaker than own troop
|
||||
tile.object.kill();
|
||||
}
|
||||
} else if (tile.object instanceof Building) {
|
||||
if (tile.object.rank < this.rank) {
|
||||
tile.object.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.game.deselectObject();
|
||||
this.setNeedsAction(false);
|
||||
|
||||
const prevCiv = tile.claimedBy;
|
||||
|
||||
tile.object = this;
|
||||
tile.overtake(this.civilization);
|
||||
|
||||
if(prevCiv instanceof Civilization && prevCiv !== this.civilization) {
|
||||
console.log(prevCiv.uuid);
|
||||
prevCiv.split();
|
||||
}
|
||||
|
||||
this.update();
|
||||
}
|
||||
|
||||
checkSurroundings(x, y, player, step = 0, excluded = []) {
|
||||
const field = this.game.field;
|
||||
const possibleSurroundings = [];
|
||||
const neighbours = field[x][y].getAdjacentTiles().filter(tile => !excluded.includes(tile));
|
||||
|
||||
neighbours.forEach(neighbour => {
|
||||
const {x, y} = neighbour.pos;
|
||||
|
||||
if (x < 0 || x >= this.game.fieldSize.x || y < 0 || y >= this.game.fieldSize.y)
|
||||
return;
|
||||
|
||||
if (this.isPossibleTile(x, y)) {
|
||||
possibleSurroundings.push(field[x][y]);
|
||||
|
||||
if (step < 4 && !!field[x][y].claimedBy && field[x][y].claimedBy.player === player)
|
||||
possibleSurroundings.push(...this.checkSurroundings(x, y, player, ++step, [...excluded, ...neighbours, ...possibleSurroundings]));
|
||||
}
|
||||
});
|
||||
|
||||
return possibleSurroundings;
|
||||
}
|
||||
|
||||
isPossibleTile(x, y) {
|
||||
const tile = this.game.field[x][y];
|
||||
if (!!tile.claimedBy && tile.claimedBy.player === this.civilization.player) { // Friendly territory
|
||||
if (!tile.object || tile.object instanceof Tree) { // No object/troop on tile
|
||||
return true;
|
||||
}
|
||||
|
||||
if (tile.object instanceof Warrior) { // Obstacle is a friendly warrior
|
||||
if (tile.object.rank <= 4 - this.rank) { // Warrior isn't yet on max level
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise there is an unavoidable obstacle
|
||||
} else { // Hostile territory
|
||||
if (!tile.object || tile.object instanceof Tree) { // No object/troop on tile
|
||||
// TODO: Check surrounding tiles
|
||||
return true;
|
||||
}
|
||||
|
||||
if (tile.object instanceof Warrior || tile.object instanceof Building) { // Obstacle is an hostile warrior
|
||||
if (tile.object.rank < this.rank) { // Hostile warrior/tower is weaker than own troop
|
||||
// TODO: Check surrounding tiles
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise there is an unavoidable obstacle
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
isPossibleDestination(x, y, player) {
|
||||
return this.getPossibleTiles(player).includes(this.game.field[x][y]);
|
||||
}
|
||||
|
||||
kill() {
|
||||
delete this.game.warriors[this.uuid];
|
||||
this.element.parentNode.removeChild(this.element);
|
||||
this.game.field[this.x][this.y].object = null;
|
||||
|
||||
// TODO: Show gravestone
|
||||
}
|
||||
|
||||
setNeedsAction(needsAction) {
|
||||
this.needsAction = needsAction;
|
||||
|
||||
if (this.civilization.player === player && needsAction) {
|
||||
this.element.classList.add('waiting');
|
||||
} else {
|
||||
this.element.classList.remove('waiting');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Farmer extends Warrior {
|
||||
constructor(x, y, game, civilization) {
|
||||
super(x, y, game, civilization, 'farmer.svg');
|
||||
this.rank = 1;
|
||||
this.costsPerRound = 2;
|
||||
}
|
||||
|
||||
static getDraggingObject(game) {
|
||||
return super.getDraggingObject(game, 'farmer', 'farmer.svg');
|
||||
}
|
||||
|
||||
static get initialCosts() {
|
||||
return 10;
|
||||
}
|
||||
}
|
||||
|
||||
class Hoplite extends Warrior {
|
||||
constructor(x, y, game, civilization) {
|
||||
super(x, y, game, civilization, 'hoplite.svg');
|
||||
this.rank = 2;
|
||||
this.costsPerRound = 6;
|
||||
}
|
||||
|
||||
static getDraggingObject(game) {
|
||||
return super.getDraggingObject(game, 'hoplite', 'hoplite.svg');
|
||||
}
|
||||
|
||||
static get initialCosts() {
|
||||
return 20;
|
||||
}
|
||||
}
|
||||
|
||||
class Servant extends Warrior {
|
||||
constructor(x, y, game, civilization) {
|
||||
super(x, y, game, civilization, '');
|
||||
this.rank = 3;
|
||||
this.costsPerRound = 18;
|
||||
}
|
||||
|
||||
static getDraggingObject(game) {
|
||||
return super.getDraggingObject(game, 'servant', 'servant.svg');
|
||||
}
|
||||
|
||||
static get initialCosts() {
|
||||
return 30;
|
||||
}
|
||||
}
|
||||
|
||||
class Knight extends Warrior {
|
||||
constructor(x, y, game, civilization) {
|
||||
super(x, y, game, civilization, 'knight.svg');
|
||||
this.rank = 4;
|
||||
this.costsPerRound = 36;
|
||||
}
|
||||
|
||||
static getDraggingObject(game) {
|
||||
return super.getDraggingObject(game, 'knight', 'knight.svg');
|
||||
}
|
||||
|
||||
static get initialCosts() {
|
||||
return 40;
|
||||
}
|
||||
}
|
1
assets/farm.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg height="512pt" viewBox="0 -46 512 512" width="512pt" xmlns="http://www.w3.org/2000/svg"><path d="m120 180c-66.167969 0-120 53.832031-120 120s53.832031 120 120 120 120-53.832031 120-120-53.832031-120-120-120zm0 210c-49.625 0-90-40.375-90-90s40.375-90 90-90 90 40.375 90 90-40.375 90-90 90zm0 0"/><path d="m120 255c-24.8125 0-45 20.1875-45 45s20.1875 45 45 45 45-20.1875 45-45-20.1875-45-45-45zm0 60c-8.269531 0-15-6.730469-15-15s6.730469-15 15-15 15 6.730469 15 15-6.730469 15-15 15zm0 0"/><path d="m482 262.980469v-52.980469c0-33.085938-26.914062-60-60-60h-30v-75c0-8.269531 6.730469-15 15-15v-30c-24.8125 0-45 20.1875-45 45v75h-65.839844l-36-120h24.839844v-30h-240v30h30v95.738281c-25.347656 6.554688-49.171875 18.578125-69.550781 35.410157l19.101562 23.132812c26.769531-22.105469 60.667969-34.28125 95.449219-34.28125 82.710938 0 150 67.289062 150 150v45h63.257812c7.164063 42.511719 44.226563 75 88.742188 75 49.625 0 90-40.375 90-90 0-26.59375-11.597656-50.527344-30-67.019531zm-377-142.34375v-90.636719h123.839844l36 120h-45.457032c-28.5-18.945312-62.671874-30-99.382812-30-5.019531 0-10.023438.21875-15 .636719zm149.039062 59.363281h167.960938c16.542969 0 30 13.457031 30 30v35.148438c-9.386719-3.328126-19.484375-5.148438-30-5.148438h-132.296875c-7.925781-22.347656-20.140625-42.679688-35.664063-60zm43.445313 90h57.496094c-11.113281 12.398438-18.839844 27.886719-21.722657 45h-33.257812v-15c0-10.21875-.867188-20.238281-2.515625-30zm124.515625 120c-33.085938 0-60-26.914062-60-60s26.914062-60 60-60 60 26.914062 60 60-26.914062 60-60 60zm0 0"/><path d="m437 330c0 8.285156-6.714844 15-15 15s-15-6.714844-15-15 6.714844-15 15-15 15 6.714844 15 15zm0 0"/></svg>
|
After Width: | Height: | Size: 1.6 KiB |
69
assets/farmer.svg
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M293.872,30.38C290.012,13,274.514,0,255.972,0s-34.04,13-37.901,30.38c-22.677,3.461-37.948,9.925-37.948,17.337
|
||||
c-0.001,7.164,14.269,13.446,35.706,16.982c0.2,22.003,18.091,39.78,40.141,39.78c22.05,0,39.942-17.777,40.141-39.78
|
||||
c21.438-3.537,35.707-9.818,35.707-16.982C331.819,40.307,316.549,33.841,293.872,30.38z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path d="M203.644,117.277c-25.349,0-45.675,20.742-45.675,45.76v142.325c0.026,10.684,8.865,19.327,19.543,19.327
|
||||
c0.016,0,0.032,0,0.048,0c10.7-0.026,19.18-8.721,19.155-19.42c0-0.004,0-142.338,0-142.338c0-5.523,7.949-5.234,7.949,0v3.904
|
||||
h0.001h15.24v-49.558H203.644z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path d="M237.08,117.277v49.558h37.784v-49.558C266.058,117.277,257.069,117.277,237.08,117.277z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path d="M308.299,117.277h-16.262v49.558h15.241v-3.898c0-5.285,8.064-5.504,8.006,0.101v142.231
|
||||
c-0.026,10.699,8.398,19.394,19.098,19.42c0.016,0,0.031,0,0.048,0c10.676-0.001,19.575-8.644,19.601-19.327V163.036
|
||||
C354.03,138.024,333.651,117.277,308.299,117.277z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path d="M307.279,184.009H204.664l0.009,304.743c0,12.839,10.408,23.247,23.247,23.247c12.839,0,23.247-10.408,23.247-23.247
|
||||
v-185.82h10.038v185.82c0,12.839,10.408,23.247,23.247,23.247c12.839,0,23.247-10.408,23.247-23.247
|
||||
C307.7,275.444,307.279,237.237,307.279,184.009z M272.788,253.617h-33.634c-6.323,0-11.45-5.126-11.45-11.45V208.84
|
||||
c0-1.513,1.227-2.74,2.74-2.74h51.053c1.513,0,2.74,1.227,2.74,2.74v33.328h0C284.238,248.49,279.112,253.617,272.788,253.617z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.0 KiB |
82
assets/hoplite.svg
Normal file
|
@ -0,0 +1,82 @@
|
|||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M279.632,331.013V487.6c0,13.476,10.923,24.4,24.4,24.4s24.4-10.924,24.4-24.4V359.974
|
||||
C309.412,355.221,292.549,344.971,279.632,331.013z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path d="M363.372,136.481c-4.182-21.952-23.437-38.045-45.783-38.307c-39.957-0.47-47.914-0.507-91.123,0
|
||||
c-17.957,0.211-34.496,10.879-42.135,27.131l-26.454,56.279l-40.179-9.891V84.199h6.738c3.154,0,5.446-3.006,4.615-6.047
|
||||
l-16.82-61.537c-1.278-4.677-7.95-4.684-9.231,0l-16.82,61.537c-0.832,3.044,1.463,6.047,4.615,6.047h6.738v82.83
|
||||
c-9.76-0.78-18.926,5.573-21.342,15.388c-2.684,10.904,3.98,21.919,14.884,24.604l6.458,1.59v293.309
|
||||
c0,5.568,4.513,10.081,10.081,10.081s10.081-4.513,10.081-10.081V213.573l46.635,11.48c9.421,2.318,19.147-2.339,23.262-11.094
|
||||
l32.702-70.515c0,189.055,0,99.771,0,344.156c0,13.476,10.924,24.4,24.4,24.4c13.476,0,24.4-10.924,24.4-24.4V317.465
|
||||
c-10.363-15.997-15.898-34.6-15.898-53.837c0-54.762,44.559-99.302,99.302-99.302c5.6,0,11.091,0.474,16.44,1.368L363.372,136.481
|
||||
z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<circle cx="274.365" cy="42.141" r="42.141"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path d="M378.403,270.181L378.403,270.181c-2.308,9.15-9.335,16.417-18.342,19.071v58.201
|
||||
c40.788-3.633,73.185-36.347,76.339-77.272H378.403z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path d="M326.596,270.181h-57.998c3.154,40.924,35.552,73.638,76.34,77.272v-58.201
|
||||
C335.932,286.597,328.904,279.331,326.596,270.181z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path d="M360.062,179.803v58.201h-0.001c8.333,2.456,14.968,8.863,17.742,17.055h58.445
|
||||
C432.205,215.077,400.179,183.377,360.062,179.803z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path d="M268.751,255.059h58.445c2.773-8.192,9.409-14.599,17.742-17.055v-58.201
|
||||
C304.821,183.377,272.794,215.077,268.751,255.059z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
53
assets/knight.svg
Normal file
|
@ -0,0 +1,53 @@
|
|||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 496 496" style="enable-background:new 0 0 496 496;" xml:space="preserve">
|
||||
<g>
|
||||
<path d="M76,312c0,17.648,14.352,32,32,32s32-14.352,32-32v-32H76V312z M92,296h32v16c0,8.824-7.176,16-16,16
|
||||
c-8.824,0-16-7.176-16-16V296z"/>
|
||||
<path d="M266.656,220.44l-13.272-8.928c-0.032,0.04-2.6,3.712-8.088,7.888l9.688,12.736
|
||||
C262.624,226.32,266.264,221.024,266.656,220.44z"/>
|
||||
<path d="M204,248c14.272,0,27.32-2.824,38.784-8.392l-7-14.392C226.52,229.72,215.832,232,204,232
|
||||
c-34.832,0-48.808-19.656-49.384-20.496l-13.272,8.928C142.096,221.56,160.216,248,204,248z"/>
|
||||
<path d="M452,192h-43.312L344,256.688l-1.376-1.376c-4.648-4.648-11.08-7.312-17.656-7.312c-2.528,0-4.912,0.488-7.216,1.192
|
||||
l-18.04-41.232c-6.368-14.552-20.76-23.96-36.64-23.96H249.16c10.368-8.376,18.32-19.568,22.792-32.4
|
||||
C287.72,149.632,300,136.296,300,120V96c0-52.936-43.064-96-96-96c-52.936,0-96,43.064-96,96v24
|
||||
c0,16.296,12.28,29.632,28.056,31.6c4.472,12.832,12.424,24.024,22.792,32.4h-15.072c-15.24,0-28.952,8.472-35.776,22.112
|
||||
l-14.2,28.392c-1.552,0.544-3.064,1.184-4.536,1.92l-32,16C49.088,256.512,44,264.736,44,273.888V312c0,35.288,28.712,64,64,64
|
||||
c5.576,0,10.96-0.792,16.128-2.136l-9.864,19.72l4.08,4.08c1.144,1.152,8.536,7.752,29.024,12.712l-6.448,32.232l-20.296,10.152
|
||||
C112.832,456.64,108,464.464,108,473.168C108,485.76,118.24,496,130.832,496h36.048c11.408,0,21.304-8.12,23.528-19.296
|
||||
l12.144-60.728c0.504,0,0.944,0.024,1.448,0.024c0.496,0,0.944-0.024,1.44-0.024l12.144,60.728
|
||||
C219.816,487.88,229.712,496,241.12,496h36.048C289.76,496,300,485.76,300,473.168c0-8.704-4.832-16.528-12.624-20.424
|
||||
l-20.296-10.152l-6.336-31.688c24.392-4.952,35.024-11.8,35.696-12.248l5.4-3.6l-19.936-49.84L308,371.312l11.688-11.688
|
||||
c14.672-1.712,26.232-13.272,27.944-27.944l0.368-0.368l5.376,5.376c4.648,4.648,11.08,7.312,17.656,7.312
|
||||
C384.8,344,396,332.8,396,319.032c0-6.672-2.592-12.944-7.312-17.656L387.312,300L452,235.312V192z M275.696,133.936
|
||||
c0.168-1.96,0.304-3.928,0.304-5.936v-21.776c4.76,2.776,8,7.88,8,13.776C284,126.016,280.624,131.208,275.696,133.936z M124,120
|
||||
c0-5.896,3.24-11,8-13.776V128c0,2.008,0.136,3.976,0.304,5.936C127.376,131.208,124,126.016,124,120z M133.24,88.752
|
||||
c-3.248,0.704-6.256,1.992-9.056,3.6C126.112,49.936,161.12,16,204,16s77.888,33.936,79.816,76.352
|
||||
c-2.8-1.608-5.808-2.896-9.056-3.6C271.664,79.072,262.688,72,252,72c-3.024,0-5.856-1.176-8-3.312l-16-16l-16,16
|
||||
c-2.144,2.136-4.976,3.312-8,3.312h-48C145.312,72,136.336,79.072,133.24,88.752z M148,128V96c0-4.408,3.584-8,8-8h48
|
||||
c7.288,0,14.152-2.84,19.312-8L228,75.312L232.688,80c5.168,5.16,12.024,8,19.312,8c4.416,0,8,3.592,8,8v32
|
||||
c0,30.88-25.128,56-56,56S148,158.88,148,128z M122.312,213.264C126.408,205.08,134.624,200,143.776,200H180v-4.2
|
||||
c7.52,2.672,15.576,4.2,24,4.2s16.48-1.528,24-4.2v4.2h35.072c9.528,0,18.168,5.648,21.992,14.376l6.384,14.6
|
||||
C281.776,237.464,248.008,264,204,264c-12.816,0-25.872-2.336-38.824-6.824c-1.848-1.888-3.984-3.528-6.44-4.752l-32-16
|
||||
c-4.368-2.176-9.12-3.536-13.968-4.072L122.312,213.264z M268,296l13.144,31.552l-4.504,4.504l-8.64-21.6V296z M180,304h-8
|
||||
v-28.128c10.68,2.68,21.392,4.128,32,4.128c17.824,0,34.008-3.776,48-9.048V304h-24H180z M212,320v16h-16v-16H212z M108,360
|
||||
c-26.472,0-48-21.528-48-48v-38.112c0-3.048,1.696-5.792,4.424-7.152l32-16C100,248.944,104,248,108,248s8,0.944,11.576,2.736
|
||||
l32,16c2.728,1.36,4.424,4.104,4.424,7.152V312C156,338.472,134.472,360,108,360z M174.728,473.568
|
||||
c-0.744,3.728-4.04,6.432-7.848,6.432h-36.048c-3.76,0-6.832-3.064-6.832-6.832c0-2.608,1.448-4.952,3.784-6.112l27.296-13.648
|
||||
L157.76,440h23.68L174.728,473.568z M184.64,424h-23.68l2.128-10.632c6.736,0.976,14.528,1.704,23.256,2.16L184.64,424z
|
||||
M223.36,424l-1.696-8.472c8.552-0.424,16.344-1.112,23.28-2.016L247.04,424H223.36z M280.216,467.056
|
||||
c2.336,1.16,3.784,3.504,3.784,6.112c0,3.768-3.072,6.832-6.832,6.832H241.12c-3.808,0-7.104-2.704-7.84-6.432L226.56,440h23.68
|
||||
l2.68,13.408L280.216,467.056z M204,400c-41.32,0-61.424-6.872-69.616-10.864l13.632-27.264
|
||||
c12.632-10.16,21.312-25,23.432-41.872H180v32h48v-32h26.584l27.248,68.12C271.656,392.384,247.416,400,204,400z
|
||||
M276.496,274.808l-5.256-12.624c11.832-6.352,21.008-13.064,26.904-17.896l6.336,14.472c-0.624,0.904-1.12,1.888-1.632,2.872
|
||||
L276.496,274.808z M282.672,289.608l18.552-9.272c1.184,3.832,3.176,7.376,6.088,10.288l5.376,5.376l-19.312,19.312
|
||||
L282.672,289.608z M308,348.688L295.312,336L324,307.312L336.688,320L308,348.688z M371.032,328c-2.36,0-4.672-0.96-6.344-2.624
|
||||
l-46.064-46.064c-1.696-1.696-2.624-3.944-2.624-6.344c0-4.944,4.024-8.968,8.968-8.968c2.36,0,4.672,0.96,6.344,2.624
|
||||
l46.064,46.064c1.696,1.696,2.624,3.944,2.624,6.344C380,323.976,375.976,328,371.032,328z M436,228.688l-60,60L355.312,268
|
||||
l60-60H436V228.688z"/>
|
||||
<rect x="228" y="104" width="16" height="16"/>
|
||||
<rect x="164" y="104" width="16" height="16"/>
|
||||
<rect x="196" y="152" width="16" height="16"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 5.1 KiB |
1
assets/servant.svg
Normal file
After Width: | Height: | Size: 6.9 KiB |
1
assets/tower_big.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg height="464pt" viewBox="-136 0 464 464" width="464pt" xmlns="http://www.w3.org/2000/svg"><path d="m192 208h-192l31.777344 73.121094-24.945313 182.878906h178.328125l-24.9375-182.878906zm-112 131.3125 16-16 16 16v25.375l-16-16-16 16zm0 48 16-16 16 16v25.375l-16-16-16 16zm32-70.625-16-16-16 16v-28.6875h32zm-48-28.6875v44.6875l-15.871094-15.878906-5.832031 5.574218 4.6875-34.382812zm-25.175781 59.847656 9.054687-8.664062 16.121094 16.128906v25.375l-15.960938-15.960938-13.304687 13.105469zm-7.632813 55.9375 16.769532-16.511718 16.039062 16.039062v25.375l-15.335938-15.335938-21 16.3125zm16.144532 30.863282 13.351562 13.351562h-30.542969zm32.664062.664062 16-16 16 16v12.6875h-32zm63.335938-21.960938-15.335938 15.335938v-25.375l16.039062-16.039062 16.769532 16.511718 3.527344 25.871094zm18.519531 34.648438h-30.542969l13.351562-13.351562zm-17.894531-83.273438-15.960938 15.960938v-25.375l16.128906-16.121094 9.054688 8.664063 4.089844 29.984375zm-.089844-47.917968-15.871094 15.878906v-44.6875h17.015625l4.6875 34.382812zm-23.871094-76.808594h16v16h-16zm-32 0h16v16h-16zm-32 0h16v16h-16zm0 0"/><path d="m168 144h-16v-64h-16v-32h-32v-48h-16v48h-32v32h-16v64h-16v48h144zm-64-48v16h-16v-16zm32 16h-16v-16h16zm-32 16v16h-16v-16zm16 0h16v16h-16zm-48-32v16h-16v-16zm-16 32h16v16h-16zm0 0"/></svg>
|
After Width: | Height: | Size: 1.3 KiB |
54
assets/tower_small.svg
Normal file
|
@ -0,0 +1,54 @@
|
|||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 440.96 440.96" style="enable-background:new 0 0 440.96 440.96;" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M350.32,367.92c2.065-3.953,3.162-8.34,3.2-12.8c0-15.773-12.787-28.56-28.56-28.56h-9.92l-36.8-153.92
|
||||
c15.661-0.859,27.661-14.251,26.802-29.912c-0.69-12.578-9.582-23.198-21.842-26.088l32-43.68c0.247-0.381,0.461-0.783,0.64-1.2
|
||||
V71.2c0.354-0.812,0.57-1.677,0.64-2.56V8c0-4.418-3.582-8-8-8H278c-4.418,0-8,3.582-8,8v16h-21.12V8c0-4.418-3.582-8-8-8h-36.4
|
||||
c-4.418,0-8,3.582-8,8v16h-20.96V8c0-4.418-3.582-8-8-8H136.8c-4.418,0-8,3.582-8,8v60.16c0.027,0.932,0.216,1.853,0.56,2.72v0.56
|
||||
c0.179,0.417,0.393,0.819,0.64,1.2l32,43.68c-15.267,3.598-24.726,18.891-21.128,34.158c2.89,12.26,13.51,21.153,26.088,21.842
|
||||
l-36.8,154.08h-9.6c-15.773,0-28.56,12.787-28.56,28.56c0.026,4.366,1.067,8.666,3.04,12.56h-1.6c-8.837,0-16,7.163-16,16v41.44
|
||||
c0,8.837,7.163,16,16,16h254.08c8.837,0,16-7.163,16-16v-41.2C363.562,375.971,357.989,369.283,350.32,367.92z M144.8,15.92h14.72
|
||||
v16c0,4.418,3.582,8,8,8h36.96c4.418,0,8-3.582,8-8v-16h20.24v16c0,4.418,3.582,8,8,8h37.04c4.418,0,8-3.582,8-8v-16h14.72v44.16
|
||||
H144.8V15.92z M152.8,76.08h140.16l-28.88,39.44h-82.64L152.8,76.08z M156.482,144.323c-0.001-0.054-0.002-0.108-0.002-0.163
|
||||
l0.48-0.08c0-6.937,5.623-12.56,12.56-12.56h106.96c6.937-0.005,12.564,5.615,12.569,12.551
|
||||
c0.005,6.937-5.615,12.564-12.551,12.569c-0.006,0-0.012,0-0.018,0h-107.2C162.344,156.773,156.615,151.258,156.482,144.323z
|
||||
M183.68,172.64h78.16l36.72,153.92h-152L183.68,172.64z M118.557,342.56c0.667-0.053,1.336-0.053,2.003,0h204.4
|
||||
c6.915-0.553,12.968,4.604,13.522,11.518c0.553,6.915-4.604,12.968-11.518,13.522c-0.667,0.053-1.336,0.053-2.003,0h-204.4
|
||||
c-6.915,0.553-12.968-4.604-13.522-11.518C106.485,349.167,111.642,343.113,118.557,342.56z M347.6,425.2H93.36v-41.52H347.6
|
||||
V425.2z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
48
assets/townhall.svg
Normal file
|
@ -0,0 +1,48 @@
|
|||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 492 492" style="enable-background:new 0 0 492 492;" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path style="fill:#010002;" d="M105,209.5v2c0,9.333,3.667,14,11,14h28v164h41v-143h41v143h40v-143h41v143h41v-164h28
|
||||
c3.333,0,6.333-1.5,9-4.5s4-6.167,4-9.5c0-4.667-1.667-8.333-5-11v-1h1l-129-67v-19c6.667,3.333,17.333,3.833,32,1.5
|
||||
s28-1.5,40,2.5v-43c-10.667-4-24-4.667-40-2s-26.667,2-32-2v-2c0-4.667-3.333-7-10-7s-10,2.333-10,7v64l-129,67h1h2h3
|
||||
C107,200.833,104.333,204.167,105,209.5z"/>
|
||||
<polygon style="fill:#010002;" points="133,399.5 103,430.5 389,430.5 359,399.5 "/>
|
||||
<path style="fill:#010002;" d="M359,236.5v153h9l4,6l27,25l10,10h83v-194H359z M420,358.5h-31v-30h31V358.5z M420,297.5h-31v-31
|
||||
h31V297.5z M461,358.5h-20v-30h20V358.5z M461,297.5h-20v-31h20V297.5z"/>
|
||||
<path style="fill:#010002;" d="M124,389.5h9v-153H0v194h83l10-9l27-26L124,389.5z M51,358.5H31v-30h20V358.5z M51,297.5H31v-31h20
|
||||
V297.5z M103,358.5H72v-30h31V358.5z M103,297.5H72v-31h31V297.5z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
104
assets/tree.svg
Normal file
|
@ -0,0 +1,104 @@
|
|||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M309.07,363.622l-10.14-61.019h2.639c2.23,0,4.352-0.982,5.797-2.688c1.445-1.704,2.066-3.958,1.7-6.163l-10.14-61.02
|
||||
h2.636c2.235,0,4.357-0.982,5.801-2.688c1.445-1.704,2.066-3.959,1.7-6.163l-4.405-26.496c-0.008-0.053-0.016-0.104-0.024-0.156
|
||||
l-1.642-9.895c-0.688-4.144-4.607-6.946-8.747-6.257c-4.143,0.687-6.944,4.604-6.256,8.747l1.618,9.749
|
||||
c0.008,0.053,0.016,0.105,0.024,0.158l2.958,17.793H219.41l4.602-27.701c0.688-4.143-2.113-8.058-6.256-8.747
|
||||
c-4.139-0.688-8.058,2.113-8.747,6.257l-6.073,36.548c-0.366,2.204,0.255,4.458,1.7,6.163c1.445,1.704,3.567,2.688,5.801,2.688
|
||||
h2.636l-10.14,61.02c-0.366,2.204,0.255,4.458,1.7,6.163c1.445,1.704,3.567,2.688,5.801,2.688h2.638l-10.139,61.019
|
||||
c-0.366,2.204,0.255,4.458,1.7,6.163s3.567,2.688,5.801,2.688h2.64l-6.387,38.458c-0.688,4.143,2.112,8.059,6.256,8.747
|
||||
c0.422,0.07,0.84,0.104,1.254,0.104c3.652,0,6.874-2.639,7.492-6.359l6.801-40.949h55.019l6.801,40.949
|
||||
c0.617,3.721,3.841,6.359,7.492,6.359c0.414,0,0.832-0.033,1.254-0.104c4.144-0.688,6.944-4.605,6.255-8.747l-6.387-38.458h2.64
|
||||
c2.235,0,4.357-0.982,5.801-2.688S309.436,365.828,309.07,363.622z M219.406,357.264l9.083-54.661h55.021l9.083,54.661H219.406z
|
||||
M219.406,287.395l9.083-54.662h55.021l9.083,54.662H219.406z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path d="M465.202,498.799c-38.997-36.702-97.039-61.282-163.434-69.211c-15.006-1.799-30.405-2.71-45.769-2.71
|
||||
c-83.138,0-161.344,26.887-209.2,71.921c-3.059,2.878-3.205,7.69-0.326,10.749c2.877,3.059,7.69,3.203,10.749,0.326
|
||||
c45.107-42.447,119.417-67.789,198.779-67.789c14.762,0,29.55,0.876,43.961,2.603c62.275,7.438,118.704,31.197,154.816,65.185
|
||||
c1.468,1.382,3.342,2.067,5.211,2.067c2.023,0,4.041-0.803,5.538-2.393C468.406,506.49,468.259,501.677,465.202,498.799z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path d="M252.83,77.193c-20.867-40.39-68.374-66.488-121.028-66.488C59.125,10.705,0,60.167,0,120.963
|
||||
c0,4.199,3.404,7.604,7.604,7.604h75.379c4.199,0,7.604-3.405,7.604-7.604c0-4.199-3.404-7.604-7.604-7.604H15.577
|
||||
C20.344,64.49,70.65,25.913,131.802,25.913c47.028,0,89.231,22.869,107.517,58.261c1.926,3.731,6.516,5.193,10.245,3.266
|
||||
C253.296,85.512,254.757,80.924,252.83,77.193z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path d="M439.803,22.581c-18.572-7.88-38.627-11.875-59.605-11.875c-52.857,0-100.433,26.235-121.208,66.836
|
||||
c-1.913,3.738-0.433,8.32,3.306,10.233c1.109,0.568,2.291,0.836,3.457,0.836c2.764,0,5.429-1.512,6.776-4.142
|
||||
c18.201-35.571,60.463-58.556,107.669-58.556c18.926,0,36.983,3.589,53.667,10.667c3.866,1.64,8.33-0.163,9.969-4.03
|
||||
C445.474,28.684,443.67,24.22,439.803,22.581z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path d="M460.792,33.682c-3.527-2.281-8.234-1.27-10.514,2.256c-2.28,3.526-1.27,8.234,2.257,10.515
|
||||
c25.666,16.598,41.343,40.662,43.89,66.907h-67.408c-4.2,0-7.604,3.405-7.604,7.604s3.404,7.604,7.604,7.604h75.379
|
||||
c4.2,0,7.604-3.405,7.604-7.604C512,86.539,493.336,54.726,460.792,33.682z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path d="M304.01,9.973c-15.183-6.579-31.335-9.914-48.009-9.914c-16.089,0-31.713,3.109-46.439,9.241
|
||||
c-3.877,1.614-5.711,6.066-4.097,9.943c1.614,3.877,6.067,5.711,9.942,4.097c12.864-5.357,26.522-8.073,40.593-8.073
|
||||
c14.581,0,28.7,2.914,41.964,8.66c0.985,0.427,2.009,0.629,3.019,0.629c2.939,0,5.738-1.714,6.981-4.583
|
||||
C309.633,16.12,307.863,11.643,304.01,9.973z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path d="M421.779,130.822c-24.706-24.705-55.963-39.988-88.016-43.033c-29.914-2.841-57.23,5.244-77.77,22.833
|
||||
c-43.781-37.391-116.465-29.111-165.778,20.2c-17.571,17.571-30.579,38.789-37.619,61.359c-1.25,4.009,0.986,8.273,4.995,9.523
|
||||
c4.016,1.251,8.272-0.986,9.524-4.995c6.31-20.231,18.017-39.295,33.856-55.134c43.238-43.24,106.087-51.537,144.013-20.353
|
||||
l-164.37,164.37c-14.501-17.602-20.922-40.984-18.15-66.694c0.449-4.175-2.57-7.925-6.745-8.375
|
||||
c-4.189-0.452-7.924,2.57-8.375,6.745c-3.581,33.226,6.233,63.311,27.637,84.712c1.484,1.484,3.431,2.227,5.376,2.227
|
||||
c1.946,0,3.892-0.742,5.378-2.227l170.266-170.266L368.183,243.9c2.083,2.083,5.182,2.776,7.953,1.777l22.48-8.093l-7.701,23.101
|
||||
c-0.912,2.733-0.202,5.745,1.835,7.782l33.513,33.512c1.427,1.425,3.36,2.227,5.378,2.227s3.95-0.801,5.378-2.227
|
||||
C480.005,258.992,473.168,182.21,421.779,130.822z M431.379,285.588l-24.552-24.552l11.171-33.51
|
||||
c0.921-2.764,0.182-5.81-1.901-7.845c-2.086-2.035-5.149-2.701-7.888-1.714l-32.686,11.768L267.011,121.22
|
||||
c17.266-14.214,40.161-20.682,65.314-18.292c28.54,2.711,56.49,16.436,78.7,38.646
|
||||
C454.265,184.815,462.562,247.663,431.379,285.588z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.8 KiB |
20
index.html
Normal file
|
@ -0,0 +1,20 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Antiyoy</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<script src="synaptic.min.js"></script>
|
||||
|
||||
<script src="NeuralNetwork.js"></script>
|
||||
<script src="GameElement.js"></script>
|
||||
<script src="Civilization.js"></script>
|
||||
<script src="CivilizationAI.js"></script>
|
||||
<script src="Tile.js"></script>
|
||||
<script src="Game.js"></script>
|
||||
<script src="Warrior.js"></script>
|
||||
<script src="main.js"></script>
|
||||
</body>
|
||||
</html>
|
28
index.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
const brain = require('brain.js');
|
||||
|
||||
const net = new brain.NeuralNetwork();
|
||||
|
||||
const out = net.train([
|
||||
{
|
||||
input:
|
||||
{x: 0, y: 0.5, isFriendly: 1, isAccessible: 1, danger: 0, canAttack: 0, rank: .5, isTree: 0},
|
||||
// {x: 0.25, y: 0.5, isFriendly: 1, isAccessible: 1, danger: 0, canAttack: 0, rank: .5, isTree: 0},
|
||||
// {x: 0.75, y: 0.5, isFriendly: 1, isAccessible: 1, danger: 0, canAttack: 0, rank: .5, isTree: 0},
|
||||
// {x: 1, y: 0.5, isFriendly: 1, isAccessible: 1, danger: 0, canAttack: 0, rank: .5, isTree: 0},
|
||||
// ], output: 0
|
||||
output: {x: 0, y: 0.5}
|
||||
}
|
||||
]);
|
||||
|
||||
console.log(out);
|
||||
console.log('test');
|
||||
console.log(net.toJSON());
|
||||
|
||||
const test = net.run([
|
||||
{x: 0, y: 0.5, isFriendly: 1, isAccessible: 1, danger: 0, canAttack: 0, rank: .5, isTree: 0},
|
||||
{x: 0.25, y: 0.5, isFriendly: 1, isAccessible: 1, danger: 0, canAttack: 0, rank: .5, isTree: 0},
|
||||
{x: 0.75, y: 0.5, isFriendly: 1, isAccessible: 1, danger: 0, canAttack: 0, rank: .5, isTree: 0},
|
||||
{x: 1, y: 0.5, isFriendly: 1, isAccessible: 1, danger: 0, canAttack: 0, rank: .5, isTree: 0},
|
||||
]);
|
||||
|
||||
console.log(test);
|
196
main.js
Normal file
|
@ -0,0 +1,196 @@
|
|||
const body = document.querySelector('body');
|
||||
|
||||
let prevMouseEvent;
|
||||
let timeout;
|
||||
|
||||
Node.prototype.createChild = function(tagName, classNames = null, text = null, src = null) {
|
||||
const element = document.createElement(tagName);
|
||||
|
||||
if(!!classNames)
|
||||
if(classNames instanceof Array)
|
||||
element.classList.add(...classNames);
|
||||
else
|
||||
element.classList.add(classNames);
|
||||
|
||||
if(!!text)
|
||||
element.innerText = text;
|
||||
|
||||
if(!!src)
|
||||
element.src = src;
|
||||
|
||||
this.appendChild(element);
|
||||
return element;
|
||||
};
|
||||
|
||||
function createExpandBtn(parent, items) {
|
||||
const container = parent.createChild('div', ['expandBtnContainer', 'expandBtnContainer' + items.length, 'disabled']);
|
||||
const expandBtn = container.createChild('div', 'expandBtn');
|
||||
expandBtn.addEventListener('click', () => {
|
||||
container.classList.toggle('isActive');
|
||||
}, false);
|
||||
expandBtn.createChild('img', null, null, 'assets/' + items[0]);
|
||||
|
||||
const btns = [];
|
||||
items.forEach((item, index) => {
|
||||
const btn = container.createChild('div', 'btn' + (index + 1));
|
||||
btn.createChild('img', null, null, 'assets/' + item);
|
||||
btns.push(btn);
|
||||
});
|
||||
|
||||
return [container, ...btns];
|
||||
}
|
||||
|
||||
function createTroopsBtn(parent) {
|
||||
const btn = createExpandBtn(parent, ['farmer.svg', 'hoplite.svg', 'servant.svg', 'knight.svg']);
|
||||
btn[0].classList.add('btn-troops');
|
||||
return btn;
|
||||
}
|
||||
|
||||
function createTowersBtn(parent) {
|
||||
const btn = createExpandBtn(parent, ['farm.svg', 'tower_small.svg', 'tower_big.svg']);
|
||||
btn[0].classList.add('btn-buildings');
|
||||
return btn;
|
||||
}
|
||||
|
||||
class Building extends GameElement {
|
||||
constructor(x, y, game, civilization, asset) {
|
||||
super(x, y, game, asset);
|
||||
|
||||
this.civilization = civilization;
|
||||
this.rank = 0;
|
||||
}
|
||||
|
||||
adjustPosition() {
|
||||
const tile = this.game.field[this.x][this.y].element;
|
||||
this.displayWidth = Math.round(this.game.hexagonSize.width * .9);
|
||||
let offsetX = tile.offsetLeft;
|
||||
let offsetY = tile.offsetTop;
|
||||
|
||||
if (this.y % 2 !== 0)
|
||||
offsetX += Math.round(this.displayWidth * .5);
|
||||
|
||||
this.element.style.width = this.displayWidth + 'px';
|
||||
this.element.style.left = offsetX + 'px';
|
||||
this.element.style.top = offsetY + 'px';
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.game.field[this.x][this.y].object = null;
|
||||
this.element.parentNode.removeChild(this.element);
|
||||
}
|
||||
}
|
||||
|
||||
class Farm extends Building {
|
||||
constructor(x, y, game, civilization) {
|
||||
super(x, y, game, civilization, 'farm.svg');
|
||||
}
|
||||
|
||||
static getDraggingObject(game) {
|
||||
return super.getDraggingObject(game, 'farm', 'farm.svg');
|
||||
}
|
||||
|
||||
static get initialCosts() {
|
||||
return 10; // TODO: dynamic price
|
||||
}
|
||||
}
|
||||
|
||||
class Tower extends Building {
|
||||
constructor(x, y, game, civilization, asset) {
|
||||
super(x, y, game, civilization, asset);
|
||||
}
|
||||
}
|
||||
|
||||
class SmallTower extends Tower {
|
||||
constructor(x, y, game, civilization) {
|
||||
super(x, y, game, civilization, 'tower_small.svg');
|
||||
}
|
||||
|
||||
static getDraggingObject(game) {
|
||||
return super.getDraggingObject(game, 'towerSmall', 'tower_small.svg');
|
||||
}
|
||||
|
||||
static get initialCosts() {
|
||||
return 20;
|
||||
}
|
||||
}
|
||||
|
||||
class BigTower extends Tower {
|
||||
constructor(x, y, game, civilization) {
|
||||
super(x, y, game, civilization, 'tower_big.svg');
|
||||
}
|
||||
|
||||
static getDraggingObject(game) {
|
||||
return super.getDraggingObject(game, 'towerBig', 'tower_big.svg');
|
||||
}
|
||||
|
||||
static get initialCosts() {
|
||||
return 30;
|
||||
}
|
||||
}
|
||||
|
||||
class Townhall extends Building {
|
||||
constructor(civilization, game) {
|
||||
super(civilization.origin.x, civilization.origin.y, game, civilization, 'townhall.svg');
|
||||
}
|
||||
|
||||
destroy() {
|
||||
super.destroy();
|
||||
// TODO: New townhall
|
||||
console.log('hallo');
|
||||
this.civilization.townhall = null;
|
||||
this.civilization.split();
|
||||
}
|
||||
}
|
||||
|
||||
class Tree extends GameElement {
|
||||
constructor(x, y, game) {
|
||||
super(x, y, game, 'tree.svg');
|
||||
}
|
||||
|
||||
breed() {
|
||||
if(Math.random() < .25) {
|
||||
const tiles = this.game.field[this.x][this.y].getAvailableAdjacentTiles();
|
||||
if(tiles.length === 0)
|
||||
return;
|
||||
|
||||
const index = Math.floor(Math.random() * (tiles.length - 1));
|
||||
const {x, y} = tiles[index].pos;
|
||||
this.game.addTree(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
cutDown() {
|
||||
delete this.game.trees[this.uuid];
|
||||
this.element.parentNode.removeChild(this.element);
|
||||
}
|
||||
}
|
||||
|
||||
class Player {
|
||||
constructor(color) {
|
||||
this.color = color;
|
||||
this.uuid = guid();
|
||||
}
|
||||
}
|
||||
|
||||
function guid() {
|
||||
function s4() {
|
||||
return Math.floor((1 + Math.random()) * 0x10000)
|
||||
.toString(16)
|
||||
.substring(1);
|
||||
}
|
||||
|
||||
return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
|
||||
}
|
||||
|
||||
const player = new Player('red');
|
||||
// let game = new GameNN();
|
||||
let game = new Game();
|
||||
const civilization = game.addCivilization(player, 2, 4, true);
|
||||
|
||||
// const anotherOne = new Player('yellow');
|
||||
// const another = game.addCivilization(anotherOne, 5, 3);
|
||||
// game.addWarrior(1, 4, civilization, Farmer);
|
||||
// game.addWarrior(4, 2, another, Farmer);
|
||||
//
|
||||
// game.addTree(1,4);
|
||||
// game.addTree(1,5);
|
40
package-lock.json
generated
Normal file
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"name": "antiyoy",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"acorn": {
|
||||
"version": "5.7.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz",
|
||||
"integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw=="
|
||||
},
|
||||
"brain.js": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/brain.js/-/brain.js-1.6.0.tgz",
|
||||
"integrity": "sha512-HMHK7ueavtlONnpa+qDDJNs4K5gegGxQkKir+TeBoXgyHjI84y4vUh44q9qOOv2LwtVlZf1h5gEp676OWnkCbA==",
|
||||
"requires": {
|
||||
"gpu.js": "^1.10.4",
|
||||
"thaw.js": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"gpu.js": {
|
||||
"version": "1.10.4",
|
||||
"resolved": "https://registry.npmjs.org/gpu.js/-/gpu.js-1.10.4.tgz",
|
||||
"integrity": "sha512-qh5O1LXwwkhGOpEicR9SnQB7xduUDY5rKGPWj9yOFLNI90bX+mReooGQjnL3iZ08btBOT0zQGu9wKA+gGy2BhQ==",
|
||||
"requires": {
|
||||
"acorn": "^5.1.1"
|
||||
}
|
||||
},
|
||||
"synaptic": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/synaptic/-/synaptic-1.1.4.tgz",
|
||||
"integrity": "sha512-JFtU98VDFI0uBTowgySo+UUEkc7rSVHaxx9D6dR5BK6u81G7uAxcp32Gi2SHciLpFbvJVd1Pfz7aqSNQO4EH9Q=="
|
||||
},
|
||||
"thaw.js": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/thaw.js/-/thaw.js-2.0.0.tgz",
|
||||
"integrity": "sha1-RSvF1+4s4bb5IDewW1BsvWVikdA="
|
||||
}
|
||||
}
|
||||
}
|
16
package.json
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"name": "antiyoy",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"brain.js": "^1.6.0",
|
||||
"synaptic": "^1.1.4"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
389
style.css
Normal file
|
@ -0,0 +1,389 @@
|
|||
body {
|
||||
background-color: #171d1c;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
font-family: Roboto, Arial, sans-serif;
|
||||
}
|
||||
|
||||
.clear:after {
|
||||
content: "";
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.grey {
|
||||
background-color: #9a9a9a;
|
||||
}
|
||||
|
||||
.red {
|
||||
background-color: #f93943;
|
||||
}
|
||||
|
||||
.yellow {
|
||||
background-color: #fde74c;
|
||||
}
|
||||
|
||||
.blue {
|
||||
background-color: #5fbff9;
|
||||
}
|
||||
|
||||
.green {
|
||||
background-color: #45cb85;
|
||||
}
|
||||
|
||||
.purple {
|
||||
background-color: #5c2751;
|
||||
}
|
||||
|
||||
.text-grey {
|
||||
color: #9a9a9a;
|
||||
}
|
||||
|
||||
.gameContainer {
|
||||
position: relative;
|
||||
width: 80%;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.gameContainer {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.viewContainer {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: calc(100vh - 2 * 84px);
|
||||
}
|
||||
|
||||
.view {
|
||||
position: absolute;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
width: 100%;
|
||||
transition: transform .2s;
|
||||
|
||||
}
|
||||
|
||||
.view.zooming {
|
||||
transition: transform .2s, left .2s, top .2s;
|
||||
}
|
||||
|
||||
.playground {
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.playground > li {
|
||||
list-style-type: none;
|
||||
position: relative;
|
||||
float: left;
|
||||
width: 27.85714285714286%;
|
||||
padding: 0 0 32.16760145166612% 0;
|
||||
-o-transform: rotate(-60deg) skewY(30deg);
|
||||
-moz-transform: rotate(-60deg) skewY(30deg);
|
||||
-webkit-transform: rotate(-60deg) skewY(30deg);
|
||||
-ms-transform: rotate(-60deg) skewY(30deg);
|
||||
transform: rotate(-60deg) skewY(30deg);
|
||||
overflow: hidden;
|
||||
visibility: hidden;
|
||||
margin-right: .33%;
|
||||
}
|
||||
|
||||
.playground > li.even-row {
|
||||
-o-transform: translateX(50%) rotate(-60deg) skewY(30deg);
|
||||
-moz-transform: translateX(50%) rotate(-60deg) skewY(30deg);
|
||||
-webkit-transform: translateX(50%) rotate(-60deg) skewY(30deg);
|
||||
-ms-transform: translateX(50%) rotate(-60deg) skewY(30deg);
|
||||
transform: translateX(50%) rotate(-60deg) skewY(30deg);
|
||||
}
|
||||
|
||||
.playground > li .hexagon {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
-o-transform: skewY(-30deg) rotate(60deg);
|
||||
-moz-transform: skewY(-30deg) rotate(60deg);
|
||||
-webkit-transform: skewY(-30deg) rotate(60deg);
|
||||
-ms-transform: skewY(-30deg) rotate(60deg);
|
||||
transform: skewY(-30deg) rotate(60deg);
|
||||
visibility: visible;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.gameContainer .gameElement {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.gameContainer .townhall {
|
||||
transform: translate(6%, 8%);
|
||||
}
|
||||
|
||||
.gameContainer .tree {
|
||||
transform: translate(8%, 12%) scale(.8);
|
||||
}
|
||||
|
||||
.gameContainer .farmer {
|
||||
transform: translate(5%, 12%);
|
||||
transition: top .5s, left .5s, transform .5s;
|
||||
}
|
||||
|
||||
.gameContainer .hoplite {
|
||||
transform: translate(12%, 10%);
|
||||
transition: top .5s, left .5s, transform .5s;
|
||||
}
|
||||
|
||||
.gameContainer .gameElement.waiting {
|
||||
animation: waiting .7s infinite;
|
||||
transition: margin-top .5s;
|
||||
}
|
||||
|
||||
@keyframes waiting {
|
||||
0% {
|
||||
/*transform: translate(14%, 10%);*/
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
60% {
|
||||
/*transform: translate(14%, 0);*/
|
||||
margin-top: -4px;
|
||||
}
|
||||
|
||||
90% {
|
||||
/*transform: translate(14%, 10%);*/
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.draggingContainer {
|
||||
position: absolute;
|
||||
transform: translateX(-50%);
|
||||
cursor: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.draggingContainer > .dragging {
|
||||
pointer-events: none;
|
||||
-webkit-transform-origin: 50% 5%;
|
||||
-moz-transform-origin: 50% 5%;
|
||||
-ms-transform-origin: 50% 5%;
|
||||
-o-transform-origin: 50% 5%;
|
||||
transform-origin: 50% 5%;
|
||||
transition: transform .25s, width .2s, height .2s;
|
||||
}
|
||||
|
||||
.gameStats, .controls {
|
||||
position: relative;
|
||||
height: 64px;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
.gameStats > .gameStat {
|
||||
position: absolute;
|
||||
color: #fff;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.gameStats > .gameStat.center {
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.gameStats > .gameStat.right {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.gameStats > .gameStat > .count {
|
||||
font-weight: bold;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
font-size: 18px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 8px 16px;
|
||||
cursor: pointer;
|
||||
transition: color .2s, background-color .2s, box-shadow .2s;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
color: #fff;
|
||||
background-color: #5c2751;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
color: #5c2751;
|
||||
background: #fff;
|
||||
-webkit-box-shadow: 0 0 10px #5c2751;
|
||||
-moz-box-shadow: 0 0 10px #5c2751;
|
||||
box-shadow: 0 0 10px #5c2751;
|
||||
}
|
||||
|
||||
.btn-img {
|
||||
padding: 8px;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
background-color: #fff;
|
||||
border-radius: 100%;
|
||||
transform: scale(1.75);
|
||||
transition: background-color .5s, opacity .5s, transform .5s cubic-bezier(.68, -0.55, .27, 1.55), box-shadow .2s;
|
||||
}
|
||||
|
||||
.btn-img:hover {
|
||||
-webkit-box-shadow: 0 0 10px #5c2751;
|
||||
-moz-box-shadow: 0 0 10px #5c2751;
|
||||
box-shadow: 0 0 10px #5c2751;
|
||||
}
|
||||
|
||||
.btn-img.disabled {
|
||||
background-color: #ccc;
|
||||
opacity: .8;
|
||||
box-shadow: none;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.btn-nextRound {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
margin: 12px 0;
|
||||
}
|
||||
|
||||
.expandBtnContainer {
|
||||
position: absolute;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
transition: transform .5s cubic-bezier(.68, -0.55, .27, 1.55);
|
||||
}
|
||||
|
||||
.btn-troops {
|
||||
left: 20px;
|
||||
}
|
||||
|
||||
.btn-buildings {
|
||||
right: 20px;
|
||||
}
|
||||
|
||||
.expandBtnContainer.disabled {
|
||||
transform: scale(.5);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.expandBtnContainer > div,
|
||||
.expandBtnContainer > div,
|
||||
.expandBtnContainer > div,
|
||||
.expandBtnContainer > div,
|
||||
.expandBtnContainer > div{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #fff;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
-webkit-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
transition: all 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
||||
}
|
||||
|
||||
.expandBtnContainer .expandBtn {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.expandBtnContainer > div > img {
|
||||
position: absolute;
|
||||
height: 60%;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.expandBtnContainer .expandBtn:before {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
background: rgba(255, 72, 120, 0.5);
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
-webkit-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
transition: all 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
||||
}
|
||||
|
||||
.expandBtnContainer.isActive .expandBtn:before {
|
||||
-webkit-transform: translate(-50%, -50%) scale(1.4);
|
||||
transform: translate(-50%, -50%) scale(1.4);
|
||||
}
|
||||
|
||||
.expandBtnContainer > div:not(.expandBtn),
|
||||
.expandBtnContainer > div:not(.expandBtn),
|
||||
.expandBtnContainer > div:not(.expandBtn),
|
||||
.expandBtnContainer > div:not(.expandBtn) {
|
||||
width: 75%;
|
||||
height: 75%;
|
||||
background: #19d1e7;
|
||||
transition-delay: 0s;
|
||||
}
|
||||
|
||||
.expandBtnContainer.expandBtnContainer4.isActive .btn1 {
|
||||
top: 0;
|
||||
left: -60%;
|
||||
transition-delay: .1s;
|
||||
}
|
||||
|
||||
.expandBtnContainer.expandBtnContainer4.isActive .btn2 {
|
||||
top: -60%;
|
||||
left: 12.5%;
|
||||
transition-delay: .2s;
|
||||
}
|
||||
|
||||
.expandBtnContainer.expandBtnContainer4.isActive .btn3 {
|
||||
top: -60%;
|
||||
left: 97.5%;
|
||||
transition-delay: .3s;
|
||||
}
|
||||
|
||||
.expandBtnContainer.expandBtnContainer4.isActive .btn4 {
|
||||
top: 0;
|
||||
left: 160%;
|
||||
transition-delay: 0s;
|
||||
}
|
||||
|
||||
.expandBtnContainer.expandBtnContainer3.isActive .btn1 {
|
||||
top: -20%;
|
||||
left: -50%;
|
||||
transition-delay: .1s;
|
||||
}
|
||||
|
||||
.expandBtnContainer.expandBtnContainer3.isActive .btn2 {
|
||||
top: -75%;
|
||||
left: 50%;
|
||||
transition-delay: .2s;
|
||||
}
|
||||
|
||||
.expandBtnContainer.expandBtnContainer3.isActive .btn3 {
|
||||
top: -20%;
|
||||
left: 150%;
|
||||
transition-delay: 0s;
|
||||
}
|