You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

579 lines
16 KiB

2 years ago
function readGameSettings(){
$('.setting').attr('disabled', false);
let fieldSize = parseInt($('#fieldsize_select :selected').val());
let turnTime = parseInt($('#turntime_select :selected').val());
let mixed = $('#mixed_check').is(':checked');
let silent = $('#silent_check').is(':checked');
gameSettings.fieldSize = fieldSize;
gameSettings.turnTime = turnTime;
gameSettings.mixed = mixed;
gameSettings.silent = silent;
if (mixed){
let maxIndex = fieldSize * fieldSize - 1;
let indices = [];
for (let i = 0; i < maxIndex * 0.1; i++){
let index;
do{
2 years ago
index = Math.floor(p.random(0, maxIndex));
2 years ago
} while (indices.find(i => i === index) != null);
indices.push(index);
}
gameSettings.emptyFieldIndices = indices;
}
socket.emit('game-settings', gameSettings);
}
let gameSettings = {};
class Game{
constructor(players){
this.turn = '';
this.playerHues = {'neutral': 0};
this.fields = [];
this.fieldCount = gameSettings.fieldSize;
this.spreads = [];
this.spreadsToRemove = [];
this.bufferedSlots = [];
let i = 0;
for (let x = 0; x < this.fieldCount; x++){
for (let y = 0; y < this.fieldCount; y++){
let isEmpty = false;
if (gameSettings.mixed)
isEmpty = gameSettings.emptyFieldIndices.find(ei => ei === i) != null;
this.fields.push(new Field(x, y, this.fieldCount, isEmpty));
i++;
}
}
this.fields.forEach(f => f.setup(this.fields));
this.leaderboard = new Leaderboard();
this.countdown = new Countdown();
this.fieldMarginFactor = 0.1;
socket.on('player-colors', (lobby, colors) => this.setPlayerHues(colors));
socket.on('current-turn', (lobby, turnId) => this.setTurnPlayer(lobby.clients.filter(c => c.isPlayer), turnId));
socket.on('set-slot', (lobby, fieldsIndex, slotsIndex, id) => this.setSlot(fieldsIndex, slotsIndex, id));
this.players = players;
}
get winCount(){
2 years ago
return Math.round(this.fields.filter(f => !f.isEmpty).length * 0.7);
2 years ago
}
//Width and height of game field
get size(){
2 years ago
return Math.min(p.width, p.height - this.leaderboard.height);
2 years ago
}
//Upper left corner of game field
get pos(){
2 years ago
return {x: (p.width - this.size) / 2, y: (p.height + this.leaderboard.height - this.size) / 2};
2 years ago
}
get hasTurn(){
return this.turn === socket.id
}
get isSpectator(){
return this.players.find(p => p.id === socket.id) == null
}
get data(){
let data = {
pH: this.playerHues,
f: []
}
this.fields.forEach((f, fi) => {
if (!f.isNeutral){
let slots = [];
f.slots.forEach((s, si) => {
if (s.isFilled)
slots.push(si);
});
data.f.push({fi: fi, id: f.ownerId, sl: slots, ie: f.isEmpty});
}
});
return data;
}
get players(){
return this._players;
}
set players(players){
this.leaderboard.setBarsFromPlayers(players);
this._players = players;
this.fields.forEach(f => {
if (players.find(p => p.id === f.ownerId) == null && !f.isNeutral){
f.slots.forEach(s => s.isFilled = false);
f.ownerId = 'neutral';
}
});
}
setPlayerHues(colors){
for (let key in colors){
this.playerHues[key] = colors[key];
2 years ago
let graphics = p.createGraphics(100, 100);
graphics.colorMode(p.HSB);
2 years ago
graphics.tint(colors[key], 100, 100);
graphics.image(gemContentImage, 0, 0, 100, 100);
gemContentGraphics[colors[key]] = graphics;
}
}
display(){
this.fields.forEach(f => f.display());
this.spreads.forEach(s => s.display());
this.leaderboard.display();
this.countdown.display();
}
update(){
this.spreads.forEach(s => s.update());
this.spreads = this.spreads.filter((s, i) => this.spreadsToRemove.find(r => r === i) == null);
if (!this.spreads.length && this.spreadsToRemove.length){
this.sendReady();
}
this.spreadsToRemove = [];
if (this.countdown.isFinished)
this.skipTurn();
if (!this.winId)
this.checkWin();
}
skipTurn(){
if (!this.countdown.isChecked && this.hasTurn && !this.doneTurn){
this.countdown.isChecked = true;
this.doneTurn = true;
socket.emit('set-slot', null, null);
}
}
checkWin(){
this.players.forEach((p, i, a) => {
let count = this.fields.filter(f => f.ownerId === p.id).length;
if (count >= this.winCount || (a.length === 1 && !productionMode)){
console.log('Player ' + p.name + ' (' + p.id + ') won the game!');
this.winId = p.id;
refreshGamePlayers(this.players);
this.countdown.stop();
//$('#play_again').show();
}
});
}
setTurnPlayer(players, turnId){
this.turn = turnId;
this.doneTurn = false;
if (!this.winId)
this.countdown.start();
refreshGamePlayers(players);
}
setSlot(fieldIndex, slotIndex, id){
if (fieldIndex == null || slotIndex == null){
this.sendReady();
return;
}
if (this.spreads.length){
this.bufferedSlots.push({fieldIndex: fieldIndex, slotIndex: slotIndex, id: id});
return;
}
if (id === socket.id)
this.firstTurnMade = true
this.fields.forEach(f => f.slots.forEach(s => s.isHighlighted = false));
let slot = this.fields[fieldIndex].slots[slotIndex];
slot.field.ownerId = id;
slot.isFilled = true;
if (this.players[0])
if (socket.id === this.players[0].id)
socket.emit('game-data', this.data)
if (slot.field.allSlotsFilled){
this.createSpreads(slot.field);
} else {
if (!gameSettings.silent)
slot.isHighlighted = true;
this.sendReady();
}
this.countdown.stop();
}
setAllSlots(data){
data.f.forEach(f => {
this.fields[f.fi].ownerId = f.id;
this.fields[f.fi].isEmpty = f.ie;
this.fields[f.fi].slots.forEach((s, i) => {
if (f.sl.find(si => si === i) != null){
s.isFilled = true;
}
})
});
this.fields.forEach(f => {
if (f.allSlotsFilled)
this.createSpreads(f);
});
}
sendReady(){
if (this.bufferedSlots.length){
let slot = this.bufferedSlots[0];
this.bufferedSlots.splice(0, 1);
this.setSlot(slot.fieldIndex, slot.slotIndex, slot.id);
} else {
let isDead = this.fields.filter(f => f.isMine).length === 0 && this.firstTurnMade;
socket.emit('ready-for-turn', isDead);
}
}
createSpreads(field){
field.slots.forEach(s => {
let spread = new Spread(s);
this.spreads.push(spread);
});
}
onMouseDown(){
this.fields.filter(f => f.isMouseOver).forEach(f => f.onMouseDown());
}
onPlayerLeft(id){
}
}
class Field{
constructor(x, y, count, isEmpty){
this.gridPos = {x: x, y: y};
this.count = count;
this.isEmpty = isEmpty;
this.slots = [];
this.ownerId = 'neutral';
}
get isTop(){
return this.gridPos.y === 0;
}
get isRight(){
return this.gridPos.x === this.count - 1;
}
get isBottom(){
return this.gridPos.y === this.count - 1;
}
get isLeft(){
return this.gridPos.x === 0;
}
get size(){
return game.size / (game.fieldCount + (game.fieldCount + 1) * game.fieldMarginFactor);
}
get margin(){
return game.fieldMarginFactor * this.size;
}
//Upper left corner of field
get pos(){
let x = game.pos.x + this.margin + this.gridPos.x * (this.size + this.margin);
let y = game.pos.y + this.margin + this.gridPos.y * (this.size + this.margin);
return {x: x, y: y};
}
get isMouseOver(){
2 years ago
return p.mouseX > this.pos.x && p.mouseX < this.pos.x + this.size
&& p.mouseY > this.pos.y && p.mouseY < this.pos.y + this.size;
2 years ago
}
get isNeutral(){
return this.ownerId === 'neutral';
}
get isEnemy(){
return this.ownerId !== socket.id && !this.isNeutral;
}
get isMine(){
return this.ownerId === socket.id;
}
get allSlotsFilled(){
return this.slots.find(s => !s.isFilled) == null;
}
setup(allFields){
if (!this.isEmpty){
2 years ago
for (let angle = 0; angle < Math.PI * 2; angle += Math.PI / 2){
let x = Math.round(Math.sin(angle));
let y = Math.round(Math.cos(angle));
2 years ago
if (this.getPartner(x, y, allFields))
this.slots.push(new Slot(x, y));
}
this.slots.forEach(s => s.field = this);
}
}
getPartner(x, y, allFields){
return allFields.find(f =>
f.gridPos.x === this.gridPos.x + x &&
f.gridPos.y === this.gridPos.y + y &&
!f.isEmpty
);
}
display(){
if (!this.isEmpty){
2 years ago
p.noStroke();
p.fill(0, 0, 5);
2 years ago
let size = this.size / 3;
2 years ago
p.rect(this.pos.x + size, this.pos.y + size, size, size);
2 years ago
}
this.slots.forEach(s => s.display());
let s = this.isNeutral ? 0 : 50;
2 years ago
p.stroke(100);
p.strokeWeight(1);
p.fill(game.playerHues[this.ownerId], s, 60, 0.3);
p.rect(this.pos.x, this.pos.y, this.size, this.size);
2 years ago
}
onMouseDown(){
this.slots.filter(s => s.isMouseOver).forEach(s => s.onMouseDown());
}
}
class Slot{
constructor(x, y){
this.gridPos = {x: x, y: y};
this.field = null;
this.isFilled = null;
this.isHighlighted = false;
}
get size(){
return this.field.size / 3;
}
//Upper left corner of slot
get pos(){
let x = this.field.pos.x + (this.gridPos.x + 1) * this.size;
let y = this.field.pos.y + (this.gridPos.y + 1) * this.size;
return {x: x, y: y};
}
get isMouseOver(){
2 years ago
return p.mouseX > this.pos.x && p.mouseX < this.pos.x + this.size
&& p.mouseY > this.pos.y && p.mouseY < this.pos.y + this.size;
2 years ago
}
get color(){
let h = game.playerHues[this.field.ownerId];
let s = this.isFilled ? 100 : 0;
let b = this.isMouseOver && !this.isFilled ? 90 : 70;
2 years ago
return p.color(h, s, b);
2 years ago
}
get partner(){
let fieldIndex = game.fields.findIndex(f =>
f.gridPos.x === this.field.gridPos.x + this.gridPos.x &&
f.gridPos.y === this.field.gridPos.y + this.gridPos.y
);
let slotIndex = game.fields[fieldIndex].slots.findIndex(s =>
s.gridPos.x === -this.gridPos.x &&
s.gridPos.y === -this.gridPos.y
);
return game.fields[fieldIndex].slots[slotIndex];
}
display(){
2 years ago
p.fill(this.color);
p.noStroke();
p.rect(this.pos.x, this.pos.y, this.size, this.size);
2 years ago
if (this.isFilled){
2 years ago
p.stroke(0, 0, 0);
p.strokeWeight(3);
2 years ago
let x = this.pos.x + this.size / 2;
let y = this.pos.y + this.size / 2;
2 years ago
p.line(this.pos.x, y, this.pos.x + this.size, y);
p.line(x, this.pos.y, x, this.pos.y + this.size);
2 years ago
}
if (this.isHighlighted){
2 years ago
p.stroke(0);
p.fill(0);
2 years ago
let hs = this.size / 2;
2 years ago
p.ellipse(this.pos.x + hs, this.pos.y + hs, hs, hs);
2 years ago
}
}
onMouseDown(){
if (!this.field.isEnemy && game.hasTurn && !game.doneTurn && !this.isFilled){
let slotsIndex = this.field.slots.indexOf(this);
let fieldsIndex = game.fields.indexOf(this.field);
socket.emit('set-slot', fieldsIndex, slotsIndex);
game.countdown.stop();
game.doneTurn = true;
}
}
}
class Spread{
constructor(slot){
this.slot = slot;
this.ownerId = slot.field.ownerId;
this.color = slot.color;
this.moveProgress = 0;
this.waitTime = 0;
this.moving = false;
}
get startPos(){
let x = this.slot.field.pos.x + this.slot.size;
let y = this.slot.field.pos.y + this.slot.size;
2 years ago
return p.createVector(x, y);
2 years ago
}
get endPos(){
let x = this.slot.partner.field.pos.x + this.slot.size;
let y = this.slot.partner.field.pos.y + this.slot.size;
2 years ago
return p.createVector(x, y);
2 years ago
}
get pos(){
let step = this.smoothStep(0, 1, this.moveProgress);
return p5.Vector.lerp(this.startPos, this.endPos, step);
}
get size(){
return this.slot.size;
}
display(){
2 years ago
p.noStroke();
2 years ago
let x = this.pos.x + this.size / 2;
let y = this.pos.y + this.size / 2;
2 years ago
p.fill(0, 0, 0);
p.ellipse(x, y, this.size * 0.9, this.size * 0.9);
2 years ago
2 years ago
let c = p.frameCount % 10 >= 5 && !this.moving ? p.color(0, 0, 0) : this.color;
p.fill(c);
p.ellipse(x, y, this.size * 0.5, this.size * 0.5);
2 years ago
}
update(){
2 years ago
this.waitTime += 1 / p.frameRate();
2 years ago
if (this.waitTime >= 0.2 && !this.moving){
this.moving = true;
this.slot.isFilled = false;
this.slot.field.ownerId = 'neutral';
}
if (this.moving){
2 years ago
this.moveProgress += 1.5 / p.frameRate();
2 years ago
}
if (this.moveProgress >= 1)
this.fillEndSlot();
}
fillEndSlot(){
game.spreadsToRemove.push(game.spreads.indexOf(this));
let endSlot = this.slot.partner;
let buffered = false;
if (endSlot.isFilled){
if (endSlot.field.allSlotsFilled){
buffered = true;
} else
endSlot.field.slots.filter(s => !s.isFilled)[0].isFilled = true;
} else {
endSlot.isFilled = true;
}
endSlot.field.ownerId = this.ownerId;
if (endSlot.field.allSlotsFilled && !buffered)
game.createSpreads(endSlot.field);
}
smoothStep(start, end, t){
function clamp(x, lowLimit, upLimit){
if (x < lowLimit)
x = lowLimit;
if (x > upLimit)
x = upLimit;
return x;
}
t = clamp((t - start) / (end - start), 0, 1);
return t * t * t * (t * (t * 6 - 15) + 10);
}
}
class Countdown{
constructor(){
this.limit = new Date().getTime();
}
get time(){
return (this.limit - new Date().getTime()) / 1000;
}
get isFinished(){
return this.time <= 0;
}
display(){
if (!this.isFinished){
2 years ago
let panelW = (p.width - game.size) / 2;
p.stroke(200);
p.fill(150);
p.textSize(panelW / 4);
2 years ago
let x = panelW / 2;
let y = game.pos.y + game.size / 2;
2 years ago
let rounded = Math.floor(this.time);
p.text(rounded, x, y);
2 years ago
}
}
start(){
this.limit = new Date().getTime() + gameSettings.turnTime * 1000;
this.isChecked = false;
}
stop(){
this.limit = 0;
}
}