summaryrefslogtreecommitdiff
path: root/client/src/logic
diff options
context:
space:
mode:
authorTyler Murphy <tylerm@tylerm.dev>2023-06-16 20:38:55 -0400
committerTyler Murphy <tylerm@tylerm.dev>2023-06-16 20:38:55 -0400
commit44334fc3852eb832280a335f72e6416c93a9f19f (patch)
tree4a97b6064a97c4ad58c07d89050ad8a11e7a4f70 /client/src/logic
parentbetter map bg renderer (diff)
downloadtuxman-44334fc3852eb832280a335f72e6416c93a9f19f.tar.gz
tuxman-44334fc3852eb832280a335f72e6416c93a9f19f.tar.bz2
tuxman-44334fc3852eb832280a335f72e6416c93a9f19f.zip
ts
Diffstat (limited to 'client/src/logic')
-rw-r--r--client/src/logic/items.ts41
-rw-r--r--client/src/logic/logic.ts80
-rw-r--r--client/src/logic/movement.ts142
-rw-r--r--client/src/logic/players.ts79
-rw-r--r--client/src/logic/ui.ts32
5 files changed, 374 insertions, 0 deletions
diff --git a/client/src/logic/items.ts b/client/src/logic/items.ts
new file mode 100644
index 0000000..5f8a38e
--- /dev/null
+++ b/client/src/logic/items.ts
@@ -0,0 +1,41 @@
+import { getMap, getItemKey } from "../map.js"
+import { GameState, Map, Player } from "../types.js"
+
+const ceilHalf = (n: number): number => {
+ return Math.ceil(n*2)/2
+}
+
+const floorHalf = (n: number): number => {
+ return Math.floor(n*2)/2
+}
+
+const eatItems = (data: GameState, map: Map, player: Player) => {
+
+ let pos = player.pos
+
+ for (let x = ceilHalf(pos.x-.5); x <= floorHalf(pos.x+.5); x += .5) {
+ for (let y = ceilHalf(pos.y-.5); y <= floorHalf(pos.y+.5); y += .5) {
+ let item_key = getItemKey(x, y, map.width)
+ delete data.items[item_key]
+ }
+ }
+}
+
+export const updateItems = (data: GameState) => {
+
+ let map = getMap(data.mapId)
+ if (!map) return
+
+ for(const id in data.input) {
+
+ const player = data.players[id]
+
+ if(!player) {
+ continue;
+ }
+
+ eatItems(data, map, player)
+
+ }
+
+}
diff --git a/client/src/logic/logic.ts b/client/src/logic/logic.ts
new file mode 100644
index 0000000..1cca2b7
--- /dev/null
+++ b/client/src/logic/logic.ts
@@ -0,0 +1,80 @@
+import { genItems, loadMap, getMap } from "../map.js";
+import { updatePlayers } from "./players.js"
+import { updateUI } from "./ui.js"
+import { updateMovement } from "./movement.js"
+import { updateItems } from "./items.js"
+import { GameState, Input } from "../types.js";
+
+
+export const InitialState: GameState = {
+ started: false,
+ input: {},
+ players: [],
+ items: {},
+ mapId: undefined
+}
+
+export const onLogic = (
+ pastData: GameState = InitialState,
+ input: Input = { players: {} },
+ _frame: number
+) => {
+
+ let data = structuredClone(pastData)
+
+ let startPressed = updatePlayers(data, input);
+
+ if (data.started) {
+ updateMovement(data)
+ updateItems(data)
+ } else {
+ updateUI(data)
+ }
+
+ if (startPressed && !data.started) {
+ initMap(data)
+ data.started = true;
+ }
+
+ return data
+
+}
+
+const initMap = (data: GameState) => {
+
+ document.getElementById("lobby").style.display = "none"
+
+ data.mapId = 0
+
+ if (getMap(0)) return
+
+ let width = 21
+ let height = 21
+ let m_data = [
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,
+ 1,0,1,1,1,0,1,1,1,0,1,0,1,1,1,0,1,1,1,0,1,
+ 1,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,1,
+ 1,0,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,0,1,0,1,
+ 1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,
+ 1,0,1,1,1,0,1,0,1,1,1,1,1,0,1,0,1,1,1,0,1,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+ 1,1,1,0,1,0,1,0,1,1,1,1,1,0,1,0,1,0,1,1,1,
+ 1,0,0,0,0,0,1,0,1,2,2,2,1,0,1,0,0,0,0,0,1,
+ 1,0,1,1,1,0,1,0,1,2,2,2,1,0,1,0,1,1,1,0,1,
+ 1,0,0,0,0,0,1,0,1,2,2,2,1,0,1,0,0,0,0,0,1,
+ 1,1,1,0,1,0,1,0,1,1,2,1,1,0,1,0,1,0,1,1,1,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+ 1,0,1,1,1,0,1,0,1,1,1,1,1,0,1,0,1,1,1,0,1,
+ 1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,
+ 1,0,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,0,1,0,1,
+ 1,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,1,
+ 1,0,1,1,1,0,1,1,1,0,1,0,1,1,1,0,1,1,1,0,1,
+ 1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
+ ]
+
+ loadMap(width, height, m_data) // cursed temp thing
+ data.items = genItems(getMap(0))
+}
+
diff --git a/client/src/logic/movement.ts b/client/src/logic/movement.ts
new file mode 100644
index 0000000..40cfc3e
--- /dev/null
+++ b/client/src/logic/movement.ts
@@ -0,0 +1,142 @@
+import { getMap } from "../map.js"
+import { Vec2, Map, Rotation, Key, Player, GameState } from "../types.js"
+
+const MOVE_SPEED = .1
+
+const roundPos = (pos: Vec2): Vec2 => {
+ return {x: Math.round(pos.x), y: Math.round(pos.y)}
+}
+
+const isStablePos = (pos: Vec2): boolean => {
+ let rpos = roundPos(pos)
+ return Math.abs(rpos.x - pos.x) < .05 && Math.abs(rpos.y - pos.y) < .05
+}
+
+const getTile = (
+ map: Map,
+ pos: Vec2,
+ ox: number,
+ oy: number
+): number => {
+ let x = Math.round(pos.x + ox)
+ let y = Math.round(pos.y + oy)
+ if (x < 0 || x >= map.width || y < 0 || y >= map.height) return 1
+ return map.data[y * map.width + x]
+}
+
+const getTileFrontWithRot = (
+ map: Map,
+ pos: Vec2,
+ rot: Rotation
+): number => {
+ let collider = 1
+ switch(rot) {
+ case Rotation.NORTH:
+ collider = getTile(map, pos, 0, -.51)
+ break
+ case Rotation.SOUTH:
+ collider = getTile(map, pos, 0, .51)
+ break
+ case Rotation.WEST:
+ collider = getTile(map, pos, -.51, 0)
+ break
+ case Rotation.EAST:
+ collider = getTile(map, pos, .51, 0)
+ break
+ }
+ return collider
+}
+
+const getRot = (key: Key): Rotation => {
+ switch (key) {
+ case Key.UP: return Rotation.NORTH
+ case Key.DOWN: return Rotation.SOUTH
+ case Key.LEFT: return Rotation.WEST
+ case Key.RIGHT: return Rotation.EAST
+ case Key.NOTHING: return Rotation.NOTHING
+ }
+}
+
+const incrementPos = (
+ pos: Vec2,
+ rot: Rotation,
+ speed: number
+): void => {
+ switch (rot) {
+ case Rotation.NORTH:
+ pos.y -= speed
+ break
+ case Rotation.SOUTH:
+ pos.y += speed
+ break
+ case Rotation.WEST:
+ pos.x -= speed
+ break
+ case Rotation.EAST:
+ pos.y += speed
+ break
+ }
+}
+
+let i = 0
+
+const updateMovementForPlayer = (
+ map: Map,
+ player: Player,
+ inputKey: Key
+) => {
+
+ let inputRot = getRot(inputKey)
+ let moveRot = player.moveRotation
+ let currentPosition = player.pos
+
+ let turningFrontTile = getTileFrontWithRot(map, currentPosition, inputRot)
+ if (turningFrontTile == 1 || turningFrontTile == 2) {
+ inputRot = Rotation.NOTHING
+ }
+
+ let turning = inputRot != Rotation.NOTHING && inputRot != moveRot
+
+ player.inputRotation = inputRot
+
+ if (turning && isStablePos(currentPosition)) {
+ currentPosition = roundPos(currentPosition)
+ player.moveRotation = inputRot
+ moveRot = inputRot
+ }
+
+ let movePos = structuredClone(currentPosition)
+ incrementPos(movePos, moveRot, MOVE_SPEED)
+
+ let frontTile = getTileFrontWithRot(map, currentPosition, moveRot)
+ if (frontTile != 1 && frontTile != 2) {
+ player.pos = movePos
+ player.moving = true
+ } else {
+ player.pos = roundPos(currentPosition)
+ player.moving = false
+ }
+
+
+}
+
+export const updateMovement = (data: GameState) => {
+
+ let map = getMap(data.mapId)
+ if (!map) return
+
+ for (const id in data.players) {
+
+ const player = data.players[id]
+
+ if(!player) {
+ continue
+ }
+
+ let inputKey = data.input[id]
+
+ updateMovementForPlayer(map, player, inputKey)
+
+ }
+
+}
diff --git a/client/src/logic/players.ts b/client/src/logic/players.ts
new file mode 100644
index 0000000..ebe469f
--- /dev/null
+++ b/client/src/logic/players.ts
@@ -0,0 +1,79 @@
+import { GameState, Input, Key, Rotation } from "../types.js"
+
+const canPlayerJoin = (data: GameState) => {
+
+ // lobby has already started
+ if (data.started) {
+ return false
+ }
+
+ // lobby full
+ if (Object.keys(data.players).length >= 4) {
+ return false
+ }
+
+ return true
+
+}
+
+export const updatePlayers = (data: GameState, input: Input) => {
+
+ let startPressed = false;
+
+ for(const added of input.added || []) {
+
+ if (!canPlayerJoin(data)) {
+ continue
+ }
+
+ console.log("added", added);
+
+ data.input[added] = Key.NOTHING
+
+ data.players[added] ||= {
+ pos: {x: 1, y: 1},
+ inputRotation: Rotation.EAST,
+ moveRotation: Rotation.EAST,
+ moving: false,
+ };
+
+ }
+
+ for(const id in input.players) {
+
+ if(!input.players[id]) {
+ continue;
+ }
+
+ if(id in data.players && input.players[id].name !== undefined) {
+
+ let name = input.players[id].name;
+ name = name.substring(0, 16);
+
+ data.players[id] = {
+ ...data.players[id],
+ name,
+ };
+
+ }
+
+ startPressed ||= input.players[id].start;
+ if (input.players[id].key)
+ data.input[id] = input.players[id].key
+
+ }
+
+ for(const removed of input.removed || []) {
+ console.log("removed", removed);
+ delete data.input[removed];
+ delete data.players[removed];
+
+ let element_id = 'span' + removed
+ let element = document.getElementById(element_id)
+ if (element !== null && element !== undefined) element.remove()
+ }
+
+ return startPressed
+
+}
+
diff --git a/client/src/logic/ui.ts b/client/src/logic/ui.ts
new file mode 100644
index 0000000..5706843
--- /dev/null
+++ b/client/src/logic/ui.ts
@@ -0,0 +1,32 @@
+import { GameState } from "../types.js"
+
+export const updateUI = (data: GameState) => {
+
+ const player_display = document.getElementById("players")
+
+ for (const id in data.players) {
+
+ const player = data.players[id]
+
+ if (!player) {
+ continue
+ }
+
+ let name = player.name
+
+ if (!name) {
+ continue
+ }
+
+ let element_id = 'span' + id
+ let element = player_display.children[element_id]
+
+ if (!element) {
+ let span = document.createElement("span")
+ span.textContent = `[${id}] ${name}`
+ span.id = element_id
+ player_display.appendChild(span)
+ }
+ }
+
+}