2021-05-17 02:32:33 +02:00
|
|
|
import Geometry, { Point, Rect } from './Geometry'
|
2021-05-17 00:27:47 +02:00
|
|
|
import Protocol from './Protocol'
|
2021-05-29 17:58:05 +02:00
|
|
|
import { Rng } from './Rng'
|
2021-05-17 00:27:47 +02:00
|
|
|
import Time from './Time'
|
2021-05-29 17:58:05 +02:00
|
|
|
import {
|
|
|
|
|
Change,
|
|
|
|
|
EncodedPiece,
|
|
|
|
|
EvtInfo,
|
|
|
|
|
Game,
|
|
|
|
|
Input,
|
|
|
|
|
Piece,
|
|
|
|
|
PieceChange,
|
|
|
|
|
Player,
|
|
|
|
|
PlayerChange,
|
2021-05-29 15:36:03 +02:00
|
|
|
Puzzle,
|
2021-05-29 17:58:05 +02:00
|
|
|
PuzzleData,
|
|
|
|
|
PuzzleDataChange,
|
2021-05-29 15:36:03 +02:00
|
|
|
ScoreMode,
|
2021-05-29 17:58:05 +02:00
|
|
|
Timestamp
|
|
|
|
|
} from './Types'
|
|
|
|
|
import Util from './Util'
|
2021-04-27 21:43:53 +02:00
|
|
|
|
2021-05-13 23:38:36 +02:00
|
|
|
const IDLE_TIMEOUT_SEC = 30
|
|
|
|
|
|
2021-05-17 00:27:47 +02:00
|
|
|
// Map<gameId, Game>
|
|
|
|
|
const GAMES: Record<string, Game> = {}
|
2020-11-17 22:34:15 +01:00
|
|
|
|
2021-05-29 17:58:05 +02:00
|
|
|
function exists(gameId: string): boolean {
|
2020-11-17 22:34:15 +01:00
|
|
|
return (!!GAMES[gameId]) || false
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-29 15:36:03 +02:00
|
|
|
function __createPlayerObject(id: string, ts: Timestamp): Player {
|
2020-12-05 19:45:34 +01:00
|
|
|
return {
|
|
|
|
|
id: id,
|
|
|
|
|
x: 0,
|
|
|
|
|
y: 0,
|
|
|
|
|
d: 0, // mouse down
|
2020-12-07 12:20:09 +01:00
|
|
|
name: null, // 'anon'
|
|
|
|
|
color: null, // '#ffffff'
|
|
|
|
|
bgcolor: null, // '#222222'
|
2020-12-05 19:45:34 +01:00
|
|
|
points: 0,
|
|
|
|
|
ts: ts,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-28 22:41:17 +02:00
|
|
|
function setGame(gameId: string, game: Game): void {
|
2020-11-17 22:34:15 +01:00
|
|
|
GAMES[gameId] = game
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-17 00:27:47 +02:00
|
|
|
function getPlayerIndexById(gameId: string, playerId: string): number {
|
2020-12-23 01:19:30 +01:00
|
|
|
let i = 0;
|
2021-05-29 17:58:05 +02:00
|
|
|
for (const player of GAMES[gameId].players) {
|
2020-12-23 01:19:30 +01:00
|
|
|
if (Util.decodePlayer(player).id === playerId) {
|
|
|
|
|
return i
|
|
|
|
|
}
|
|
|
|
|
i++
|
|
|
|
|
}
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-17 02:32:33 +02:00
|
|
|
function getPlayerIdByIndex(gameId: string, playerIndex: number): string|null {
|
2020-12-23 01:19:30 +01:00
|
|
|
if (GAMES[gameId].players.length > playerIndex) {
|
|
|
|
|
return Util.decodePlayer(GAMES[gameId].players[playerIndex]).id
|
|
|
|
|
}
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-29 11:44:55 +02:00
|
|
|
function getPlayer(gameId: string, playerId: string): Player|null {
|
2021-05-17 02:32:33 +02:00
|
|
|
const idx = getPlayerIndexById(gameId, playerId)
|
2021-05-29 11:44:55 +02:00
|
|
|
if (idx === -1) {
|
|
|
|
|
return null
|
|
|
|
|
}
|
2020-12-23 01:19:30 +01:00
|
|
|
return Util.decodePlayer(GAMES[gameId].players[idx])
|
2020-12-05 19:45:34 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-28 22:41:17 +02:00
|
|
|
function setPlayer(
|
|
|
|
|
gameId: string,
|
|
|
|
|
playerId: string,
|
|
|
|
|
player: Player
|
|
|
|
|
): void {
|
2021-05-17 02:32:33 +02:00
|
|
|
const idx = getPlayerIndexById(gameId, playerId)
|
2020-12-23 01:19:30 +01:00
|
|
|
if (idx === -1) {
|
|
|
|
|
GAMES[gameId].players.push(Util.encodePlayer(player))
|
|
|
|
|
} else {
|
|
|
|
|
GAMES[gameId].players[idx] = Util.encodePlayer(player)
|
|
|
|
|
}
|
2020-12-05 19:45:34 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-29 15:36:03 +02:00
|
|
|
function setPiece(gameId: string, pieceIdx: number, piece: Piece): void {
|
|
|
|
|
GAMES[gameId].puzzle.tiles[pieceIdx] = Util.encodePiece(piece)
|
2020-12-05 19:45:34 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-28 22:41:17 +02:00
|
|
|
function setPuzzleData(gameId: string, data: PuzzleData): void {
|
2020-12-05 19:45:34 +01:00
|
|
|
GAMES[gameId].puzzle.data = data
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-28 22:41:17 +02:00
|
|
|
function playerExists(gameId: string, playerId: string): boolean {
|
2020-12-23 01:19:30 +01:00
|
|
|
const idx = getPlayerIndexById(gameId, playerId)
|
|
|
|
|
return idx !== -1
|
2020-12-03 21:11:52 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-17 02:32:33 +02:00
|
|
|
function getActivePlayers(gameId: string, ts: number): Array<Player> {
|
2021-05-13 23:38:36 +02:00
|
|
|
const minTs = ts - IDLE_TIMEOUT_SEC * Time.SEC
|
2021-05-17 00:27:47 +02:00
|
|
|
return getAllPlayers(gameId).filter((p: Player) => p.ts >= minTs)
|
2020-12-21 02:29:14 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-17 02:32:33 +02:00
|
|
|
function getIdlePlayers(gameId: string, ts: number): Array<Player> {
|
2021-05-13 23:38:36 +02:00
|
|
|
const minTs = ts - IDLE_TIMEOUT_SEC * Time.SEC
|
2021-05-17 00:27:47 +02:00
|
|
|
return getAllPlayers(gameId).filter((p: Player) => p.ts < minTs && p.points > 0)
|
2020-12-05 19:45:34 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-29 15:36:03 +02:00
|
|
|
function addPlayer(gameId: string, playerId: string, ts: Timestamp): void {
|
2020-12-23 01:19:30 +01:00
|
|
|
if (!playerExists(gameId, playerId)) {
|
2020-12-22 22:35:09 +01:00
|
|
|
setPlayer(gameId, playerId, __createPlayerObject(playerId, ts))
|
2020-11-25 22:03:35 +01:00
|
|
|
} else {
|
|
|
|
|
changePlayer(gameId, playerId, { ts })
|
2020-11-17 22:34:15 +01:00
|
|
|
}
|
2020-12-24 15:48:47 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-17 02:32:33 +02:00
|
|
|
function getEvtInfo(gameId: string, playerId: string): EvtInfo {
|
2020-12-24 15:48:47 +01:00
|
|
|
if (playerId in GAMES[gameId].evtInfos) {
|
|
|
|
|
return GAMES[gameId].evtInfos[playerId]
|
2020-11-17 22:34:15 +01:00
|
|
|
}
|
2020-12-24 15:48:47 +01:00
|
|
|
return {
|
|
|
|
|
_last_mouse: null,
|
|
|
|
|
_last_mouse_down: null,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-28 22:41:17 +02:00
|
|
|
function setEvtInfo(
|
|
|
|
|
gameId: string,
|
|
|
|
|
playerId: string,
|
|
|
|
|
evtInfo: EvtInfo
|
|
|
|
|
): void {
|
2020-12-24 15:48:47 +01:00
|
|
|
GAMES[gameId].evtInfos[playerId] = evtInfo
|
2020-11-17 22:34:15 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-17 00:27:47 +02:00
|
|
|
function getAllGames(): Array<Game> {
|
|
|
|
|
return Object.values(GAMES).sort((a: Game, b: Game) => {
|
2020-12-06 21:55:23 +01:00
|
|
|
// when both have same finished state, sort by started
|
|
|
|
|
if (isFinished(a.id) === isFinished(b.id)) {
|
|
|
|
|
return b.puzzle.data.started - a.puzzle.data.started
|
|
|
|
|
}
|
|
|
|
|
// otherwise, sort: unfinished, finished
|
|
|
|
|
return isFinished(a.id) ? 1 : -1
|
|
|
|
|
})
|
2020-12-05 19:45:34 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-17 02:32:33 +02:00
|
|
|
function getAllPlayers(gameId: string): Array<Player> {
|
2020-12-05 19:45:34 +01:00
|
|
|
return GAMES[gameId]
|
2020-12-23 01:19:30 +01:00
|
|
|
? GAMES[gameId].players.map(Util.decodePlayer)
|
2020-12-05 19:45:34 +01:00
|
|
|
: []
|
2020-11-25 22:03:35 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-29 17:58:05 +02:00
|
|
|
function get(gameId: string): Game|null {
|
|
|
|
|
return GAMES[gameId] || null
|
2020-11-17 22:34:15 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-29 15:36:03 +02:00
|
|
|
function getPieceCount(gameId: string): number {
|
2020-12-05 19:45:34 +01:00
|
|
|
return GAMES[gameId].puzzle.tiles.length
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-17 02:32:33 +02:00
|
|
|
function getImageUrl(gameId: string): string {
|
2020-12-05 19:45:34 +01:00
|
|
|
return GAMES[gameId].puzzle.info.imageUrl
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-28 22:41:17 +02:00
|
|
|
function setImageUrl(gameId: string, imageUrl: string): void {
|
2021-04-21 09:54:10 +02:00
|
|
|
GAMES[gameId].puzzle.info.imageUrl = imageUrl
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-17 02:32:33 +02:00
|
|
|
function getScoreMode(gameId: string): ScoreMode {
|
|
|
|
|
return GAMES[gameId].scoreMode || ScoreMode.FINAL
|
2021-04-27 21:43:53 +02:00
|
|
|
}
|
|
|
|
|
|
2021-05-28 22:41:17 +02:00
|
|
|
function isFinished(gameId: string): boolean {
|
2021-05-29 15:36:03 +02:00
|
|
|
return getFinishedPiecesCount(gameId) === getPieceCount(gameId)
|
2020-12-06 21:55:23 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-29 15:36:03 +02:00
|
|
|
function getFinishedPiecesCount(gameId: string): number {
|
2020-12-05 19:45:34 +01:00
|
|
|
let count = 0
|
2021-05-29 17:58:05 +02:00
|
|
|
for (const t of GAMES[gameId].puzzle.tiles) {
|
2021-05-29 15:36:03 +02:00
|
|
|
if (Util.decodePiece(t).owner === -1) {
|
2020-12-05 19:45:34 +01:00
|
|
|
count++
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return count
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-29 15:36:03 +02:00
|
|
|
function getPiecesSortedByZIndex(gameId: string): Piece[] {
|
|
|
|
|
const pieces = GAMES[gameId].puzzle.tiles.map(Util.decodePiece)
|
|
|
|
|
return pieces.sort((t1, t2) => t1.z - t2.z)
|
2020-12-05 19:45:34 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-28 22:41:17 +02:00
|
|
|
function changePlayer(
|
|
|
|
|
gameId: string,
|
|
|
|
|
playerId: string,
|
2021-05-29 17:58:05 +02:00
|
|
|
change: PlayerChange
|
2021-05-28 22:41:17 +02:00
|
|
|
): void {
|
2020-12-05 19:45:34 +01:00
|
|
|
const player = getPlayer(gameId, playerId)
|
2021-05-29 11:44:55 +02:00
|
|
|
if (player === null) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-29 17:58:05 +02:00
|
|
|
for (const k of Object.keys(change)) {
|
2021-05-17 02:32:33 +02:00
|
|
|
// @ts-ignore
|
2020-12-05 19:45:34 +01:00
|
|
|
player[k] = change[k]
|
2020-11-17 22:34:15 +01:00
|
|
|
}
|
2020-12-05 19:45:34 +01:00
|
|
|
setPlayer(gameId, playerId, player)
|
2020-11-17 22:34:15 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-29 17:58:05 +02:00
|
|
|
function changeData(gameId: string, change: PuzzleDataChange): void {
|
|
|
|
|
for (const k of Object.keys(change)) {
|
2021-05-17 00:27:47 +02:00
|
|
|
// @ts-ignore
|
2020-11-17 22:34:15 +01:00
|
|
|
GAMES[gameId].puzzle.data[k] = change[k]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-29 17:58:05 +02:00
|
|
|
function changePiece(
|
|
|
|
|
gameId: string,
|
|
|
|
|
pieceIdx: number,
|
|
|
|
|
change: PieceChange
|
|
|
|
|
): void {
|
|
|
|
|
for (const k of Object.keys(change)) {
|
2021-05-29 15:36:03 +02:00
|
|
|
const piece = Util.decodePiece(GAMES[gameId].puzzle.tiles[pieceIdx])
|
2021-05-17 02:32:33 +02:00
|
|
|
// @ts-ignore
|
2021-05-29 15:36:03 +02:00
|
|
|
piece[k] = change[k]
|
|
|
|
|
GAMES[gameId].puzzle.tiles[pieceIdx] = Util.encodePiece(piece)
|
2020-11-17 22:34:15 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-29 15:36:03 +02:00
|
|
|
const getPiece = (gameId: string, pieceIdx: number): Piece => {
|
|
|
|
|
return Util.decodePiece(GAMES[gameId].puzzle.tiles[pieceIdx])
|
2020-11-17 22:34:15 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-29 15:36:03 +02:00
|
|
|
const getPieceGroup = (gameId: string, tileIdx: number): number => {
|
|
|
|
|
const tile = getPiece(gameId, tileIdx)
|
2020-11-17 22:34:15 +01:00
|
|
|
return tile.group
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-29 15:36:03 +02:00
|
|
|
const getFinalPiecePos = (gameId: string, tileIdx: number): Point => {
|
2020-11-17 22:34:15 +01:00
|
|
|
const info = GAMES[gameId].puzzle.info
|
|
|
|
|
const boardPos = {
|
|
|
|
|
x: (info.table.width - info.width) / 2,
|
|
|
|
|
y: (info.table.height - info.height) / 2
|
|
|
|
|
}
|
|
|
|
|
const srcPos = srcPosByTileIdx(gameId, tileIdx)
|
|
|
|
|
return Geometry.pointAdd(boardPos, srcPos)
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-29 15:36:03 +02:00
|
|
|
const getPiecePos = (gameId: string, tileIdx: number): Point => {
|
|
|
|
|
const tile = getPiece(gameId, tileIdx)
|
2020-11-17 22:34:15 +01:00
|
|
|
return tile.pos
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-20 23:02:44 +02:00
|
|
|
// todo: instead, just make the table bigger and use that :)
|
2021-05-28 22:41:17 +02:00
|
|
|
const getBounds = (gameId: string): Rect => {
|
2021-04-20 23:02:44 +02:00
|
|
|
const tw = getTableWidth(gameId)
|
|
|
|
|
const th = getTableHeight(gameId)
|
|
|
|
|
|
|
|
|
|
const overX = Math.round(tw / 4)
|
|
|
|
|
const overY = Math.round(th / 4)
|
|
|
|
|
return {
|
|
|
|
|
x: 0 - overX,
|
|
|
|
|
y: 0 - overY,
|
|
|
|
|
w: tw + 2 * overX,
|
|
|
|
|
h: th + 2 * overY,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-29 15:36:03 +02:00
|
|
|
const getPieceBounds = (gameId: string, tileIdx: number): Rect => {
|
|
|
|
|
const s = getPieceSize(gameId)
|
|
|
|
|
const tile = getPiece(gameId, tileIdx)
|
2021-04-20 23:02:44 +02:00
|
|
|
return {
|
|
|
|
|
x: tile.pos.x,
|
|
|
|
|
y: tile.pos.y,
|
|
|
|
|
w: s,
|
|
|
|
|
h: s,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-29 17:58:05 +02:00
|
|
|
const getPieceZIndex = (gameId: string, pieceIdx: number): number => {
|
|
|
|
|
return getPiece(gameId, pieceIdx).z
|
2020-11-17 22:34:15 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-29 15:36:03 +02:00
|
|
|
const getFirstOwnedPieceIdx = (gameId: string, playerId: string): number => {
|
2021-05-29 17:58:05 +02:00
|
|
|
for (const t of GAMES[gameId].puzzle.tiles) {
|
2021-05-29 15:36:03 +02:00
|
|
|
const tile = Util.decodePiece(t)
|
2021-05-17 00:27:47 +02:00
|
|
|
if (tile.owner === playerId) {
|
2020-12-05 19:45:34 +01:00
|
|
|
return tile.idx
|
2020-11-17 22:34:15 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-29 17:58:05 +02:00
|
|
|
const getFirstOwnedPiece = (
|
|
|
|
|
gameId: string,
|
|
|
|
|
playerId: string
|
|
|
|
|
): EncodedPiece|null => {
|
2021-05-29 15:36:03 +02:00
|
|
|
const idx = getFirstOwnedPieceIdx(gameId, playerId)
|
2020-12-05 19:45:34 +01:00
|
|
|
return idx < 0 ? null : GAMES[gameId].puzzle.tiles[idx]
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-29 15:36:03 +02:00
|
|
|
const getPieceDrawOffset = (gameId: string): number => {
|
2020-12-05 19:45:34 +01:00
|
|
|
return GAMES[gameId].puzzle.info.tileDrawOffset
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-29 15:36:03 +02:00
|
|
|
const getPieceDrawSize = (gameId: string): number => {
|
2020-12-05 19:45:34 +01:00
|
|
|
return GAMES[gameId].puzzle.info.tileDrawSize
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-29 15:36:03 +02:00
|
|
|
const getPieceSize = (gameId: string): number => {
|
2021-04-20 23:02:44 +02:00
|
|
|
return GAMES[gameId].puzzle.info.tileSize
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-28 22:41:17 +02:00
|
|
|
const getStartTs = (gameId: string): Timestamp => {
|
2020-12-07 02:38:07 +01:00
|
|
|
return GAMES[gameId].puzzle.data.started
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-28 22:41:17 +02:00
|
|
|
const getFinishTs = (gameId: string): Timestamp => {
|
2020-12-07 02:38:07 +01:00
|
|
|
return GAMES[gameId].puzzle.data.finished
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-28 22:41:17 +02:00
|
|
|
const getMaxGroup = (gameId: string): number => {
|
2020-11-17 22:34:15 +01:00
|
|
|
return GAMES[gameId].puzzle.data.maxGroup
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-28 22:41:17 +02:00
|
|
|
const getMaxZIndex = (gameId: string): number => {
|
2020-11-17 22:34:15 +01:00
|
|
|
return GAMES[gameId].puzzle.data.maxZ
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-29 17:58:05 +02:00
|
|
|
const getMaxZIndexByPieceIdxs = (gameId: string, pieceIdxs: Array<number>): number => {
|
2020-11-17 22:34:15 +01:00
|
|
|
let maxZ = 0
|
2021-05-29 17:58:05 +02:00
|
|
|
for (const pieceIdx of pieceIdxs) {
|
|
|
|
|
const curZ = getPieceZIndex(gameId, pieceIdx)
|
|
|
|
|
if (curZ > maxZ) {
|
|
|
|
|
maxZ = curZ
|
2020-11-17 22:34:15 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return maxZ
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-28 22:41:17 +02:00
|
|
|
function srcPosByTileIdx(gameId: string, tileIdx: number): Point {
|
2020-11-17 22:34:15 +01:00
|
|
|
const info = GAMES[gameId].puzzle.info
|
|
|
|
|
|
2021-05-29 17:58:05 +02:00
|
|
|
const c = Util.coordByPieceIdx(info, tileIdx)
|
2020-11-17 22:34:15 +01:00
|
|
|
const cx = c.x * info.tileSize
|
|
|
|
|
const cy = c.y * info.tileSize
|
|
|
|
|
|
|
|
|
|
return { x: cx, y: cy }
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-17 00:27:47 +02:00
|
|
|
function getSurroundingTilesByIdx(gameId: string, tileIdx: number) {
|
2020-11-17 22:34:15 +01:00
|
|
|
const info = GAMES[gameId].puzzle.info
|
|
|
|
|
|
2021-05-29 17:58:05 +02:00
|
|
|
const c = Util.coordByPieceIdx(info, tileIdx)
|
2020-11-17 22:34:15 +01:00
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
// top
|
2020-12-05 19:45:34 +01:00
|
|
|
(c.y > 0) ? (tileIdx - info.tilesX) : -1,
|
2020-11-17 22:34:15 +01:00
|
|
|
// right
|
2020-12-05 19:45:34 +01:00
|
|
|
(c.x < info.tilesX - 1) ? (tileIdx + 1) : -1,
|
2020-11-17 22:34:15 +01:00
|
|
|
// bottom
|
2020-12-05 19:45:34 +01:00
|
|
|
(c.y < info.tilesY - 1) ? (tileIdx + info.tilesX) : -1,
|
2020-11-17 22:34:15 +01:00
|
|
|
// left
|
2020-12-05 19:45:34 +01:00
|
|
|
(c.x > 0) ? (tileIdx - 1) : -1,
|
2020-11-17 22:34:15 +01:00
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-29 17:58:05 +02:00
|
|
|
const setPiecesZIndex = (gameId: string, tileIdxs: Array<number>, zIndex: number): void => {
|
|
|
|
|
for (const tilesIdx of tileIdxs) {
|
|
|
|
|
changePiece(gameId, tilesIdx, { z: zIndex })
|
2020-11-17 22:34:15 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-28 22:41:17 +02:00
|
|
|
const moveTileDiff = (gameId: string, tileIdx: number, diff: Point): void => {
|
2021-05-29 15:36:03 +02:00
|
|
|
const oldPos = getPiecePos(gameId, tileIdx)
|
2020-11-17 22:34:15 +01:00
|
|
|
const pos = Geometry.pointAdd(oldPos, diff)
|
2021-05-29 17:58:05 +02:00
|
|
|
changePiece(gameId, tileIdx, { pos })
|
2020-11-17 22:34:15 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-29 17:58:05 +02:00
|
|
|
const movePiecesDiff = (
|
2021-05-28 22:41:17 +02:00
|
|
|
gameId: string,
|
2021-05-29 17:58:05 +02:00
|
|
|
pieceIdxs: Array<number>,
|
2021-05-28 22:41:17 +02:00
|
|
|
diff: Point
|
|
|
|
|
): void => {
|
2021-05-29 15:36:03 +02:00
|
|
|
const drawSize = getPieceDrawSize(gameId)
|
2021-04-20 23:02:44 +02:00
|
|
|
const bounds = getBounds(gameId)
|
|
|
|
|
const cappedDiff = diff
|
|
|
|
|
|
2021-05-29 17:58:05 +02:00
|
|
|
for (const pieceIdx of pieceIdxs) {
|
|
|
|
|
const t = getPiece(gameId, pieceIdx)
|
2021-04-20 23:02:44 +02:00
|
|
|
if (t.pos.x + diff.x < bounds.x) {
|
|
|
|
|
cappedDiff.x = Math.max(bounds.x - t.pos.x, cappedDiff.x)
|
2021-05-29 15:36:03 +02:00
|
|
|
} else if (t.pos.x + drawSize + diff.x > bounds.x + bounds.w) {
|
|
|
|
|
cappedDiff.x = Math.min(bounds.x + bounds.w - t.pos.x + drawSize, cappedDiff.x)
|
2021-04-20 23:02:44 +02:00
|
|
|
}
|
|
|
|
|
if (t.pos.y + diff.y < bounds.y) {
|
|
|
|
|
cappedDiff.y = Math.max(bounds.y - t.pos.y, cappedDiff.y)
|
2021-05-29 15:36:03 +02:00
|
|
|
} else if (t.pos.y + drawSize + diff.y > bounds.y + bounds.h) {
|
|
|
|
|
cappedDiff.y = Math.min(bounds.y + bounds.h - t.pos.y + drawSize, cappedDiff.y)
|
2021-04-20 23:02:44 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-29 17:58:05 +02:00
|
|
|
for (const pieceIdx of pieceIdxs) {
|
|
|
|
|
moveTileDiff(gameId, pieceIdx, cappedDiff)
|
2020-11-17 22:34:15 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-29 17:58:05 +02:00
|
|
|
const finishPieces = (gameId: string, pieceIdxs: Array<number>): void => {
|
|
|
|
|
for (const pieceIdx of pieceIdxs) {
|
|
|
|
|
changePiece(gameId, pieceIdx, { owner: -1, z: 1 })
|
2020-11-17 22:34:15 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-17 00:27:47 +02:00
|
|
|
const setTilesOwner = (
|
|
|
|
|
gameId: string,
|
2021-05-29 17:58:05 +02:00
|
|
|
pieceIdxs: Array<number>,
|
2021-05-17 00:27:47 +02:00
|
|
|
owner: string|number
|
2021-05-28 22:41:17 +02:00
|
|
|
): void => {
|
2021-05-29 17:58:05 +02:00
|
|
|
for (const pieceIdx of pieceIdxs) {
|
|
|
|
|
changePiece(gameId, pieceIdx, { owner })
|
2020-11-17 22:34:15 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// get all grouped tiles for a tile
|
2021-05-29 15:36:03 +02:00
|
|
|
function getGroupedPieceIdxs(gameId: string, pieceIdx: number): number[] {
|
|
|
|
|
const pieces = GAMES[gameId].puzzle.tiles
|
|
|
|
|
const piece = Util.decodePiece(pieces[pieceIdx])
|
2020-11-17 22:34:15 +01:00
|
|
|
|
|
|
|
|
const grouped = []
|
2021-05-29 15:36:03 +02:00
|
|
|
if (piece.group) {
|
2021-05-29 17:58:05 +02:00
|
|
|
for (const other of pieces) {
|
2021-05-29 15:36:03 +02:00
|
|
|
const otherPiece = Util.decodePiece(other)
|
|
|
|
|
if (otherPiece.group === piece.group) {
|
|
|
|
|
grouped.push(otherPiece.idx)
|
2020-11-17 22:34:15 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2021-05-29 15:36:03 +02:00
|
|
|
grouped.push(piece.idx)
|
2020-11-17 22:34:15 +01:00
|
|
|
}
|
|
|
|
|
return grouped
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns the index of the puzzle tile with the highest z index
|
|
|
|
|
// that is not finished yet and that matches the position
|
2021-05-29 15:36:03 +02:00
|
|
|
const freePieceIdxByPos = (gameId: string, pos: Point): number => {
|
2021-05-29 17:58:05 +02:00
|
|
|
const info = GAMES[gameId].puzzle.info
|
|
|
|
|
const pieces = GAMES[gameId].puzzle.tiles
|
2020-11-17 22:34:15 +01:00
|
|
|
|
|
|
|
|
let maxZ = -1
|
2021-05-29 15:36:03 +02:00
|
|
|
let pieceIdx = -1
|
|
|
|
|
for (let idx = 0; idx < pieces.length; idx++) {
|
|
|
|
|
const piece = Util.decodePiece(pieces[idx])
|
|
|
|
|
if (piece.owner !== 0) {
|
2020-11-17 22:34:15 +01:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-17 02:32:33 +02:00
|
|
|
const collisionRect: Rect = {
|
2021-05-29 15:36:03 +02:00
|
|
|
x: piece.pos.x,
|
|
|
|
|
y: piece.pos.y,
|
2020-11-17 22:34:15 +01:00
|
|
|
w: info.tileSize,
|
|
|
|
|
h: info.tileSize,
|
|
|
|
|
}
|
|
|
|
|
if (Geometry.pointInBounds(pos, collisionRect)) {
|
2021-05-29 15:36:03 +02:00
|
|
|
if (maxZ === -1 || piece.z > maxZ) {
|
|
|
|
|
maxZ = piece.z
|
|
|
|
|
pieceIdx = idx
|
2020-11-17 22:34:15 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-05-29 15:36:03 +02:00
|
|
|
return pieceIdx
|
2020-11-17 22:34:15 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-28 22:41:17 +02:00
|
|
|
const getPlayerBgColor = (gameId: string, playerId: string): string|null => {
|
2020-12-22 22:35:09 +01:00
|
|
|
const p = getPlayer(gameId, playerId)
|
|
|
|
|
return p ? p.bgcolor : null
|
2020-12-05 19:45:34 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-28 22:41:17 +02:00
|
|
|
const getPlayerColor = (gameId: string, playerId: string): string|null => {
|
2020-12-22 22:35:09 +01:00
|
|
|
const p = getPlayer(gameId, playerId)
|
|
|
|
|
return p ? p.color : null
|
2020-12-05 19:45:34 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-28 22:41:17 +02:00
|
|
|
const getPlayerName = (gameId: string, playerId: string): string|null => {
|
2020-12-22 22:35:09 +01:00
|
|
|
const p = getPlayer(gameId, playerId)
|
|
|
|
|
return p ? p.name : null
|
2020-12-05 19:45:34 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-17 02:32:33 +02:00
|
|
|
const getPlayerPoints = (gameId: string, playerId: string): number => {
|
2020-12-22 22:35:09 +01:00
|
|
|
const p = getPlayer(gameId, playerId)
|
2021-05-17 02:32:33 +02:00
|
|
|
return p ? p.points : 0
|
2020-12-05 19:45:34 +01:00
|
|
|
}
|
|
|
|
|
|
2020-11-17 22:34:15 +01:00
|
|
|
// determine if two tiles are grouped together
|
2021-05-28 22:41:17 +02:00
|
|
|
const areGrouped = (
|
|
|
|
|
gameId: string,
|
|
|
|
|
tileIdx1: number,
|
|
|
|
|
tileIdx2: number
|
|
|
|
|
): boolean => {
|
2021-05-29 15:36:03 +02:00
|
|
|
const g1 = getPieceGroup(gameId, tileIdx1)
|
|
|
|
|
const g2 = getPieceGroup(gameId, tileIdx2)
|
2021-05-28 22:41:17 +02:00
|
|
|
return !!(g1 && g1 === g2)
|
2020-11-17 22:34:15 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-28 22:41:17 +02:00
|
|
|
const getTableWidth = (gameId: string): number => {
|
2020-12-05 19:45:34 +01:00
|
|
|
return GAMES[gameId].puzzle.info.table.width
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-28 22:41:17 +02:00
|
|
|
const getTableHeight = (gameId: string): number => {
|
2020-12-05 19:45:34 +01:00
|
|
|
return GAMES[gameId].puzzle.info.table.height
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-28 22:41:17 +02:00
|
|
|
const getPuzzle = (gameId: string): Puzzle => {
|
2020-12-22 22:35:09 +01:00
|
|
|
return GAMES[gameId].puzzle
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-17 00:27:47 +02:00
|
|
|
const getRng = (gameId: string): Rng => {
|
2020-12-22 22:35:09 +01:00
|
|
|
return GAMES[gameId].rng.obj
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-28 22:41:17 +02:00
|
|
|
const getPuzzleWidth = (gameId: string): number => {
|
2020-12-05 19:45:34 +01:00
|
|
|
return GAMES[gameId].puzzle.info.width
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-28 22:41:17 +02:00
|
|
|
const getPuzzleHeight = (gameId: string): number => {
|
2020-12-05 19:45:34 +01:00
|
|
|
return GAMES[gameId].puzzle.info.height
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-28 22:41:17 +02:00
|
|
|
function handleInput(
|
|
|
|
|
gameId: string,
|
|
|
|
|
playerId: string,
|
2021-05-29 17:58:05 +02:00
|
|
|
input: Input,
|
|
|
|
|
ts: Timestamp
|
|
|
|
|
): Array<Change> {
|
2020-12-05 19:45:34 +01:00
|
|
|
const puzzle = GAMES[gameId].puzzle
|
2021-05-09 21:43:35 +02:00
|
|
|
const evtInfo = getEvtInfo(gameId, playerId)
|
2020-11-17 22:34:15 +01:00
|
|
|
|
2021-05-29 17:58:05 +02:00
|
|
|
const changes: Array<Change> = []
|
2020-11-17 22:34:15 +01:00
|
|
|
|
2021-05-28 22:41:17 +02:00
|
|
|
const _dataChange = (): void => {
|
2021-04-19 23:59:14 +02:00
|
|
|
changes.push([Protocol.CHANGE_DATA, puzzle.data])
|
2020-11-17 22:34:15 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-29 17:58:05 +02:00
|
|
|
const _pieceChange = (pieceIdx: number): void => {
|
2021-05-09 21:43:35 +02:00
|
|
|
changes.push([
|
|
|
|
|
Protocol.CHANGE_TILE,
|
2021-05-29 17:58:05 +02:00
|
|
|
Util.encodePiece(getPiece(gameId, pieceIdx)),
|
2021-05-09 21:43:35 +02:00
|
|
|
])
|
2020-11-17 22:34:15 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-29 17:58:05 +02:00
|
|
|
const _pieceChanges = (pieceIdxs: Array<number>): void => {
|
|
|
|
|
for (const pieceIdx of pieceIdxs) {
|
|
|
|
|
_pieceChange(pieceIdx)
|
2020-11-17 22:34:15 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-28 22:41:17 +02:00
|
|
|
const _playerChange = (): void => {
|
2021-05-29 11:44:55 +02:00
|
|
|
const player = getPlayer(gameId, playerId)
|
|
|
|
|
if (!player) {
|
|
|
|
|
return
|
|
|
|
|
}
|
2021-05-09 21:43:35 +02:00
|
|
|
changes.push([
|
|
|
|
|
Protocol.CHANGE_PLAYER,
|
2021-05-29 11:44:55 +02:00
|
|
|
Util.encodePlayer(player),
|
2021-05-09 21:43:35 +02:00
|
|
|
])
|
2020-11-17 22:34:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// put both tiles (and their grouped tiles) in the same group
|
2021-05-28 22:41:17 +02:00
|
|
|
const groupTiles = (
|
|
|
|
|
gameId: string,
|
2021-05-29 17:58:05 +02:00
|
|
|
pieceIdx1: number,
|
|
|
|
|
pieceIdx2: number
|
2021-05-28 22:41:17 +02:00
|
|
|
): void => {
|
2021-05-29 17:58:05 +02:00
|
|
|
const pieces = GAMES[gameId].puzzle.tiles
|
|
|
|
|
const group1 = getPieceGroup(gameId, pieceIdx1)
|
|
|
|
|
const group2 = getPieceGroup(gameId, pieceIdx2)
|
2020-11-17 22:34:15 +01:00
|
|
|
|
|
|
|
|
let group
|
2021-05-09 21:43:35 +02:00
|
|
|
const searchGroups = []
|
2020-11-17 22:34:15 +01:00
|
|
|
if (group1) {
|
|
|
|
|
searchGroups.push(group1)
|
|
|
|
|
}
|
|
|
|
|
if (group2) {
|
|
|
|
|
searchGroups.push(group2)
|
|
|
|
|
}
|
|
|
|
|
if (group1) {
|
|
|
|
|
group = group1
|
|
|
|
|
} else if (group2) {
|
|
|
|
|
group = group2
|
|
|
|
|
} else {
|
2021-05-09 21:43:35 +02:00
|
|
|
const maxGroup = getMaxGroup(gameId) + 1
|
2020-11-17 22:34:15 +01:00
|
|
|
changeData(gameId, { maxGroup })
|
|
|
|
|
_dataChange()
|
|
|
|
|
group = getMaxGroup(gameId)
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-29 17:58:05 +02:00
|
|
|
changePiece(gameId, pieceIdx1, { group })
|
|
|
|
|
_pieceChange(pieceIdx1)
|
|
|
|
|
changePiece(gameId, pieceIdx2, { group })
|
|
|
|
|
_pieceChange(pieceIdx2)
|
2020-11-17 22:34:15 +01:00
|
|
|
|
|
|
|
|
// TODO: strange
|
|
|
|
|
if (searchGroups.length > 0) {
|
2021-05-29 17:58:05 +02:00
|
|
|
for (const p of pieces) {
|
|
|
|
|
const piece = Util.decodePiece(p)
|
2021-05-29 15:36:03 +02:00
|
|
|
if (searchGroups.includes(piece.group)) {
|
2021-05-29 17:58:05 +02:00
|
|
|
changePiece(gameId, piece.idx, { group })
|
|
|
|
|
_pieceChange(piece.idx)
|
2020-11-17 22:34:15 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-25 22:03:35 +01:00
|
|
|
const type = input[0]
|
2020-12-23 01:19:30 +01:00
|
|
|
if (type === Protocol.INPUT_EV_BG_COLOR) {
|
2020-12-03 21:24:59 +01:00
|
|
|
const bgcolor = input[1]
|
|
|
|
|
changePlayer(gameId, playerId, { bgcolor, ts })
|
|
|
|
|
_playerChange()
|
2020-12-23 01:19:30 +01:00
|
|
|
} else if (type === Protocol.INPUT_EV_PLAYER_COLOR) {
|
2020-11-25 22:03:35 +01:00
|
|
|
const color = input[1]
|
|
|
|
|
changePlayer(gameId, playerId, { color, ts })
|
|
|
|
|
_playerChange()
|
2020-12-23 01:19:30 +01:00
|
|
|
} else if (type === Protocol.INPUT_EV_PLAYER_NAME) {
|
2020-11-25 22:03:35 +01:00
|
|
|
const name = `${input[1]}`.substr(0, 16)
|
|
|
|
|
changePlayer(gameId, playerId, { name, ts })
|
|
|
|
|
_playerChange()
|
2020-12-23 01:19:30 +01:00
|
|
|
} else if (type === Protocol.INPUT_EV_MOUSE_DOWN) {
|
2020-11-25 22:03:35 +01:00
|
|
|
const x = input[1]
|
|
|
|
|
const y = input[2]
|
|
|
|
|
const pos = {x, y}
|
|
|
|
|
|
|
|
|
|
changePlayer(gameId, playerId, { d: 1, ts })
|
2020-11-17 22:34:15 +01:00
|
|
|
_playerChange()
|
|
|
|
|
evtInfo._last_mouse_down = pos
|
|
|
|
|
|
2021-05-29 15:36:03 +02:00
|
|
|
const tileIdxAtPos = freePieceIdxByPos(gameId, pos)
|
2020-11-17 22:34:15 +01:00
|
|
|
if (tileIdxAtPos >= 0) {
|
2021-05-29 17:58:05 +02:00
|
|
|
const maxZ = getMaxZIndex(gameId) + 1
|
2020-11-17 22:34:15 +01:00
|
|
|
changeData(gameId, { maxZ })
|
|
|
|
|
_dataChange()
|
2021-05-29 15:36:03 +02:00
|
|
|
const tileIdxs = getGroupedPieceIdxs(gameId, tileIdxAtPos)
|
2021-05-29 17:58:05 +02:00
|
|
|
setPiecesZIndex(gameId, tileIdxs, getMaxZIndex(gameId))
|
2020-11-17 22:34:15 +01:00
|
|
|
setTilesOwner(gameId, tileIdxs, playerId)
|
2021-05-29 17:58:05 +02:00
|
|
|
_pieceChanges(tileIdxs)
|
2020-11-17 22:34:15 +01:00
|
|
|
}
|
2020-11-25 22:03:35 +01:00
|
|
|
evtInfo._last_mouse = pos
|
2020-11-17 22:34:15 +01:00
|
|
|
|
2020-12-23 01:19:30 +01:00
|
|
|
} else if (type === Protocol.INPUT_EV_MOUSE_MOVE) {
|
2020-11-25 22:03:35 +01:00
|
|
|
const x = input[1]
|
|
|
|
|
const y = input[2]
|
|
|
|
|
const pos = {x, y}
|
|
|
|
|
|
2020-12-24 15:48:47 +01:00
|
|
|
if (evtInfo._last_mouse_down === null) {
|
|
|
|
|
// player is just moving the hand
|
|
|
|
|
changePlayer(gameId, playerId, {x, y, ts})
|
|
|
|
|
_playerChange()
|
|
|
|
|
} else {
|
2021-05-29 17:58:05 +02:00
|
|
|
const pieceIdx = getFirstOwnedPieceIdx(gameId, playerId)
|
|
|
|
|
if (pieceIdx >= 0) {
|
2020-12-24 15:48:47 +01:00
|
|
|
// player is moving a tile (and hand)
|
|
|
|
|
changePlayer(gameId, playerId, {x, y, ts})
|
|
|
|
|
_playerChange()
|
|
|
|
|
|
2021-04-20 23:02:44 +02:00
|
|
|
// check if pos is on the tile, otherwise dont move
|
|
|
|
|
// (mouse could be out of table, but tile stays on it)
|
2021-05-29 17:58:05 +02:00
|
|
|
const pieceIdxs = getGroupedPieceIdxs(gameId, pieceIdx)
|
2021-04-20 23:02:44 +02:00
|
|
|
let anyOk = Geometry.pointInBounds(pos, getBounds(gameId))
|
|
|
|
|
&& Geometry.pointInBounds(evtInfo._last_mouse_down, getBounds(gameId))
|
2021-05-29 17:58:05 +02:00
|
|
|
for (const idx of pieceIdxs) {
|
2021-05-29 15:36:03 +02:00
|
|
|
const bounds = getPieceBounds(gameId, idx)
|
2021-04-20 23:02:44 +02:00
|
|
|
if (Geometry.pointInBounds(pos, bounds)) {
|
|
|
|
|
anyOk = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (anyOk) {
|
|
|
|
|
const diffX = x - evtInfo._last_mouse_down.x
|
|
|
|
|
const diffY = y - evtInfo._last_mouse_down.y
|
|
|
|
|
|
|
|
|
|
const diff = { x: diffX, y: diffY }
|
2021-05-29 17:58:05 +02:00
|
|
|
movePiecesDiff(gameId, pieceIdxs, diff)
|
2021-04-20 23:02:44 +02:00
|
|
|
|
2021-05-29 17:58:05 +02:00
|
|
|
_pieceChanges(pieceIdxs)
|
2021-04-20 23:02:44 +02:00
|
|
|
}
|
2020-12-24 15:48:47 +01:00
|
|
|
} else {
|
|
|
|
|
// player is just moving map, so no change in position!
|
|
|
|
|
changePlayer(gameId, playerId, {ts})
|
|
|
|
|
_playerChange()
|
2020-11-17 22:34:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
evtInfo._last_mouse_down = pos
|
|
|
|
|
}
|
2020-11-25 22:03:35 +01:00
|
|
|
evtInfo._last_mouse = pos
|
|
|
|
|
|
2020-12-23 01:19:30 +01:00
|
|
|
} else if (type === Protocol.INPUT_EV_MOUSE_UP) {
|
2020-11-25 22:03:35 +01:00
|
|
|
const x = input[1]
|
|
|
|
|
const y = input[2]
|
|
|
|
|
const pos = {x, y}
|
2021-05-16 01:42:21 +02:00
|
|
|
const d = 0
|
2020-11-25 22:03:35 +01:00
|
|
|
|
2020-11-17 22:34:15 +01:00
|
|
|
evtInfo._last_mouse_down = null
|
|
|
|
|
|
2021-05-29 17:58:05 +02:00
|
|
|
const pieceIdx = getFirstOwnedPieceIdx(gameId, playerId)
|
|
|
|
|
if (pieceIdx >= 0) {
|
2020-11-17 22:34:15 +01:00
|
|
|
// drop the tile(s)
|
2021-05-29 17:58:05 +02:00
|
|
|
const pieceIdxs = getGroupedPieceIdxs(gameId, pieceIdx)
|
|
|
|
|
setTilesOwner(gameId, pieceIdxs, 0)
|
|
|
|
|
_pieceChanges(pieceIdxs)
|
2020-11-17 22:34:15 +01:00
|
|
|
|
|
|
|
|
// Check if the tile was dropped near the final location
|
2021-05-29 17:58:05 +02:00
|
|
|
const tilePos = getPiecePos(gameId, pieceIdx)
|
|
|
|
|
const finalPos = getFinalPiecePos(gameId, pieceIdx)
|
2020-11-17 22:34:15 +01:00
|
|
|
if (Geometry.pointDistance(finalPos, tilePos) < puzzle.info.snapDistance) {
|
2021-05-29 17:58:05 +02:00
|
|
|
const diff = Geometry.pointSub(finalPos, tilePos)
|
2020-11-17 22:34:15 +01:00
|
|
|
// Snap the tile to the final destination
|
2021-05-29 17:58:05 +02:00
|
|
|
movePiecesDiff(gameId, pieceIdxs, diff)
|
|
|
|
|
finishPieces(gameId, pieceIdxs)
|
|
|
|
|
_pieceChanges(pieceIdxs)
|
2021-04-27 21:43:53 +02:00
|
|
|
|
2021-05-16 01:42:21 +02:00
|
|
|
let points = getPlayerPoints(gameId, playerId)
|
2021-05-17 02:32:33 +02:00
|
|
|
if (getScoreMode(gameId) === ScoreMode.FINAL) {
|
2021-05-29 17:58:05 +02:00
|
|
|
points += pieceIdxs.length
|
2021-05-17 02:32:33 +02:00
|
|
|
} else if (getScoreMode(gameId) === ScoreMode.ANY) {
|
2021-05-16 01:42:21 +02:00
|
|
|
points += 1
|
2021-04-27 21:43:53 +02:00
|
|
|
} else {
|
|
|
|
|
// no score mode... should never occur, because there is a
|
2021-05-17 02:32:33 +02:00
|
|
|
// fallback to ScoreMode.FINAL in getScoreMode function
|
2021-04-27 21:43:53 +02:00
|
|
|
}
|
2021-05-16 01:42:21 +02:00
|
|
|
changePlayer(gameId, playerId, { d, ts, points })
|
|
|
|
|
_playerChange()
|
2021-04-27 21:43:53 +02:00
|
|
|
|
2020-12-06 21:55:23 +01:00
|
|
|
// check if the puzzle is finished
|
2021-05-29 15:36:03 +02:00
|
|
|
if (getFinishedPiecesCount(gameId) === getPieceCount(gameId)) {
|
2020-12-22 22:35:09 +01:00
|
|
|
changeData(gameId, { finished: ts })
|
2020-12-06 21:55:23 +01:00
|
|
|
_dataChange()
|
|
|
|
|
}
|
2020-11-17 22:34:15 +01:00
|
|
|
} else {
|
|
|
|
|
// Snap to other tiles
|
2021-05-17 00:27:47 +02:00
|
|
|
const check = (
|
|
|
|
|
gameId: string,
|
|
|
|
|
tileIdx: number,
|
|
|
|
|
otherTileIdx: number,
|
|
|
|
|
off: Array<number>
|
2021-05-28 22:41:17 +02:00
|
|
|
): boolean => {
|
2021-05-29 17:58:05 +02:00
|
|
|
const info = GAMES[gameId].puzzle.info
|
2020-11-17 22:34:15 +01:00
|
|
|
if (otherTileIdx < 0) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
if (areGrouped(gameId, tileIdx, otherTileIdx)) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
2021-05-29 15:36:03 +02:00
|
|
|
const tilePos = getPiecePos(gameId, tileIdx)
|
2020-11-17 22:34:15 +01:00
|
|
|
const dstPos = Geometry.pointAdd(
|
2021-05-29 15:36:03 +02:00
|
|
|
getPiecePos(gameId, otherTileIdx),
|
2020-11-17 22:34:15 +01:00
|
|
|
{x: off[0] * info.tileSize, y: off[1] * info.tileSize}
|
|
|
|
|
)
|
|
|
|
|
if (Geometry.pointDistance(tilePos, dstPos) < info.snapDistance) {
|
2021-05-29 17:58:05 +02:00
|
|
|
const diff = Geometry.pointSub(dstPos, tilePos)
|
|
|
|
|
let pieceIdxs = getGroupedPieceIdxs(gameId, tileIdx)
|
|
|
|
|
movePiecesDiff(gameId, pieceIdxs, diff)
|
2020-11-17 22:34:15 +01:00
|
|
|
groupTiles(gameId, tileIdx, otherTileIdx)
|
2021-05-29 17:58:05 +02:00
|
|
|
pieceIdxs = getGroupedPieceIdxs(gameId, tileIdx)
|
|
|
|
|
const zIndex = getMaxZIndexByPieceIdxs(gameId, pieceIdxs)
|
|
|
|
|
setPiecesZIndex(gameId, pieceIdxs, zIndex)
|
|
|
|
|
_pieceChanges(pieceIdxs)
|
2020-11-17 22:34:15 +01:00
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-16 01:42:21 +02:00
|
|
|
let snapped = false
|
2021-05-29 17:58:05 +02:00
|
|
|
for (const pieceIdxTmp of getGroupedPieceIdxs(gameId, pieceIdx)) {
|
|
|
|
|
const othersIdxs = getSurroundingTilesByIdx(gameId, pieceIdxTmp)
|
2020-11-17 22:34:15 +01:00
|
|
|
if (
|
2021-05-29 17:58:05 +02:00
|
|
|
check(gameId, pieceIdxTmp, othersIdxs[0], [0, 1]) // top
|
|
|
|
|
|| check(gameId, pieceIdxTmp, othersIdxs[1], [-1, 0]) // right
|
|
|
|
|
|| check(gameId, pieceIdxTmp, othersIdxs[2], [0, -1]) // bottom
|
|
|
|
|
|| check(gameId, pieceIdxTmp, othersIdxs[3], [1, 0]) // left
|
2020-11-17 22:34:15 +01:00
|
|
|
) {
|
2021-05-16 01:42:21 +02:00
|
|
|
snapped = true
|
2020-11-17 22:34:15 +01:00
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-05-17 02:32:33 +02:00
|
|
|
if (snapped && getScoreMode(gameId) === ScoreMode.ANY) {
|
2021-05-16 01:42:21 +02:00
|
|
|
const points = getPlayerPoints(gameId, playerId) + 1
|
|
|
|
|
changePlayer(gameId, playerId, { d, ts, points })
|
|
|
|
|
_playerChange()
|
|
|
|
|
} else {
|
|
|
|
|
changePlayer(gameId, playerId, { d, ts })
|
|
|
|
|
_playerChange()
|
|
|
|
|
}
|
2020-11-17 22:34:15 +01:00
|
|
|
}
|
2021-05-16 01:42:21 +02:00
|
|
|
} else {
|
|
|
|
|
changePlayer(gameId, playerId, { d, ts })
|
|
|
|
|
_playerChange()
|
2020-11-17 22:34:15 +01:00
|
|
|
}
|
2020-11-25 22:03:35 +01:00
|
|
|
evtInfo._last_mouse = pos
|
2021-05-13 23:38:36 +02:00
|
|
|
} else if (type === Protocol.INPUT_EV_ZOOM_IN) {
|
2021-05-16 01:42:21 +02:00
|
|
|
const x = input[1]
|
|
|
|
|
const y = input[2]
|
|
|
|
|
changePlayer(gameId, playerId, { x, y, ts })
|
2021-05-13 23:38:36 +02:00
|
|
|
_playerChange()
|
2021-05-16 01:42:21 +02:00
|
|
|
evtInfo._last_mouse = { x, y }
|
2021-05-13 23:38:36 +02:00
|
|
|
} else if (type === Protocol.INPUT_EV_ZOOM_OUT) {
|
2021-05-16 01:42:21 +02:00
|
|
|
const x = input[1]
|
|
|
|
|
const y = input[2]
|
|
|
|
|
changePlayer(gameId, playerId, { x, y, ts })
|
2021-05-13 23:38:36 +02:00
|
|
|
_playerChange()
|
2021-05-16 01:42:21 +02:00
|
|
|
evtInfo._last_mouse = { x, y }
|
2020-11-25 22:03:35 +01:00
|
|
|
} else {
|
|
|
|
|
changePlayer(gameId, playerId, { ts })
|
|
|
|
|
_playerChange()
|
2020-11-17 22:34:15 +01:00
|
|
|
}
|
|
|
|
|
|
2020-12-24 15:48:47 +01:00
|
|
|
setEvtInfo(gameId, playerId, evtInfo)
|
2020-11-17 22:34:15 +01:00
|
|
|
return changes
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default {
|
2021-04-30 01:03:43 +02:00
|
|
|
setGame,
|
2020-11-17 22:34:15 +01:00
|
|
|
exists,
|
2020-12-03 21:11:52 +01:00
|
|
|
playerExists,
|
2020-12-05 19:45:34 +01:00
|
|
|
getActivePlayers,
|
2021-05-13 23:38:36 +02:00
|
|
|
getIdlePlayers,
|
2020-11-17 22:34:15 +01:00
|
|
|
addPlayer,
|
2021-05-29 15:36:03 +02:00
|
|
|
getFinishedPiecesCount,
|
|
|
|
|
getPieceCount,
|
2020-12-05 19:45:34 +01:00
|
|
|
getImageUrl,
|
2021-04-21 09:54:10 +02:00
|
|
|
setImageUrl,
|
2020-11-17 22:34:15 +01:00
|
|
|
get,
|
2020-11-25 22:03:35 +01:00
|
|
|
getAllGames,
|
2020-12-05 19:45:34 +01:00
|
|
|
getPlayerBgColor,
|
|
|
|
|
getPlayerColor,
|
|
|
|
|
getPlayerName,
|
2020-12-23 01:19:30 +01:00
|
|
|
getPlayerIndexById,
|
|
|
|
|
getPlayerIdByIndex,
|
2020-12-05 19:45:34 +01:00
|
|
|
changePlayer,
|
|
|
|
|
setPlayer,
|
2021-05-29 15:36:03 +02:00
|
|
|
setPiece,
|
2020-12-05 19:45:34 +01:00
|
|
|
setPuzzleData,
|
|
|
|
|
getTableWidth,
|
|
|
|
|
getTableHeight,
|
2020-12-22 22:35:09 +01:00
|
|
|
getPuzzle,
|
|
|
|
|
getRng,
|
2020-12-05 19:45:34 +01:00
|
|
|
getPuzzleWidth,
|
|
|
|
|
getPuzzleHeight,
|
2021-05-29 15:36:03 +02:00
|
|
|
getPiecesSortedByZIndex,
|
|
|
|
|
getFirstOwnedPiece,
|
|
|
|
|
getPieceDrawOffset,
|
|
|
|
|
getPieceDrawSize,
|
|
|
|
|
getFinalPiecePos,
|
2020-12-07 02:38:07 +01:00
|
|
|
getStartTs,
|
|
|
|
|
getFinishTs,
|
2020-11-17 22:34:15 +01:00
|
|
|
handleInput,
|
|
|
|
|
}
|