commit
3906e45f26
27 changed files with 1997 additions and 0 deletions
@ -0,0 +1 @@ |
|||||||
|
.idea |
@ -0,0 +1,81 @@ |
|||||||
|
1.0.0 > - Started versioning |
||||||
|
|
||||||
|
- Released first version of the game |
||||||
|
|
||||||
|
- Still in progress, but already playable with a maximum of 4 players |
||||||
|
|
||||||
|
- You cant join running games, looking forward to add a spectator mode |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
1.0.1 > - Enabled user feedback and made changelog.txt public |
||||||
|
|
||||||
|
- Menues fade more smoothly |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
1.0.2 > - Added tooltip for feedback button when disabled |
||||||
|
|
||||||
|
- Less space for Leaderboard, more space for Gameboard |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
1.1.0 > - Added spectator mode: |
||||||
|
|
||||||
|
- Rooms which games have already been started can be joined as spectator |
||||||
|
|
||||||
|
- Room menu allows to choose between being spectator or player |
||||||
|
|
||||||
|
- Players who lose become spectators |
||||||
|
|
||||||
|
|
||||||
|
- Gameplay: |
||||||
|
|
||||||
|
- Players without fields now lose immediatly instead of having the ability to take new fields at their turn |
||||||
|
|
||||||
|
- Players can now also win by being last man standing, without being owner of the required amount of fields |
||||||
|
|
||||||
|
|
||||||
|
- QoL and Fixes: |
||||||
|
|
||||||
|
- Reloading the page is not needed anymore, every menu state can be left |
||||||
|
|
||||||
|
- Added some tooltips |
||||||
|
|
||||||
|
- If a player wins, the winning chainreact doesn't break but plays till the end for visual satisfaction :) |
||||||
|
|
||||||
|
- Fixed a bug where players couldn't join their own created rooms |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
1.1.1 > - Fixed a game-breaking bug from the last update where players had multiple turns at once |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
1.1.2 > - Fixed a simple bug which resulted in multiple useless log messages |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
1.2.0 > - Added editable settings for a game, currently only including field size |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
1.2.1 > - Added setting for maximum turn time. Countdown is shown at the top left corner while it's a players turn |
||||||
|
|
||||||
|
- Made explosions a bit fancier |
||||||
|
|
||||||
|
- Player or room names have limitations now... |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
1.2.2 > - Added setting to choose if there should be empty fields |
||||||
|
|
||||||
|
- Better explosion animation |
||||||
|
|
||||||
|
- Countdown appearance is clearer |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
1.2.3 > - Name limitations are better now ;) |
||||||
|
|
||||||
|
- Changed names of settings |
After Width: | Height: | Size: 318 B |
After Width: | Height: | Size: 679 B |
After Width: | Height: | Size: 585 B |
After Width: | Height: | Size: 782 B |
@ -0,0 +1,33 @@ |
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
function keyPressed(){
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
function keyReleased(){ |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
function mouseMoved(){ |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
function mouseDragged(){ |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
function mousePressed(){ |
||||||
|
if (game) |
||||||
|
if (!game.winId) |
||||||
|
game.onMouseDown(); |
||||||
|
} |
||||||
|
|
||||||
|
function mouseReleased(){ |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
window.onresize = () => { |
||||||
|
let w = $('#canvas-holder').width(); |
||||||
|
let h = $('#canvas-holder').height(); |
||||||
|
resizeCanvas(w, h); |
||||||
|
} |
@ -0,0 +1,579 @@ |
|||||||
|
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{ |
||||||
|
index = floor(random(0, maxIndex)); |
||||||
|
} 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(){ |
||||||
|
return round(this.fields.filter(f => !f.isEmpty).length * 0.7); |
||||||
|
} |
||||||
|
|
||||||
|
//Width and height of game field
|
||||||
|
get size(){ |
||||||
|
return Math.min(width, height - this.leaderboard.height); |
||||||
|
} |
||||||
|
|
||||||
|
//Upper left corner of game field
|
||||||
|
get pos(){ |
||||||
|
return {x: (width - this.size) / 2, y: (height + this.leaderboard.height - this.size) / 2}; |
||||||
|
} |
||||||
|
|
||||||
|
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]; |
||||||
|
let graphics = createGraphics(100, 100); |
||||||
|
graphics.colorMode(HSB); |
||||||
|
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(){ |
||||||
|
return mouseX > this.pos.x && mouseX < this.pos.x + this.size |
||||||
|
&& mouseY > this.pos.y && mouseY < this.pos.y + this.size; |
||||||
|
} |
||||||
|
|
||||||
|
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){ |
||||||
|
for (let angle = 0; angle < TWO_PI; angle += PI / 2){ |
||||||
|
let x = round(sin(angle)); |
||||||
|
let y = round(cos(angle)); |
||||||
|
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){ |
||||||
|
noStroke(); |
||||||
|
fill(0, 0, 5); |
||||||
|
let size = this.size / 3; |
||||||
|
rect(this.pos.x + size, this.pos.y + size, size, size); |
||||||
|
} |
||||||
|
|
||||||
|
this.slots.forEach(s => s.display()); |
||||||
|
|
||||||
|
let s = this.isNeutral ? 0 : 50; |
||||||
|
stroke(100); |
||||||
|
strokeWeight(1); |
||||||
|
fill(game.playerHues[this.ownerId], s, 60, 0.3); |
||||||
|
rect(this.pos.x, this.pos.y, this.size, this.size); |
||||||
|
} |
||||||
|
|
||||||
|
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(){ |
||||||
|
return mouseX > this.pos.x && mouseX < this.pos.x + this.size |
||||||
|
&& mouseY > this.pos.y && mouseY < this.pos.y + this.size; |
||||||
|
} |
||||||
|
|
||||||
|
get color(){ |
||||||
|
let h = game.playerHues[this.field.ownerId]; |
||||||
|
let s = this.isFilled ? 100 : 0; |
||||||
|
let b = this.isMouseOver && !this.isFilled ? 90 : 70; |
||||||
|
return color(h, s, b) |
||||||
|
} |
||||||
|
|
||||||
|
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(){ |
||||||
|
|
||||||
|
fill(this.color); |
||||||
|
noStroke(); |
||||||
|
rect(this.pos.x, this.pos.y, this.size, this.size); |
||||||
|
|
||||||
|
if (this.isFilled){ |
||||||
|
stroke(0, 0, 0); |
||||||
|
strokeWeight(3); |
||||||
|
let x = this.pos.x + this.size / 2; |
||||||
|
let y = this.pos.y + this.size / 2; |
||||||
|
line(this.pos.x, y, this.pos.x + this.size, y); |
||||||
|
line(x, this.pos.y, x, this.pos.y + this.size); |
||||||
|
} |
||||||
|
|
||||||
|
if (this.isHighlighted){ |
||||||
|
stroke(0); |
||||||
|
fill(0); |
||||||
|
let hs = this.size / 2; |
||||||
|
ellipse(this.pos.x + hs, this.pos.y + hs, hs, hs); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
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; |
||||||
|
return createVector(x, y); |
||||||
|
} |
||||||
|
|
||||||
|
get endPos(){ |
||||||
|
let x = this.slot.partner.field.pos.x + this.slot.size; |
||||||
|
let y = this.slot.partner.field.pos.y + this.slot.size; |
||||||
|
return createVector(x, y); |
||||||
|
} |
||||||
|
|
||||||
|
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(){ |
||||||
|
|
||||||
|
noStroke(); |
||||||
|
let x = this.pos.x + this.size / 2; |
||||||
|
let y = this.pos.y + this.size / 2; |
||||||
|
fill(0, 0, 0); |
||||||
|
ellipse(x, y, this.size * 0.9, this.size * 0.9); |
||||||
|
|
||||||
|
let c = frameCount % 10 >= 5 && !this.moving ? color(0, 0, 0) : this.color; |
||||||
|
fill(c); |
||||||
|
ellipse(x, y, this.size * 0.5, this.size * 0.5); |
||||||
|
} |
||||||
|
|
||||||
|
update(){ |
||||||
|
this.waitTime += 1 / frameRate(); |
||||||
|
if (this.waitTime >= 0.2 && !this.moving){ |
||||||
|
this.moving = true; |
||||||
|
this.slot.isFilled = false; |
||||||
|
this.slot.field.ownerId = 'neutral'; |
||||||
|
} |
||||||
|
if (this.moving){ |
||||||
|
this.moveProgress += 1.5 / frameRate(); |
||||||
|
} |
||||||
|
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){ |
||||||
|
let panelW = (width - game.size) / 2; |
||||||
|
stroke(200); |
||||||
|
fill(150); |
||||||
|
textSize(panelW / 4); |
||||||
|
let x = panelW / 2; |
||||||
|
let y = game.pos.y + game.size / 2; |
||||||
|
let rounded = floor(this.time); |
||||||
|
text(rounded, x, y); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
start(){ |
||||||
|
this.limit = new Date().getTime() + gameSettings.turnTime * 1000; |
||||||
|
this.isChecked = false; |
||||||
|
} |
||||||
|
|
||||||
|
stop(){ |
||||||
|
this.limit = 0; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,104 @@ |
|||||||
|
'use strict'; |
||||||
|
|
||||||
|
let projectName = "chainreact"; |
||||||
|
|
||||||
|
let debug = false, |
||||||
|
productionMode = false, |
||||||
|
font, |
||||||
|
localSettings, |
||||||
|
loader; |
||||||
|
|
||||||
|
//Only for online games
|
||||||
|
let socket; |
||||||
|
|
||||||
|
let game; |
||||||
|
let gemContentImage; |
||||||
|
let gemBorderImage; |
||||||
|
let gemContentGraphics = {}; |
||||||
|
|
||||||
|
let antiCacheQuery = '?_=' + new Date().getTime(); |
||||||
|
|
||||||
|
function preload(){ |
||||||
|
localSettings = loadJSON('data/settings/settings.json' + antiCacheQuery, json => { |
||||||
|
console.log('Local settings loaded: ', json); |
||||||
|
}, error => { |
||||||
|
console.log('Local settings failed: ', error); |
||||||
|
}); |
||||||
|
|
||||||
|
font = loadFont('data/styles/Tajawal/Tajawal-Regular.ttf', json => { |
||||||
|
console.log('Local font loaded: ', json); |
||||||
|
}, error => { |
||||||
|
console.log('Local font failed: ', error); |
||||||
|
}); |
||||||
|
|
||||||
|
gemContentImage = loadImage('data/images/gem_content.png', img => { |
||||||
|
console.log('Image loaded: ', img); |
||||||
|
}, error => { |
||||||
|
console.log('Image failed: ' , error); |
||||||
|
}); |
||||||
|
|
||||||
|
gemBorderImage = loadImage('data/images/gem_border.png', img => { |
||||||
|
console.log('Image loaded: ', img); |
||||||
|
}, error => { |
||||||
|
console.log('Image failed: ' , error); |
||||||
|
}); |
||||||
|
|
||||||
|
loadJSON('data/settings/libraries.json' + antiCacheQuery, json => { |
||||||
|
loadScripts(json) |
||||||
|
console.log('BenjoCraeft library scripts loaded: ', json) |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
function setup(){ |
||||||
|
canvasSetup(); |
||||||
|
interfaceSetup(); |
||||||
|
} |
||||||
|
|
||||||
|
function draw(){ |
||||||
|
background(0, 0, 10); |
||||||
|
|
||||||
|
if (game){ |
||||||
|
game.display(); |
||||||
|
game.update(); |
||||||
|
} |
||||||
|
|
||||||
|
if (loader){ |
||||||
|
loader.update(); |
||||||
|
loader.display(); |
||||||
|
} |
||||||
|
|
||||||
|
if (debug) debugInformation(); |
||||||
|
} |
||||||
|
|
||||||
|
function canvasSetup(){ |
||||||
|
setFrameRate(60); |
||||||
|
let w = $('#canvas-holder').width(), |
||||||
|
h = $('#canvas-holder').height(); |
||||||
|
let canvas = createCanvas(w, h); |
||||||
|
canvas.parent('canvas-holder'); |
||||||
|
textFont(font); |
||||||
|
textAlign(CENTER, CENTER); |
||||||
|
imageMode(CENTER); |
||||||
|
colorMode(HSB); |
||||||
|
} |
||||||
|
|
||||||
|
function interfaceSetup(){ |
||||||
|
window.onresize(); |
||||||
|
setInterval(() => window.onresize(), 500); |
||||||
|
$('#version').html(localSettings.project.version); |
||||||
|
$('#start_feedback, #give_feedback').attr('disabled', 'disabled'); |
||||||
|
nameTyped($('#main > input')); |
||||||
|
|
||||||
|
$('#main').fadeIn(menuesFadeTime); |
||||||
|
} |
||||||
|
|
||||||
|
function loadScripts(libs){ |
||||||
|
for (let script in libs){ |
||||||
|
if (libs[script]){ |
||||||
|
let url = '/lib/benjocraeft/' + script + '.js' |
||||||
|
$.getScript(url, () => { |
||||||
|
console.log('Successfully loaded script: ', url) |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,78 @@ |
|||||||
|
class Leaderboard{ |
||||||
|
|
||||||
|
constructor(){ |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
get pos(){ |
||||||
|
return createVector(game.pos.x, 0); |
||||||
|
} |
||||||
|
|
||||||
|
get width(){ |
||||||
|
return game.size; |
||||||
|
} |
||||||
|
|
||||||
|
get height(){ |
||||||
|
let h = 0; |
||||||
|
if (this.bars[0]) |
||||||
|
h = this.bars[0].marginY; |
||||||
|
this.bars.forEach(b => h += b.height + b.marginY); |
||||||
|
return h; |
||||||
|
} |
||||||
|
|
||||||
|
setBarsFromPlayers(players){ |
||||||
|
this.bars = []; |
||||||
|
players.forEach(p => this.bars.push(new Bar(p))); |
||||||
|
} |
||||||
|
|
||||||
|
display(){ |
||||||
|
this.bars.forEach(b => b.display()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
class Bar{ |
||||||
|
|
||||||
|
constructor(player){ |
||||||
|
this.id = player.id; |
||||||
|
} |
||||||
|
|
||||||
|
get marginX(){ |
||||||
|
return game.size / game.winCount; |
||||||
|
} |
||||||
|
get marginY(){ |
||||||
|
return height * 0.005; |
||||||
|
} |
||||||
|
|
||||||
|
get pos(){ |
||||||
|
let lb = game.leaderboard; |
||||||
|
let x = lb.pos.x + this.marginX; |
||||||
|
let y = lb.pos.y + this.marginY + lb.bars.findIndex(b => b.id === this.id) * (this.height + this.marginY); |
||||||
|
return createVector(x, y); |
||||||
|
} |
||||||
|
|
||||||
|
get height(){ |
||||||
|
return 40; |
||||||
|
} |
||||||
|
|
||||||
|
get width(){ |
||||||
|
return game.leaderboard.width - this.marginX * 2; |
||||||
|
} |
||||||
|
|
||||||
|
display(){ |
||||||
|
let count = game.winCount; |
||||||
|
let imageSize = Math.min(this.height, this.width / count * 0.85); |
||||||
|
let y = this.pos.y + this.height / 2; |
||||||
|
|
||||||
|
let filled = game.fields.filter(f => f.ownerId === this.id).length; |
||||||
|
for (let i = 0; i < count; i++){ |
||||||
|
let x = i * this.width / count + this.width / count / 2 + this.pos.x; |
||||||
|
image(gemBorderImage, x, y, imageSize, imageSize); |
||||||
|
} |
||||||
|
for (let i = 0; i < filled; i++){ |
||||||
|
let x = i * this.width / count + this.width / count / 2 + this.pos.x; |
||||||
|
image(gemContentGraphics[game.playerHues[this.id]], x, y, imageSize, imageSize); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,270 @@ |
|||||||
|
'use strict'; |
||||||
|
|
||||||
|
let playerName; |
||||||
|
function nameTyped(dom){ |
||||||
|
playerName = $(dom).val(); |
||||||
|
let disabled = isValid(playerName, 1, 20) ? false : 'disabled'; |
||||||
|
$('#main > button').attr('disabled', disabled); |
||||||
|
} |
||||||
|
function roomTyped(dom){ |
||||||
|
let roomName = $(dom).val(); |
||||||
|
let disabled = isValid(roomName, 1, 30) ? false : 'disabled'; |
||||||
|
$('#create_room').attr('disabled', disabled); |
||||||
|
} |
||||||
|
function isValid(name, min, max){ |
||||||
|
let regExp = /^\w+(?:[' _-]\w+)*$/; |
||||||
|
return name.length >= min && name.length <= max && regExp.test(name); |
||||||
|
} |
||||||
|
|
||||||
|
let menuesFadeTime = 200; |
||||||
|
let connecting = false; |
||||||
|
function socketConnect(project, name = playerName){ |
||||||
|
if (connecting) |
||||||
|
return; |
||||||
|
connecting = true; |
||||||
|
|
||||||
|
let urlQueries = '?game=' + project.name + '&name=' + name; |
||||||
|
$.get('/php/get_nodejs_port.php', port => { |
||||||
|
let url = 'https://' + location.hostname + ':' + port + urlQueries; |
||||||
|
|
||||||
|
socket = io.connect(url); |
||||||
|
socket.on('connect', () => { |
||||||
|
console.log('Connected to ', url); |
||||||
|
$('#start_feedback, #give_feedback').attr('disabled', false); |
||||||
|
socket.on('disconnect', () => { |
||||||
|
connecting = false; |
||||||
|
console.log('Disconnected from ' + url); |
||||||
|
$('#lobby, #room, #game_room').filter(':visible').fadeOut(menuesFadeTime, () => { |
||||||
|
$('#main').fadeIn(menuesFadeTime); |
||||||
|
$('#start_feedback, #give_feedback').attr('disabled', 'disabled'); |
||||||
|
}); |
||||||
|
}); |
||||||
|
socket.emit('room-list'); |
||||||
|
socket.on('room-list', (roomList) => { |
||||||
|
roomList = roomList.filter(r => r.game === project.name); |
||||||
|
console.log('Received room list: ', roomList); |
||||||
|
$('#room_listings').html(''); |
||||||
|
for (let room of roomList){ |
||||||
|
let button = $('<button></button>'); |
||||||
|
button.html(room.name); |
||||||
|
button.addClass('room_listing'); |
||||||
|
button.on('click', () => socket.emit('join-lobby', room.id)); |
||||||
|
$('#room_listings').append(button); |
||||||
|
} |
||||||
|
}); |
||||||
|
$('#main').fadeOut(menuesFadeTime, () => { |
||||||
|
$('#lobby > h1').html('Welcome, ' + name + '!'); |
||||||
|
$('#lobby > input').val(name + "'s room"); |
||||||
|
$('#lobby').fadeIn(menuesFadeTime); |
||||||
|
roomTyped($('#lobby > input')); |
||||||
|
}); |
||||||
|
socket.on('created-lobby', (room) => { |
||||||
|
console.log('You successfully created a room!'); |
||||||
|
$('#create_room').attr('disabled', false); |
||||||
|
$('#lobby').fadeOut(menuesFadeTime, () => { |
||||||
|
roomSetup(room); |
||||||
|
}); |
||||||
|
}); |
||||||
|
socket.on('client-list', (room, clients) => { |
||||||
|
console.log('Received client list: ', clients); |
||||||
|
|
||||||
|
let players = clients.filter(c => c.isPlayer); |
||||||
|
let spectators = clients.filter(c => c.isSpectator); |
||||||
|
let leader = players[0]; |
||||||
|
|
||||||
|
if (game) |
||||||
|
game.players = players; |
||||||
|
|
||||||
|
setInterfacePlayers(players); |
||||||
|
setInterfaceSpectators(spectators); |
||||||
|
|
||||||
|
function setInterfacePlayers(players){ |
||||||
|
$('#player_listings').html(''); |
||||||
|
for (let c of players){ |
||||||
|
let div = $('<div></div>'); |
||||||
|
let span = $('<span></span>'); |
||||||
|
let ready = $('<button></button>'); |
||||||
|
div.addClass('player_listing'); |
||||||
|
ready.addClass('player_ready'); |
||||||
|
ready.on('click', () => { |
||||||
|
if (c.id !== socket.id) |
||||||
|
return; |
||||||
|
let isReady = $(ready).data('isReady'); |
||||||
|
$(ready).data('isReady', !isReady); |
||||||
|
socket.emit('set-ready', !isReady); |
||||||
|
}); |
||||||
|
span.html(c.name); |
||||||
|
ready.css('background-color', c.isReady ? 'green' : 'transparent'); |
||||||
|
ready.data('isReady', c.isReady); |
||||||
|
div.append(span, ready); |
||||||
|
$('#player_listings').append(div); |
||||||
|
} |
||||||
|
|
||||||
|
let allReady = players.find(c => !c.isReady) == null; |
||||||
|
let startClickable = allReady && players.length <= 4 && (players.length > 1 || productionMode); |
||||||
|
if (startClickable && players[0]) |
||||||
|
startClickable = players[0].id === socket.id; |
||||||
|
$('#start_button').attr('disabled', startClickable ? false : 'disabled'); |
||||||
|
|
||||||
|
if (players.find(p => p.id === socket.id)){ |
||||||
|
$('#player_list .join_type').hide(); |
||||||
|
} else { |
||||||
|
$('#player_list .join_type').show(); |
||||||
|
} |
||||||
|
|
||||||
|
refreshGamePlayers(players); |
||||||
|
} |
||||||
|
|
||||||
|
function setInterfaceSpectators(spectators){ |
||||||
|
$('.spectator_listings').html(''); |
||||||
|
spectators.forEach(s => { |
||||||
|
let div = $('<div></div>'); |
||||||
|
div.addClass('spectator_listing'); |
||||||
|
div.html(s.name); |
||||||
|
|
||||||
|
$('.spectator_listings').append(div); |
||||||
|
}); |
||||||
|
if (spectators.find(p => p.id === socket.id)){ |
||||||
|
$('.spectator_list .join_type').hide(); |
||||||
|
} else { |
||||||
|
$('.spectator_list .join_type').show(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (leader){ |
||||||
|
if (leader.id === socket.id) |
||||||
|
readGameSettings(); |
||||||
|
else |
||||||
|
disableSettings(); |
||||||
|
} else |
||||||
|
disableSettings(); |
||||||
|
|
||||||
|
function disableSettings(){ |
||||||
|
$('.setting').attr('disabled', 'disabled'); |
||||||
|
} |
||||||
|
}); |
||||||
|
socket.on('member-joined', (room, joinedId, joinedName) => { |
||||||
|
if (joinedId === socket.id){ |
||||||
|
console.log('You joined a room!'); |
||||||
|
$('#lobby').fadeOut(menuesFadeTime, () => { |
||||||
|
if (room.hasStarted){ |
||||||
|
$('#game_room').fadeIn(menuesFadeTime); |
||||||
|
} else { |
||||||
|
roomSetup(room); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
else |
||||||
|
console.log('A player joined your room: ' + joinedName + ' (' + joinedId + ')'); |
||||||
|
}); |
||||||
|
socket.on('member-left', (room, leftId, leftName) => { |
||||||
|
console.log('A player left your room: ' + leftName + ' (' + leftId + ')'); |
||||||
|
if (game) |
||||||
|
game.onPlayerLeft(leftId); |
||||||
|
}); |
||||||
|
socket.on('start-game', (room, seed) => startGame(room, seed)); |
||||||
|
socket.on('start-spectate', (room, data, hues, turnId) => spectateGame(room, data, hues, turnId)); |
||||||
|
socket.on('game-settings', (room, settings) => applySettings(settings)); |
||||||
|
socket.on('left-lobby', () => { |
||||||
|
console.log('You left a room!'); |
||||||
|
$('#leave_room').get(0).disabled = false; |
||||||
|
game = null; |
||||||
|
socket.removeAllListeners('player-colors'); |
||||||
|
socket.removeAllListeners('current-turn'); |
||||||
|
socket.removeAllListeners('set-slot'); |
||||||
|
$('#room, #game_room').filter(':visible').fadeOut(menuesFadeTime, () => { |
||||||
|
$('#lobby').fadeIn(menuesFadeTime); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
function applySettings(settings){ |
||||||
|
for (let key in settings) |
||||||
|
gameSettings[key] = settings[key]; |
||||||
|
|
||||||
|
console.log('Applied game settings: ', gameSettings); |
||||||
|
|
||||||
|
$('#fieldsize_select').val(gameSettings.fieldSize); |
||||||
|
$('#turntime_select').val(gameSettings.turnTime); |
||||||
|
$('#mixed_check').prop('checked', gameSettings.mixed); |
||||||
|
$('#silent_check').prop('checked', gameSettings.silent); |
||||||
|
} |
||||||
|
|
||||||
|
function roomSetup(room){ |
||||||
|
$('#room > h1').html(room.name); |
||||||
|
$('#room').fadeIn(menuesFadeTime); |
||||||
|
} |
||||||
|
|
||||||
|
function createRoom(dom){ |
||||||
|
dom.disabled = true; |
||||||
|
let name = $('#lobby > input').val(); |
||||||
|
if (name === '' || name.length > 30) |
||||||
|
dom.disabled = false; |
||||||
|
else |
||||||
|
socket.emit('create-lobby', localSettings, name); |
||||||
|
} |
||||||
|
|
||||||
|
function leaveRoom(dom){ |
||||||
|
dom.disabled = true; |
||||||
|
socket.emit('leave-lobby'); |
||||||
|
} |
||||||
|
|
||||||
|
function serverStartGame(dom){ |
||||||
|
socket.emit('start-game'); |
||||||
|
} |
||||||
|
|
||||||
|
function startGame(room, seed){ |
||||||
|
randomSeed(seed); |
||||||
|
buildGame(room); |
||||||
|
console.log('Game started'); |
||||||
|
socket.emit('ready-for-turn'); |
||||||
|
} |
||||||
|
|
||||||
|
function spectateGame(room, data, hues, turnId){ |
||||||
|
let players = room.clients.filter(c => c.isPlayer); |
||||||
|
buildGame(room); |
||||||
|
if (data) |
||||||
|
game.setAllSlots(data); |
||||||
|
if (hues) |
||||||
|
game.setPlayerHues(hues); |
||||||
|
if (turnId) |
||||||
|
game.setTurnPlayer(players, turnId); |
||||||
|
refreshGamePlayers(players); |
||||||
|
console.log('Started spectating'); |
||||||
|
} |
||||||
|
|
||||||
|
function buildGame(room){ |
||||||
|
$('#room').filter(':visible').fadeOut(menuesFadeTime, () => { |
||||||
|
$('#game_room').fadeIn(menuesFadeTime); |
||||||
|
$('#play_again').hide(); |
||||||
|
}); |
||||||
|
game = new Game(room.clients.filter(c => c.isPlayer)); |
||||||
|
} |
||||||
|
|
||||||
|
function refreshGamePlayers(players){ |
||||||
|
$('#game_players').html(''); |
||||||
|
for (let c of players){ |
||||||
|
let div = $('<div></div>'); |
||||||
|
div.addClass('game_player'); |
||||||
|
div.html(c.name); |
||||||
|
let color = 'hsl(0, 0%, 50%)'; |
||||||
|
if (game){ |
||||||
|
if (game.turn === c.id){ |
||||||
|
color = 'hsl(' + game.playerHues[c.id] + ', 100%, 50%)'; |
||||||
|
} else { |
||||||
|
color = 'hsl(' + game.playerHues[c.id] + ', 10%, 50%)'; |
||||||
|
} |
||||||
|
if (game.winId === c.id){ |
||||||
|
div.effect('pulsate', 5000, () => { |
||||||
|
div.css('background-color', color); |
||||||
|
}); |
||||||
|
} |
||||||
|
}
|
||||||
|
div.css('background-color', color); |
||||||
|
|
||||||
|
$('#game_players').append(div); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,8 @@ |
|||||||
|
{ |
||||||
|
"collision": false, |
||||||
|
"colorPicker": false, |
||||||
|
"cookie": true, |
||||||
|
"loader": true, |
||||||
|
"prototypes": true, |
||||||
|
"technical": true |
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
{ |
||||||
|
"project": { |
||||||
|
"version": "v1.2.3", |
||||||
|
"name": "chainreact", |
||||||
|
"author": "BenjoCraeft", |
||||||
|
"playerCounts": [1, 2, 3, 4], |
||||||
|
"online": { |
||||||
|
"iceServers": [ |
||||||
|
{ |
||||||
|
"urls": "stun:stun.l.google.com:19302" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"urls": "turn:numb.viagenie.ca", |
||||||
|
"credential": "muazkh", |
||||||
|
"username": "webrtc@live.com" |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
|
}, |
||||||
|
"spectators": true, |
||||||
|
"frameWork": { |
||||||
|
"frameRate": 60, |
||||||
|
"width": null, |
||||||
|
"height": null |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,93 @@ |
|||||||
|
Copyright 2018 Boutros International. (https://www.boutrosfonts.com) |
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1. |
||||||
|
This license is copied below, and is also available with a FAQ at: |
||||||
|
https://scripts.sil.org/OFL |
||||||
|
|
||||||
|
|
||||||
|
----------------------------------------------------------- |
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 |
||||||
|
----------------------------------------------------------- |
||||||
|
|
||||||
|
PREAMBLE |
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide |
||||||
|
development of collaborative font projects, to support the font creation |
||||||
|
efforts of academic and linguistic communities, and to provide a free and |
||||||
|
open framework in which fonts may be shared and improved in partnership |
||||||
|
with others. |
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and |
||||||
|
redistributed freely as long as they are not sold by themselves. The |
||||||
|
fonts, including any derivative works, can be bundled, embedded, |
||||||
|
redistributed and/or sold with any software provided that any reserved |
||||||
|
names are not used by derivative works. The fonts and derivatives, |
||||||
|
however, cannot be released under any other type of license. The |
||||||
|
requirement for fonts to remain under this license does not apply |
||||||
|
to any document created using the fonts or their derivatives. |
||||||
|
|
||||||
|
DEFINITIONS |
||||||
|
"Font Software" refers to the set of files released by the Copyright |
||||||
|
Holder(s) under this license and clearly marked as such. This may |
||||||
|
include source files, build scripts and documentation. |
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the |
||||||
|
copyright statement(s). |
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as |
||||||
|
distributed by the Copyright Holder(s). |
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting, |
||||||
|
or substituting -- in part or in whole -- any of the components of the |
||||||
|
Original Version, by changing formats or by porting the Font Software to a |
||||||
|
new environment. |
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical |
||||||
|
writer or other person who contributed to the Font Software. |
||||||
|
|
||||||
|
PERMISSION & CONDITIONS |
||||||
|
Permission is hereby granted, free of charge, to any person obtaining |
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify, |
||||||
|
redistribute, and sell modified and unmodified copies of the Font |
||||||
|
Software, subject to the following conditions: |
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components, |
||||||
|
in Original or Modified Versions, may be sold by itself. |
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled, |
||||||
|
redistributed and/or sold with any software, provided that each copy |
||||||
|
contains the above copyright notice and this license. These can be |
||||||
|
included either as stand-alone text files, human-readable headers or |
||||||
|
in the appropriate machine-readable metadata fields within text or |
||||||
|
binary files as long as those fields can be easily viewed by the user. |
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font |
||||||
|
Name(s) unless explicit written permission is granted by the corresponding |
||||||
|
Copyright Holder. This restriction only applies to the primary font name as |
||||||
|
presented to the users. |
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font |
||||||
|
Software shall not be used to promote, endorse or advertise any |
||||||
|
Modified Version, except to acknowledge the contribution(s) of the |
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written |
||||||
|
permission. |
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole, |
||||||
|
must be distributed entirely under this license, and must not be |
||||||
|
distributed under any other license. The requirement for fonts to |
||||||
|
remain under this license does not apply to any document created |
||||||
|
using the Font Software. |
||||||
|
|
||||||
|
TERMINATION |
||||||
|
This license becomes null and void if any of the above conditions are |
||||||
|
not met. |
||||||
|
|
||||||
|
DISCLAIMER |
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF |
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT |
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE |
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL |
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM |
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE. |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,130 @@ |
|||||||
|
/* CSS Created by CSS CHECKBOX */ |
||||||
|
/**********************************/ |
||||||
|
/**** www.CSScheckbox.com *********/ |
||||||
|
|
||||||
|
/*general styles for all CSS Checkboxes*/ |
||||||
|
label { |
||||||
|
-webkit-touch-callout: none; |
||||||
|
-webkit-user-select: none; |
||||||
|
-khtml-user-select: none; |
||||||
|
-moz-user-select: none; |
||||||
|
-ms-user-select: none; |
||||||
|
user-select: none; |
||||||
|
} |
||||||
|
|
||||||
|
input[type=checkbox].css-checkbox { |
||||||
|
position: absolute; |
||||||
|
overflow: hidden; |
||||||
|
clip: rect(0 0 0 0); |
||||||
|
height:1px; |
||||||
|
width:1px; |
||||||
|
margin:-1px; |
||||||
|
padding:0; |
||||||
|
border:0; |
||||||
|
} |
||||||
|
|
||||||
|
input[type=checkbox].css-checkbox + label.css-label { |
||||||
|
padding-left:20px; |
||||||
|
height:15px; |
||||||
|
display:inline-block; |
||||||
|
line-height:15px; |
||||||
|
background-repeat:no-repeat; |
||||||
|
background-position: 0 0; |
||||||
|
font-size:15px; |
||||||
|
vertical-align:middle; |
||||||
|
cursor:pointer; |
||||||
|
} |
||||||
|
|
||||||
|
input[type=checkbox].css-checkbox:checked + label.css-label { |
||||||
|
background-position: 0 -15px; |
||||||
|
} |
||||||
|
|
||||||
|
.css-label{ |
||||||
|
background-image:url(https://csscheckbox.com/checkboxes/dark-check-green.png); |
||||||
|
} |
||||||
|
|
||||||
|
/*specific classes related to Checkbox skins*/ |
||||||
|
|
||||||
|
.lite-green-check{background-image:url(https://csscheckbox.com/checkboxes/lite-green-check.png);} |
||||||
|
.lite-blue-check{background-image:url(https://csscheckbox.com/checkboxes/lite-blue-check.png);} |
||||||
|
.lite-gray-check{background-image:url(https://csscheckbox.com/checkboxes/lite-gray-check.png);} |
||||||
|
.lite-cyan-check{background-image:url(https://csscheckbox.com/checkboxes/lite-cyan-check.png);} |
||||||
|
.lite-orange-check{background-image:url(https://csscheckbox.com/checkboxes/lite-orange-check.png);} |
||||||
|
.lite-red-check{background-image:url(https://csscheckbox.com/checkboxes/lite-red-check.png);} |
||||||
|
|
||||||
|
.lite-x-cyan{background-image:url(https://csscheckbox.com/checkboxes/lite-x-cyan.png);} |
||||||
|
.lite-x-gray{background-image:url(https://csscheckbox.com/checkboxes/lite-x-gray.png);} |
||||||
|
.lite-x-blue{background-image:url(https://csscheckbox.com/checkboxes/lite-x-blue.png);} |
||||||
|
.lite-x-orange{background-image:url(https://csscheckbox.com/checkboxes/lite-x-orange.png);} |
||||||
|
.lite-x-red{background-image:url(https://csscheckbox.com/checkboxes/lite-x-red.png);} |
||||||
|
.lite-x-green{background-image:url(https://csscheckbox.com/checkboxes/lite-x-green.png);} |
||||||
|
|
||||||
|
.mac-style{background-image:url(https://csscheckbox.com/checkboxes/mac-style.png);} |
||||||
|
.mario-style{background-image:url(https://csscheckbox.com/checkboxes/mario-style.png);} |
||||||
|
.alert-style{background-image:url(https://csscheckbox.com/checkboxes/alert-style.png);} |
||||||
|
.lite-plus{background-image:url(https://csscheckbox.com/checkboxes/lite-plus.png);} |
||||||
|
.dark-plus{background-image:url(https://csscheckbox.com/checkboxes/dark-plus.png);} |
||||||
|
.dark-plus-cyan{background-image:url(https://csscheckbox.com/checkboxes/dark-plus-cyan.png);} |
||||||
|
.dark-plus-orange{background-image:url(https://csscheckbox.com/checkboxes/dark-plus-orange.png);} |
||||||
|
.dark-check-cyan{background-image:url(https://csscheckbox.com/checkboxes/dark-check-cyan.png);} |
||||||
|
.dark-check-green{background-image:url(https://csscheckbox.com/checkboxes/dark-check-green.png);} |
||||||
|
.dark-check-orange{background-image:url(https://csscheckbox.com/checkboxes/dark-check-orange.png);} |
||||||
|
|
||||||
|
|
||||||
|
.depressed-lite-small{background-image:url(https://csscheckbox.com/checkboxes/depressed-lite-small.png);} |
||||||
|
.elegant{background-image:url(https://csscheckbox.com/checkboxes/elegant.png);} |
||||||
|
.depressed{background-image:url(https://csscheckbox.com/checkboxes/depressed.png);} |
||||||
|
.chrome-style{background-image:url(https://csscheckbox.com/checkboxes/chrome-style.png);} |
||||||
|
.web-two-style{background-image:url(https://csscheckbox.com/checkboxes/web-two-style.png);} |
||||||
|
.vlad{background-image:url(https://csscheckbox.com/checkboxes/vlad.png);} |
||||||
|
.klaus{background-image:url(https://csscheckbox.com/checkboxes/klaus.png);} |
||||||
|
|
||||||
|
input[type=checkbox].css-checkbox.med + label.css-label.med { |
||||||
|
padding-left:22px; |
||||||
|
height:17px; |
||||||
|
display:inline-block; |
||||||
|
line-height:17px; |
||||||
|
background-repeat:no-repeat; |
||||||
|
background-position: 0 0; |
||||||
|
font-size:15px; |
||||||
|
vertical-align:middle; |
||||||
|
cursor:pointer; |
||||||
|
} |
||||||
|
|
||||||
|
input[type=checkbox].css-checkbox.med:checked + label.css-label.med { |
||||||
|
|
||||||
|
background-position: 0 -17px; |
||||||
|
} |
||||||
|
input[type=checkbox].css-checkbox.sme + label.css-label.sme { |
||||||
|
padding-left:22px; |
||||||
|
height:16px; |
||||||
|
display:inline-block; |
||||||
|
line-height:16px; |
||||||
|
background-repeat:no-repeat; |
||||||
|
background-position: 0 0; |
||||||
|
font-size:15px; |
||||||
|
vertical-align:middle; |
||||||
|
cursor:pointer; |
||||||
|
} |
||||||
|
|
||||||
|
input[type=checkbox].css-checkbox.sme:checked + label.css-label.sme{ |
||||||
|
|
||||||
|
background-position: 0 -16px; |
||||||
|
} |
||||||
|
input[type=checkbox].css-checkbox.lrg + label.css-label.lrg { |
||||||
|
padding-left:22px; |
||||||
|
height:20px; |
||||||
|
display:inline-block; |
||||||
|
line-height:20px; |
||||||
|
background-repeat:no-repeat; |
||||||
|
background-position: 0 0; |
||||||
|
font-size:15px; |
||||||
|
vertical-align:middle; |
||||||
|
cursor:pointer; |
||||||
|
} |
||||||
|
|
||||||
|
input[type=checkbox].css-checkbox.lrg:checked + label.css-label.lrg{ |
||||||
|
|
||||||
|
background-position: 0 -20px; |
||||||
|
} |
||||||
|
|
@ -0,0 +1,88 @@ |
|||||||
|
#color_picker{ |
||||||
|
width: 300px; |
||||||
|
height: 25%; |
||||||
|
margin: 20px; |
||||||
|
margin-top: 50px; |
||||||
|
border: 5px solid #000; |
||||||
|
background-color: #000; |
||||||
|
-webkit-user-select: none; |
||||||
|
-moz-user-select: none; |
||||||
|
-ms-user-select: none; |
||||||
|
user-select: none; |
||||||
|
position: relative; |
||||||
|
} |
||||||
|
#color_picker_numeric{ |
||||||
|
width: 80%; |
||||||
|
padding: 5%; |
||||||
|
margin: 5%; |
||||||
|
background-color: #888; |
||||||
|
border-radius: 10px; |
||||||
|
overflow: hidden; |
||||||
|
} |
||||||
|
.color_picker_rgb{ |
||||||
|
float: left; |
||||||
|
width: 22%; |
||||||
|
height: 35px; |
||||||
|
font-size: 25px; |
||||||
|
color: #000; |
||||||
|
} |
||||||
|
.color_picker_rgb:nth-child(1){ |
||||||
|
margin-right: 10%; |
||||||
|
margin-left: 3%; |
||||||
|
background-color: #F00; |
||||||
|
|
||||||
|
} |
||||||
|
.color_picker_rgb:nth-child(2){ |
||||||
|
background-color: #0F0; |
||||||
|
} |
||||||
|
.color_picker_rgb:nth-child(3){ |
||||||
|
margin-left: 10%; |
||||||
|
background-color: #00F; |
||||||
|
color: #FFF; |
||||||
|
} |
||||||
|
#color_picker_hex{ |
||||||
|
width: 50%; |
||||||
|
height: 30px; |
||||||
|
font-size: 25px; |
||||||
|
margin: 10% 25% 0 25%; |
||||||
|
} |
||||||
|
#saturation{ |
||||||
|
position: relative; |
||||||
|
width: calc(100% - 33px); |
||||||
|
height: 100%; |
||||||
|
background: linear-gradient(to right, #FFF 0%, #F00 100%); |
||||||
|
float: left; |
||||||
|
margin-right: 6px; |
||||||
|
} |
||||||
|
#value { |
||||||
|
width: 100%; |
||||||
|
height: 100%; |
||||||
|
background: linear-gradient(to top, #000 0%, rgba(255,255,255,0) 100%); |
||||||
|
} |
||||||
|
#sb_picker{ |
||||||
|
border: 2px solid; |
||||||
|
border-color: #FFF; |
||||||
|
position: absolute; |
||||||
|
width: 14px; |
||||||
|
height: 14px; |
||||||
|
border-radius: 10px; |
||||||
|
bottom: 50px; |
||||||
|
left: 50px; |
||||||
|
box-sizing: border-box; |
||||||
|
z-index: 10; |
||||||
|
} |
||||||
|
#hue { |
||||||
|
width: 27px; |
||||||
|
height: 100%; |
||||||
|
position: relative; |
||||||
|
float: left; |
||||||
|
background: linear-gradient(to bottom, #F00 0%, #F0F 17%, #00F 34%, #0FF 50%, #0F0 67%, #FF0 84%, #F00 100%); |
||||||
|
} |
||||||
|
#hue_picker { |
||||||
|
position: absolute; |
||||||
|
background: #000; |
||||||
|
border-bottom: 1px solid #000; |
||||||
|
top: 0; |
||||||
|
width: 27px; |
||||||
|
height: 2px; |
||||||
|
} |
Binary file not shown.
@ -0,0 +1,88 @@ |
|||||||
|
input[type=range] { |
||||||
|
-webkit-appearance: none; |
||||||
|
margin: 18px 0; |
||||||
|
width: 100%; |
||||||
|
} |
||||||
|
input[type=range]:focus { |
||||||
|
outline: none; |
||||||
|
} |
||||||
|
input[type=range]::-webkit-slider-runnable-track { |
||||||
|
width: 100%; |
||||||
|
height: 8.4px; |
||||||
|
cursor: pointer; |
||||||
|
animate: 0.2s; |
||||||
|
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; |
||||||
|
background: #3071a9; |
||||||
|
border-radius: 1.3px; |
||||||
|
border: 0.2px solid #010101; |
||||||
|
} |
||||||
|
input[type=range]::-webkit-slider-thumb { |
||||||
|
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; |
||||||
|
border: 1px solid #000000; |
||||||
|
height: 36px; |
||||||
|
width: 16px; |
||||||
|
border-radius: 3px; |
||||||
|
background: #ffffff; |
||||||
|
cursor: pointer; |
||||||
|
-webkit-appearance: none; |
||||||
|
margin-top: -14px; |
||||||
|
} |
||||||
|
input[type=range]:focus::-webkit-slider-runnable-track { |
||||||
|
background: #367ebd; |
||||||
|
} |
||||||
|
input[type=range]::-moz-range-track { |
||||||
|
width: 100%; |
||||||
|
height: 8.4px; |
||||||
|
cursor: pointer; |
||||||
|
animate: 0.2s; |
||||||
|
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; |
||||||
|
background: #3071a9; |
||||||
|
border-radius: 1.3px; |
||||||
|
border: 0.2px solid #010101; |
||||||
|
} |
||||||
|
input[type=range]::-moz-range-thumb { |
||||||
|
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; |
||||||
|
border: 1px solid #000000; |
||||||
|
height: 36px; |
||||||
|
width: 16px; |
||||||
|
border-radius: 3px; |
||||||
|
background: #ffffff; |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
input[type=range]::-ms-track { |
||||||
|
width: 100%; |
||||||
|
height: 8.4px; |
||||||
|
cursor: pointer; |
||||||
|
animate: 0.2s; |
||||||
|
background: transparent; |
||||||
|
border-color: transparent; |
||||||
|
border-width: 16px 0; |
||||||
|
color: transparent; |
||||||
|
} |
||||||
|
input[type=range]::-ms-fill-lower { |
||||||
|
background: #2a6495; |
||||||
|
border: 0.2px solid #010101; |
||||||
|
border-radius: 2.6px; |
||||||
|
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; |
||||||
|
} |
||||||
|
input[type=range]::-ms-fill-upper { |
||||||
|
background: #3071a9; |
||||||
|
border: 0.2px solid #010101; |
||||||
|
border-radius: 2.6px; |
||||||
|
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; |
||||||
|
} |
||||||
|
input[type=range]::-ms-thumb { |
||||||
|
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; |
||||||
|
border: 1px solid #000000; |
||||||
|
height: 36px; |
||||||
|
width: 16px; |
||||||
|
border-radius: 3px; |
||||||
|
background: #ffffff; |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
input[type=range]:focus::-ms-fill-lower { |
||||||
|
background: #3071a9; |
||||||
|
} |
||||||
|
input[type=range]:focus::-ms-fill-upper { |
||||||
|
background: #367ebd; |
||||||
|
} |
@ -0,0 +1,142 @@ |
|||||||
|
<!-- Web project created by Benjo Craeft (alias) --> |
||||||
|
<!DOCTYPE html> |
||||||
|
<html lang="en"> |
||||||
|
<head> |
||||||
|
<meta charset="utf-8"> |
||||||
|
<script src="/lib/socket.io/socket.io.min.js" type="text/javascript"></script> |
||||||
|
<script src="/lib/socket.io/socket.io-p2p.min.js" type="text/javascript"></script> |
||||||
|
<script src="/lib/p5/p5.min.js" type="text/javascript"></script> |
||||||
|
<script src="/lib/p5/p5.dom.min.js" type="text/javascript"></script> |
||||||
|
<script src="/lib/p5/p5.sound.min.js" type="text/javascript"></script> |
||||||
|
<script src="/lib/jquery/jquery.min.js" type="text/javascript"></script> |
||||||
|
<script src="/lib/jquery/jquery-ui.min.js" type="text/javascript"></script> |
||||||
|
<script src="/lib/vue/vue.js" type="text/javascript"></script> |
||||||
|
<script src="data/scripts/init.js" type="text/javascript"></script> |
||||||
|
<script src="data/scripts/events.js" type="text/javascript"></script> |
||||||
|
<script src="data/scripts/online.js" type="text/javascript"></script> |
||||||
|
<script src="data/scripts/game.js" type="text/javascript"></script> |
||||||
|
<script src="data/scripts/leaderboard.js" type="text/javascript"></script> |
||||||
|
<link href="styles.css" rel="stylesheet"> |
||||||
|
<link href="data/styles/color_picker.css" rel="stylesheet"> |
||||||
|
<link href="data/styles/range_input.css" rel="stylesheet"> |
||||||
|
<link href="data/styles/checkbox.css" rel="stylesheet"> |
||||||
|
<link href="data/images/favicon.ico" rel="icon" type="image/x-icon"> |
||||||
|
<title>Chainreact</title> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<div id="content"> |
||||||
|
<div id="interface"> |
||||||
|
<div id="main" style="display: none;"> |
||||||
|
<h1>Enter your Nickname!</h1> |
||||||
|
<input oninput="nameTyped(this)" type="text"> |
||||||
|
<button class="tooltip" onclick="socketConnect(localSettings.project);"> |
||||||
|
Connect & Play! |
||||||
|
<span class="tooltiptext">Enter a name (max. 20 characters)!</span> |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
<div id="lobby" style="display: none;"> |
||||||
|
<h1>Welcome, Benjo</h1> |
||||||
|
<button class="tooltip" id="create_room" onclick="createRoom(this);"> |
||||||
|
Create room |
||||||
|
<span class="tooltiptext">Enter a room name (Max. 30 characters)!</span> |
||||||
|
</button> |
||||||
|
<input oninput="roomTyped(this);" type="text"> |
||||||
|
<fieldset class="fieldset_container" id="room_list"> |
||||||
|
<legend>Available rooms</legend> |
||||||
|
<div id="room_listings"></div> |
||||||
|
</fieldset> |
||||||
|
<button onclick="socket.disconnect()">Disconnect</button> |
||||||
|
</div> |
||||||
|
<div id="room" style="display: none;"> |
||||||
|
<h1></h1> |
||||||
|
<button class="tooltip" id="start_button" onclick="serverStartGame(this);"> |
||||||
|
Start |
||||||
|
<span class="tooltiptext">Leader can start when all players (2-4) are ready!</span> |
||||||
|
</button> |
||||||
|
<fieldset class="fieldset_container" id="settings_list"> |
||||||
|
<legend>Settings</legend> |
||||||
|
<table id="settings" onchange="readGameSettings()"> |
||||||
|
<tr> |
||||||
|
<td>Turn time:</td> |
||||||
|
<td><select class="setting" id="turntime_select"> |
||||||
|
<option value="5">5s</option> |
||||||
|
<option value="10">10s</option> |
||||||
|
<option value="15">15s</option> |
||||||
|
<option value="20">20s</option> |
||||||
|
<option value="25">25s</option> |
||||||
|
<option value="30">30s</option> |
||||||
|
<option value="35">35s</option> |
||||||
|
<option value="50">40s</option> |
||||||
|
<option value="45">45s</option> |
||||||
|
<option value="50">50s</option> |
||||||
|
<option value="55">55s</option> |
||||||
|
<option value="60">60s</option> |
||||||
|
</select></td> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<td>Field size:</td> |
||||||
|
<td><select class="setting" id="fieldsize_select"> |
||||||
|
<option value="4">4x4</option> |
||||||
|
<option value="5">5x5</option> |
||||||
|
<option value="6">6x6</option> |
||||||
|
<option value="7">7x7</option> |
||||||
|
<option value="8">8x8</option> |
||||||
|
<option value="9">9x9</option> |
||||||
|
</select></td> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<td>Walls:</td> |
||||||
|
<td><input class="setting" id="mixed_check" type="checkbox"/></td> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<td>Silent:</td> |
||||||
|
<td><input class="setting" id="silent_check" type="checkbox"></td> |
||||||
|
</tr> |
||||||
|
</table> |
||||||
|
</fieldset> |
||||||
|
<fieldset class="spectator_list fieldset_container"> |
||||||
|
<legend>Spectators</legend> |
||||||
|
<button class="join_type" onclick="socket.emit('join-spectators');">Join</button> |
||||||
|
<div class="spectator_listings"></div> |
||||||
|
</fieldset> |
||||||
|
<fieldset class="fieldset_container" id="player_list"> |
||||||
|
<legend>Players</legend> |
||||||
|
<button class="join_type" onclick="socket.emit('join-players');">Join</button> |
||||||
|
<div id="player_listings"></div> |
||||||
|
</fieldset> |
||||||
|
<button onclick="leaveRoom(this);">Leave</button> |
||||||
|
</div> |
||||||
|
<div id="game_room" style="display: none;"> |
||||||
|
<fieldset class="spectator_list fieldset_container"> |
||||||
|
<legend>Spectators</legend> |
||||||
|
<div class="spectator_listings"></div> |
||||||
|
</fieldset> |
||||||
|
<fieldset class="fieldset_container" id="game_players_holder"> |
||||||
|
<legend>Players</legend> |
||||||
|
<div id="game_players"></div> |
||||||
|
</fieldset> |
||||||
|
<button id="play_again" onclick="sendPlayAgain(this)">Play again!</button> |
||||||
|
<button id="leave_room" onclick="leaveRoom(this);">Leave</button> |
||||||
|
</div> |
||||||
|
<div id="project_info"> |
||||||
|
<span id="version"></span> |
||||||
|
<a href="./changelog.txt" target="_blank">Changelog</a> |
||||||
|
<button class="tooltip" id="start_feedback" onclick="$('#user_feedback').show();givingFeedback = true;"> |
||||||
|
Feedback |
||||||
|
<span class="tooltiptext">Connect to send feedback!</span> |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
<div id="user_feedback" style="display: none;"> |
||||||
|
<span>Write me feedback about bugs/features!</span> |
||||||
|
<textarea cols="50" rows="10"></textarea> |
||||||
|
<button class="tooltip" id="give_feedback" onclick="$.post('/php/post_feedback.php', {content: $('#user_feedback > textarea').val(), projectName: localSettings.project.name});$('#user_feedback').hide();givingFeedback = false;"> |
||||||
|
Submit |
||||||
|
<span class="tooltiptext">Connect to send feedback!</span> |
||||||
|
</button> |
||||||
|
<button onclick="$('#user_feedback').hide();givingFeedback = false;">Cancel</button> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div id="canvas-holder"></div> |
||||||
|
</div> |
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,276 @@ |
|||||||
|
a:link, a:hover, a:active, a:visited{color: #000;} |
||||||
|
|
||||||
|
html, body{margin: 0; padding: 0; height: 100%; width: 100%;} |
||||||
|
|
||||||
|
canvas{margin: 0; padding: 0; border: none; display: block;} |
||||||
|
|
||||||
|
button:not([disabled]){cursor: pointer;} |
||||||
|
|
||||||
|
@font-face{ |
||||||
|
font-family: "Rametto"; |
||||||
|
src: url("data/styles/font.ttf"); |
||||||
|
} |
||||||
|
|
||||||
|
@font-face{ |
||||||
|
font-family: "Tajawal"; |
||||||
|
src: url("data/styles/Tajawal/Tajawal-Regular.ttf"); |
||||||
|
} |
||||||
|
|
||||||
|
*{ |
||||||
|
font-family: "Tajawal"; |
||||||
|
color: #000; |
||||||
|
font-size: 17px; |
||||||
|
} |
||||||
|
|
||||||
|
:root{ |
||||||
|
--if-width: 300px; |
||||||
|
--if-border-width: 5px; |
||||||
|
--if-padding: 25px; |
||||||
|
--if-content-width: calc(var(--if-width) - var(--if-padding) * 2); |
||||||
|
--if-background-color: rgb(70, 70, 70); |
||||||
|
--button-color: rgb(40, 143, 228); |
||||||
|
} |
||||||
|
|
||||||
|
.tooltip > .tooltiptext { |
||||||
|
visibility: hidden; |
||||||
|
width: 150px; |
||||||
|
background-color: var(--button-color); |
||||||
|
text-align: center; |
||||||
|
padding: 5px; |
||||||
|
border-radius: 5px; |
||||||
|
|
||||||
|
/* Position the tooltip text */ |
||||||
|
position: absolute; |
||||||
|
z-index: 1; |
||||||
|
bottom: 145%; |
||||||
|
left: 50%; |
||||||
|
margin-left: -80px; |
||||||
|
|
||||||
|
/* Fade in tooltip */ |
||||||
|
opacity: 0; |
||||||
|
transition: opacity 0.1s; |
||||||
|
} |
||||||
|
|
||||||
|
.tooltip:hover:disabled > .tooltiptext { |
||||||
|
visibility: visible; |
||||||
|
opacity: 1; |
||||||
|
} |
||||||
|
.tooltip > .tooltiptext::after { |
||||||
|
content: ""; |
||||||
|
position: absolute; |
||||||
|
top: 100%; |
||||||
|
left: 50%; |
||||||
|
margin-left: -8px; |
||||||
|
border-width: 8px; |
||||||
|
border-style: solid; |
||||||
|
border-color: rgb(34, 137, 189) transparent transparent transparent; |
||||||
|
} |
||||||
|
|
||||||
|
#project_info{ |
||||||
|
position: absolute; |
||||||
|
bottom: 0; |
||||||
|
left: 0; |
||||||
|
width: var(--if-width); |
||||||
|
display: flex; |
||||||
|
align-items: baseline; |
||||||
|
} |
||||||
|
#project_info > *{ |
||||||
|
margin: 5px; |
||||||
|
font-size: 20px; |
||||||
|
height: 30px; |
||||||
|
} |
||||||
|
|
||||||
|
#user_feedback{ |
||||||
|
position: absolute; |
||||||
|
top: 0; |
||||||
|
right: 0; |
||||||
|
bottom: 0; |
||||||
|
left: 0; |
||||||
|
width: 100%; |
||||||
|
height: 100%; |
||||||
|
background-color: rgba(83, 83, 83, 0.699); |
||||||
|
text-align: center; |
||||||
|
display: flex; |
||||||
|
flex-direction: column; |
||||||
|
justify-content: center; |
||||||
|
align-items: center; |
||||||
|
} |
||||||
|
#user_feedback > *{ |
||||||
|
width: 50%; |
||||||
|
padding: 5px; |
||||||
|
box-sizing: content-box; |
||||||
|
margin: 5px; |
||||||
|
} |
||||||
|
#user_feedback > span{ |
||||||
|
font-size: 40px; |
||||||
|
margin: 20px; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Standard styles |
||||||
|
*/ |
||||||
|
|
||||||
|
#content{ |
||||||
|
display: grid; |
||||||
|
grid-template-columns: |
||||||
|
calc(var(--if-width) + var(--if-border-width)) |
||||||
|
calc(100% - (var(--if-width) + var(--if-border-width)) * 1) |
||||||
|
/**calc(var(--if-width) + var(--if-border-width)) */ |
||||||
|
; |
||||||
|
grid-template-rows: 100%; |
||||||
|
width: 100%; |
||||||
|
height: 100%; |
||||||
|
} |
||||||
|
|
||||||
|
#interface{ |
||||||
|
background-color: var(--if-background-color); |
||||||
|
border: var(--if-border-width) solid rgb(200, 200, 200); |
||||||
|
} |
||||||
|
|
||||||
|
#p5_loading{ |
||||||
|
display: none; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
Interface |
||||||
|
*/ |
||||||
|
#interface{ |
||||||
|
border-width: 0 var(--if-border-width) 0 0; |
||||||
|
} |
||||||
|
#main, #lobby, #room, #game_room{ |
||||||
|
height: 100%; |
||||||
|
display: flex; |
||||||
|
flex-direction: column; |
||||||
|
justify-content: center; |
||||||
|
align-items: center; |
||||||
|
} |
||||||
|
h1{ |
||||||
|
font-size: 30px; |
||||||
|
margin: 10px; |
||||||
|
width: 80%; |
||||||
|
word-wrap: break-word; |
||||||
|
text-align: center; |
||||||
|
} |
||||||
|
input[type=text]{ |
||||||
|
border: 2px solid; |
||||||
|
padding: 0; |
||||||
|
width: 200px; |
||||||
|
height: 30px; |
||||||
|
font-size: 25px; |
||||||
|
margin: 10px; |
||||||
|
text-align: center; |
||||||
|
} |
||||||
|
button:not(.player_ready){ |
||||||
|
width: 200px; |
||||||
|
height: 40px; |
||||||
|
margin: 10px; |
||||||
|
font-size: 25px; |
||||||
|
background-color: var(--button-color); |
||||||
|
border: 2px solid; |
||||||
|
} |
||||||
|
button:hover:not([disabled]){ |
||||||
|
filter: brightness(150%); |
||||||
|
} |
||||||
|
button:disabled{ |
||||||
|
filter: brightness(70%); |
||||||
|
} |
||||||
|
#interface > div:not(#project_info):not(#user_feedback) > *:last-child{ |
||||||
|
margin-bottom: 45px; |
||||||
|
} |
||||||
|
.fieldset_container{ |
||||||
|
width: 200px; |
||||||
|
height: auto; |
||||||
|
min-height: 100px; |
||||||
|
overflow-y: auto; |
||||||
|
background-color: rgb(50, 50, 50); |
||||||
|
border: 5px solid #000; |
||||||
|
margin-bottom: 10px; |
||||||
|
} |
||||||
|
.fieldset_container > div{ |
||||||
|
display: flex; |
||||||
|
flex-direction: column; |
||||||
|
align-items: center; |
||||||
|
} |
||||||
|
.fieldset_container > div > *{ |
||||||
|
width: 180px; |
||||||
|
height: auto; |
||||||
|
margin: 10px 0; |
||||||
|
word-wrap: break-word; |
||||||
|
} |
||||||
|
legend{ |
||||||
|
font-size: 25px; |
||||||
|
} |
||||||
|
|
||||||
|
table{ |
||||||
|
width: inherit; |
||||||
|
} |
||||||
|
td{ |
||||||
|
font-size: 20px; |
||||||
|
} |
||||||
|
td:nth-child(1){ |
||||||
|
width: 60%; |
||||||
|
} |
||||||
|
td:nth-child(2){ |
||||||
|
width: 40%; |
||||||
|
} |
||||||
|
td > input, td > select{ |
||||||
|
border-radius: 2px; |
||||||
|
border: 1px solid #000; |
||||||
|
background-color: rgb(95, 95, 95); |
||||||
|
width: 100%; |
||||||
|
} |
||||||
|
|
||||||
|
#lobby > input{ |
||||||
|
margin-bottom: 50px; |
||||||
|
} |
||||||
|
.player_listing{ |
||||||
|
display: flex; |
||||||
|
flex-direction: row; |
||||||
|
align-items: center; |
||||||
|
justify-content: flex-end; |
||||||
|
background-color: rgb(80, 80, 80); |
||||||
|
border-radius: 5px; |
||||||
|
} |
||||||
|
.player_listing > span{ |
||||||
|
margin-right: 15px; |
||||||
|
font-size: 30px; |
||||||
|
text-align: center; |
||||||
|
word-wrap: break-word; |
||||||
|
width: 126px; |
||||||
|
} |
||||||
|
.player_ready{ |
||||||
|
margin-right: 5px; |
||||||
|
width: 30px; |
||||||
|
height: 30px; |
||||||
|
background-color: none; |
||||||
|
border-radius: 4px; |
||||||
|
border: 2px solid #000; |
||||||
|
} |
||||||
|
|
||||||
|
.spectator_listing{ |
||||||
|
text-align: center; |
||||||
|
font-size: 30px; |
||||||
|
border-radius: 5px; |
||||||
|
background-color: rgb(100, 100, 100); |
||||||
|
} |
||||||
|
|
||||||
|
#game_players_holder{ |
||||||
|
height: auto; |
||||||
|
overflow-y: hidden; |
||||||
|
} |
||||||
|
.game_player{ |
||||||
|
font-size: 35px; |
||||||
|
text-align: center; |
||||||
|
width: 180px; |
||||||
|
word-wrap: wrap; |
||||||
|
border-radius: 5px; |
||||||
|
padding: 10px; |
||||||
|
} |
||||||
|
|
||||||
|
.join_type{ |
||||||
|
width: 100%; |
||||||
|
margin-bottom: 5px; |
||||||
|
} |
Loading…
Reference in new issue