Compare commits
No commits in common. "ffe9d8226d6b485ff74475a6d197b6172aa686f3" and "99f73315b3af6c8c8633be66df3dd5e57ab9c4ef" have entirely different histories.
ffe9d8226d
...
99f73315b3
26 changed files with 264 additions and 740 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
44
backend/Dockerfile
Normal file
44
backend/Dockerfile
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
# Build stage
|
||||||
|
FROM golang:1.25-bookworm AS builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install dependencies including libenet-dev from Debian repos
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
git \
|
||||||
|
gcc \
|
||||||
|
pkg-config \
|
||||||
|
libenet-dev \
|
||||||
|
sqlite3 \
|
||||||
|
libsqlite3-dev \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Copy go mod and sum files
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
|
||||||
|
# Download dependencies
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
COPY ./*.go .
|
||||||
|
|
||||||
|
# Enable CGO for go-sqlite3 and go-enet
|
||||||
|
ENV CGO_ENABLED=1
|
||||||
|
|
||||||
|
RUN go build -o main .
|
||||||
|
|
||||||
|
# Runtime stage
|
||||||
|
FROM debian:bookworm-slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install runtime dependencies
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
libenet7 \
|
||||||
|
ca-certificates \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY --from=builder /app/main .
|
||||||
|
|
||||||
|
EXPOSE 8095
|
||||||
|
|
||||||
|
CMD ["./main"]
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
{:paths ["src"]
|
|
||||||
:deps { ;; db
|
|
||||||
com.datomic/local {:mvn/version "1.0.291"}
|
|
||||||
|
|
||||||
;; logging
|
|
||||||
com.taoensso/telemere {:mvn/version "1.0.0"}
|
|
||||||
|
|
||||||
;; environment variables
|
|
||||||
environ/environ {:mvn/version "1.2.0"}
|
|
||||||
|
|
||||||
;; json / schema validation
|
|
||||||
metosin/malli {:mvn/version "0.18.0"}
|
|
||||||
metosin/muuntaja {:mvn/version "0.6.11"}
|
|
||||||
|
|
||||||
;; networking - UDP/TCP/HTTP streaming
|
|
||||||
aleph/aleph {:mvn/version "0.9.7"}
|
|
||||||
org.clj-commons/byte-streams {:mvn/version "0.3.4"}
|
|
||||||
|
|
||||||
org.clojure/clojure {:mvn/version "1.12.1"}}
|
|
||||||
:aliases
|
|
||||||
{;; Run with clj -T:build function-in-build
|
|
||||||
:build {:deps {io.github.clojure/tools.build {:git/tag "v0.10.9" :git/sha "e405aac"}}
|
|
||||||
:ns-default build}}}
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# Watch for changes to .clj files and auto-restart the server
|
|
||||||
# fd -e clj: Find all files ending in .clj
|
|
||||||
# entr -r: Run the command when files change, -r flag restarts the command on re-trigger
|
|
||||||
# clojure: Clojure CLI
|
|
||||||
# -M: Use the main alias from deps.edn (if defined)
|
|
||||||
# -m game-server.main: Run the -main function from game-server.main namespace
|
|
||||||
fd -e clj | entr -r clojure -M -m game-server.main
|
|
||||||
|
|
||||||
8
backend/docker-compose.yml
Normal file
8
backend/docker-compose.yml
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
services:
|
||||||
|
backend:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: love2d-backend-dev
|
||||||
|
ports:
|
||||||
|
- "8095:8095"
|
||||||
16
backend/go.mod
Normal file
16
backend/go.mod
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
module love2d_backend
|
||||||
|
|
||||||
|
go 1.25.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/codecat/go-libs v0.0.0-20210906174629-ffa6674c8e05
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.32
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/codecat/go-enet v0.0.0-20250728072647-ae229138f138
|
||||||
|
github.com/fatih/color v1.19.0 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
golang.org/x/sys v0.42.0 // indirect
|
||||||
|
)
|
||||||
15
backend/go.sum
Normal file
15
backend/go.sum
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
github.com/codecat/go-enet v0.0.0-20250728072647-ae229138f138 h1:qQaqly9oVi84i4nLWmM551z7kcFZIy5koCLoPwCarGY=
|
||||||
|
github.com/codecat/go-enet v0.0.0-20250728072647-ae229138f138/go.mod h1:sXkhNvv8T1d/aPEwipUzk7rGWwGJ3uJST2P/Z0zE0L4=
|
||||||
|
github.com/codecat/go-libs v0.0.0-20210906174629-ffa6674c8e05 h1:JSfDXHJvrIpQ8Agy//yoIlGpfIprTCDUytmf68fd/Lc=
|
||||||
|
github.com/codecat/go-libs v0.0.0-20210906174629-ffa6674c8e05/go.mod h1:xJW98cHEb+Kbuu0qmoKzExh3blthZqojIYOFo27VgvE=
|
||||||
|
github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w=
|
||||||
|
github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE=
|
||||||
|
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||||
|
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||||
|
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||||
76
backend/main.go
Normal file
76
backend/main.go
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"log/slog"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
enet "github.com/codecat/go-enet"
|
||||||
|
_ "github.com/mattn/go-sqlite3"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
port := uint16(8095)
|
||||||
|
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
|
||||||
|
slog.SetDefault(logger)
|
||||||
|
|
||||||
|
// Initialize enet
|
||||||
|
slog.Info("Initializing E-Net Server", "port", port)
|
||||||
|
enet.Initialize()
|
||||||
|
|
||||||
|
// Create a host listening on 0.0.0.0:8095
|
||||||
|
host, err := enet.NewHost(enet.NewListenAddress(port), 32, 1, 0, 0)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("Couldn't create host", "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
slog.Info("Server started", "port", port)
|
||||||
|
|
||||||
|
// The event loop
|
||||||
|
for true {
|
||||||
|
// Wait until the next event
|
||||||
|
ev := host.Service(1000)
|
||||||
|
|
||||||
|
// Do nothing if we didn't get any event
|
||||||
|
if ev.GetType() == enet.EventNone {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ev.GetType() {
|
||||||
|
case enet.EventConnect: // A new peer has connected
|
||||||
|
slog.Info("New peer connected", "address", ev.GetPeer().GetAddress())
|
||||||
|
|
||||||
|
case enet.EventDisconnect: // A connected peer has disconnected
|
||||||
|
slog.Info("Peer disconnected", "address", ev.GetPeer().GetAddress())
|
||||||
|
|
||||||
|
case enet.EventReceive: // A peer sent us some data
|
||||||
|
// Get the packet
|
||||||
|
packet := ev.GetPacket()
|
||||||
|
|
||||||
|
// We must destroy the packet when we're done with it
|
||||||
|
defer packet.Destroy()
|
||||||
|
|
||||||
|
// Get the bytes in the packet
|
||||||
|
packetBytes := packet.GetData()
|
||||||
|
|
||||||
|
// Respond "pong" to "ping"
|
||||||
|
if string(packetBytes) == "ping" {
|
||||||
|
ev.GetPeer().SendString("pong", ev.GetChannelID(), enet.PacketFlagReliable)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disconnect the peer if they say "bye"
|
||||||
|
if string(packetBytes) == "bye" {
|
||||||
|
slog.Info("Peer sent bye, disconnecting")
|
||||||
|
ev.GetPeer().Disconnect(0)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroy the host when we're done with it
|
||||||
|
host.Destroy()
|
||||||
|
|
||||||
|
// Uninitialize enet
|
||||||
|
enet.Deinitialize()
|
||||||
|
}
|
||||||
|
|
@ -1,151 +0,0 @@
|
||||||
(ns game-server.main
|
|
||||||
(:require
|
|
||||||
[aleph.udp :as udp]
|
|
||||||
[manifold.stream :as s]
|
|
||||||
[clj-commons.byte-streams :as bs]
|
|
||||||
[taoensso.telemere :as t]
|
|
||||||
[clojure.pprint :refer [pprint]]
|
|
||||||
[clojure.string :as str])
|
|
||||||
(:import (java.security MessageDigest))
|
|
||||||
(:gen-class))
|
|
||||||
|
|
||||||
;; UDP examples
|
|
||||||
;; - https://gist.github.com/crosstyan/8a87ebdb0c23e549b1c75e9e4013ffa5
|
|
||||||
|
|
||||||
(def port 9999)
|
|
||||||
(def players (atom {})) ; client registry: {player-id {:host ... :port ...}}
|
|
||||||
(def games (atom {:by-id {} :all []}))
|
|
||||||
|
|
||||||
;; Global socket reference for sending
|
|
||||||
(def server-socket (atom nil))
|
|
||||||
|
|
||||||
;; (defn parse-position-msg
|
|
||||||
;; "Parse: 'player-id:x:y' format"
|
|
||||||
;; [msg-str]
|
|
||||||
;; (try
|
|
||||||
;; (let [[player-id x y] (str/split msg-str #":")]
|
|
||||||
;; {:player-id (Integer/parseInt player-id)
|
|
||||||
;; :x (Double/parseDouble x)
|
|
||||||
;; :y (Double/parseDouble y)})
|
|
||||||
;; (catch Exception e
|
|
||||||
;; (t/log! {:level :warn :data {:msg msg-str :error (str e)}} "Failed to parse message")
|
|
||||||
;; nil)))
|
|
||||||
;;
|
|
||||||
|
|
||||||
;; (if waiting-game
|
|
||||||
;; (swap! games (fn [state]
|
|
||||||
;; (let )
|
|
||||||
|
|
||||||
;; :by-id (assoc (:by-id state) player-id {:player-1 }
|
|
||||||
|
|
||||||
;; (-> (:by-id state)
|
|
||||||
;; (assoc player-id waiting-game))
|
|
||||||
;; :waiting (disj (:waiting state) waiting-game)
|
|
||||||
;; :all (conj (:all state) waiting-game)
|
|
||||||
;; })))
|
|
||||||
|
|
||||||
;; (defn get-game-by-player-id [player-id]
|
|
||||||
;; (first (filter (fn [game] (or (= (:player-1 game) player-id) (= (:player-2 game) player-id))) @games)))
|
|
||||||
|
|
||||||
;; (defn get-waiting-game []
|
|
||||||
;; (first (filter (fn [game] (nil? (:player-2 game))) @games)))
|
|
||||||
|
|
||||||
;; (defn join-waiting-game! [player-id other-player-id]
|
|
||||||
;; (if-let [waiting-game (get-game-by-player-id other-player-id)]
|
|
||||||
;; (swap! games update-in [(.indexOf @games waiting-game)] assoc :player-2 player-id)
|
|
||||||
;; (swap! games conj {:player-1 player-id :player-2 nil})))
|
|
||||||
|
|
||||||
(defn md5 [s]
|
|
||||||
(let [md (MessageDigest/getInstance "MD5")
|
|
||||||
digest (.digest md (.getBytes s))]
|
|
||||||
(apply str (map #(format "%02x" %) digest))))
|
|
||||||
|
|
||||||
(defn send-to-player
|
|
||||||
"Send a UDP packet to a player"
|
|
||||||
[player-id message]
|
|
||||||
(if-let [player (get @players player-id)]
|
|
||||||
(try
|
|
||||||
(t/log! {:level :info :data {:player-id player-id :message message}} "Sending to player")
|
|
||||||
(s/put! @server-socket
|
|
||||||
{:host (:host player)
|
|
||||||
:port (:port player)
|
|
||||||
:message message})
|
|
||||||
(catch Exception e
|
|
||||||
(t/log! {:level :warn :data {:player-id player-id :error (str e)}} "Failed to send to player")))
|
|
||||||
(t/log! {:level :warn :data {:player-id player-id}} "Player not found")))
|
|
||||||
|
|
||||||
(defn join-game! [player-id]
|
|
||||||
(let [existing-game (get-in @games [:by-id player-id])
|
|
||||||
waiting-game
|
|
||||||
(some-> (first (filter (fn [game] (nil? (:player-2 game))) (@games :all)))
|
|
||||||
(assoc :player-2 player-id))
|
|
||||||
other-player-id (:player-1 waiting-game)
|
|
||||||
new-game {:player-1 player-id :player-2 nil}]
|
|
||||||
(cond
|
|
||||||
existing-game
|
|
||||||
(if (nil? (:player-2 existing-game))
|
|
||||||
(send-to-player player-id "WAIT")
|
|
||||||
(send-to-player player-id "READY_TO_PLAY"))
|
|
||||||
waiting-game (do
|
|
||||||
(swap! games (fn [state]
|
|
||||||
{:by-id
|
|
||||||
(-> (:by-id state)
|
|
||||||
(assoc player-id waiting-game)
|
|
||||||
(assoc other-player-id waiting-game))
|
|
||||||
:all (map (fn [game] (if (= (:player-1 game) other-player-id)
|
|
||||||
waiting-game
|
|
||||||
game)) (:all state))}))
|
|
||||||
(send-to-player other-player-id "READY_TO_PLAY")
|
|
||||||
(send-to-player player-id "READY_TO_PLAY"))
|
|
||||||
:else (do
|
|
||||||
(swap! games (fn [state]
|
|
||||||
{:by-id (assoc (:by-id state) player-id new-game)
|
|
||||||
:all (conj (:all state) new-game)}))
|
|
||||||
(send-to-player player-id "WAIT")))))
|
|
||||||
|
|
||||||
(defn register-player! [host port]
|
|
||||||
(let [player-id (md5 (str host port))]
|
|
||||||
(swap! players assoc player-id {:host host :port port})
|
|
||||||
(t/log! {:level :info :data {:player-id player-id}} "Player registered")
|
|
||||||
(join-game! player-id)
|
|
||||||
(pprint {:games @games :players @players})))
|
|
||||||
|
|
||||||
;; (defn broadcast-to-others [from-player-id position-data]
|
|
||||||
;; "Send position to all other players"
|
|
||||||
;; (let [other-players (dissoc @players from-player-id)
|
|
||||||
;; msg (str from-player-id ":" (:x position-data) ":" (:y position-data))]
|
|
||||||
;; (doseq [[pid _] other-players]
|
|
||||||
;; (send-to-player pid msg))))
|
|
||||||
|
|
||||||
(defn parse-packet [packet]
|
|
||||||
(let [host (-> packet (:sender) (bean) (:address) (bean) (:hostAddress))
|
|
||||||
port (-> packet (:sender) (bean) (:port))
|
|
||||||
vector-msg (-> packet (:message) (vec))
|
|
||||||
string-msg (bs/to-string (:message packet))
|
|
||||||
msg-parts (str/split string-msg #"#")]
|
|
||||||
{:host host
|
|
||||||
:port port
|
|
||||||
:msg (:message packet)
|
|
||||||
:msg-vec vector-msg
|
|
||||||
:msg-string string-msg
|
|
||||||
:msg-parts msg-parts}))
|
|
||||||
|
|
||||||
(defn packet-consumer [packet]
|
|
||||||
(t/log! {:level :info :data {:packet packet}} "Consuming packet")
|
|
||||||
(case (first (:msg-parts packet))
|
|
||||||
"REGISTER" (register-player! (:host packet) (:port packet))))
|
|
||||||
|
|
||||||
(defn start-server [port]
|
|
||||||
(let [socket @(udp/socket {:port port})]
|
|
||||||
(reset! server-socket socket)
|
|
||||||
(t/log! {:level :info :data {:port port}} "UDP server listening")
|
|
||||||
|
|
||||||
(->> socket
|
|
||||||
(s/map parse-packet)
|
|
||||||
(s/consume packet-consumer))
|
|
||||||
socket))
|
|
||||||
|
|
||||||
(defn -main []
|
|
||||||
(t/log! {:level :info :data {:port port}} "Game server starting")
|
|
||||||
(start-server port)
|
|
||||||
@(promise)) ;; keep server running
|
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# UDP test client with interactive key controls
|
|
||||||
# Connects to game server and displays responses
|
|
||||||
|
|
||||||
HOST="localhost"
|
|
||||||
SERVER_PORT=9999
|
|
||||||
LISTEN_PORT=12345
|
|
||||||
|
|
||||||
echo "=========================================="
|
|
||||||
echo " Game Test Client"
|
|
||||||
echo "=========================================="
|
|
||||||
echo ""
|
|
||||||
echo "Controls:"
|
|
||||||
echo " 1 = REGISTER (join game)"
|
|
||||||
echo " 2 = Send position update (100, 200)"
|
|
||||||
echo " 3 = Send position update (300, 150)"
|
|
||||||
echo " q = quit"
|
|
||||||
echo ""
|
|
||||||
echo "Listening for server responses on port $LISTEN_PORT..."
|
|
||||||
echo "=========================================="
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Create named pipe for responses
|
|
||||||
RESPONSE_PIPE=$(mktemp -d)/response
|
|
||||||
mkfifo "$RESPONSE_PIPE"
|
|
||||||
|
|
||||||
# Start UDP listener in background, output to FIFO
|
|
||||||
nc -u -l 127.0.0.1 $LISTEN_PORT > "$RESPONSE_PIPE" 2>/dev/null &
|
|
||||||
LISTENER_PID=$!
|
|
||||||
|
|
||||||
# Read responses in background and display them
|
|
||||||
(while IFS= read -r line; do
|
|
||||||
echo "[SERVER] $line"
|
|
||||||
done < "$RESPONSE_PIPE") &
|
|
||||||
READER_PID=$!
|
|
||||||
|
|
||||||
# Cleanup on exit
|
|
||||||
cleanup() {
|
|
||||||
kill $LISTENER_PID $READER_PID 2>/dev/null
|
|
||||||
rm -rf "$(dirname "$RESPONSE_PIPE")"
|
|
||||||
echo ""
|
|
||||||
echo "Disconnected."
|
|
||||||
}
|
|
||||||
trap cleanup EXIT
|
|
||||||
|
|
||||||
# Interactive input loop
|
|
||||||
echo -n "> "
|
|
||||||
while IFS= read -rsn1 key; do
|
|
||||||
case "$key" in
|
|
||||||
1)
|
|
||||||
echo ""
|
|
||||||
echo "[CLIENT] Sending: REGISTER"
|
|
||||||
echo -n "REGISTER" | nc -u -w1 -p $LISTEN_PORT "$HOST" "$SERVER_PORT" 2>/dev/null
|
|
||||||
echo -n "> "
|
|
||||||
;;
|
|
||||||
2)
|
|
||||||
echo ""
|
|
||||||
echo "[CLIENT] Sending: POS#100#200"
|
|
||||||
echo -n "POS#100#200" | nc -u -w1 -p $LISTEN_PORT "$HOST" "$SERVER_PORT" 2>/dev/null
|
|
||||||
echo -n "> "
|
|
||||||
;;
|
|
||||||
3)
|
|
||||||
echo ""
|
|
||||||
echo "[CLIENT] Sending: POS#300#150"
|
|
||||||
echo -n "POS#300#150" | nc -u -w1 -p $LISTEN_PORT "$HOST" "$SERVER_PORT" 2>/dev/null
|
|
||||||
echo -n "> "
|
|
||||||
;;
|
|
||||||
q|Q)
|
|
||||||
echo ""
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
# Don't print anything for invalid keys, just show prompt again
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Simple UDP test script for the game server
|
|
||||||
# Sends registration from two players on specific ports and receives responses
|
|
||||||
|
|
||||||
HOST="localhost"
|
|
||||||
SERVER_PORT=9999
|
|
||||||
|
|
||||||
echo "Testing game server at $HOST:$SERVER_PORT"
|
|
||||||
RESPONSE1=$(mktemp)
|
|
||||||
# RESPONSE2=$(mktemp)
|
|
||||||
|
|
||||||
echo "=== Listener on port 12345 ==="
|
|
||||||
nc -u -l 127.0.0.1 12345 > "$RESPONSE1" &
|
|
||||||
PID1=$!
|
|
||||||
sleep 3
|
|
||||||
|
|
||||||
trap "rm -f $RESPONSE1 $RESPONSE2; jobs -p | xargs kill -9 2>/dev/null" EXIT
|
|
||||||
# echo ""
|
|
||||||
|
|
||||||
# # Create temp files for responses
|
|
||||||
# RESPONSE1=$(mktemp)
|
|
||||||
# RESPONSE2=$(mktemp)
|
|
||||||
|
|
||||||
# # Cleanup on exit
|
|
||||||
# trap "rm -f $RESPONSE1 $RESPONSE2; jobs -p | xargs kill -9 2>/dev/null" EXIT
|
|
||||||
|
|
||||||
# echo "=== Starting Player 1 listener on port 12345 ==="
|
|
||||||
# nc -u -l 127.0.0.1 12345 > "$RESPONSE1" 2>/dev/null &
|
|
||||||
# PID1=$!
|
|
||||||
# sleep 0.3
|
|
||||||
|
|
||||||
# echo "=== Starting Player 2 listener on port 12346 ==="
|
|
||||||
# nc -u -l 127.0.0.1 12346 > "$RESPONSE2" 2>/dev/null &
|
|
||||||
# PID2=$!
|
|
||||||
# sleep 0.3
|
|
||||||
|
|
||||||
# echo ""
|
|
||||||
# echo "=== Sending REGISTER from Player 1 (port 12345) ==="
|
|
||||||
# echo -n "REGISTER" | nc -u -w1 -p 12345 "$HOST" "$SERVER_PORT"
|
|
||||||
# sleep 0.5
|
|
||||||
|
|
||||||
# echo "=== Sending REGISTER from Player 2 (port 12346) ==="
|
|
||||||
# echo -n "REGISTER" | nc -u -w1 -p 12346 "$HOST" "$SERVER_PORT"
|
|
||||||
# sleep 1
|
|
||||||
|
|
||||||
# echo ""
|
|
||||||
# echo "=== Stopping listeners ==="
|
|
||||||
# kill $PID1 $PID2 2>/dev/null
|
|
||||||
# sleep 0.2
|
|
||||||
|
|
||||||
# echo ""
|
|
||||||
# echo "=== Results ==="
|
|
||||||
# echo "Player 1 response:"
|
|
||||||
# cat "$RESPONSE1"
|
|
||||||
# echo ""
|
|
||||||
# echo "Player 2 response:"
|
|
||||||
# cat "$RESPONSE2"
|
|
||||||
|
|
||||||
# echo ""
|
|
||||||
echo "Done!"
|
|
||||||
Binary file not shown.
|
|
@ -2,103 +2,70 @@
|
||||||
(ns gen-levels
|
(ns gen-levels
|
||||||
(:require
|
(:require
|
||||||
[babashka.fs :as fs]
|
[babashka.fs :as fs]
|
||||||
[clojure.pprint :refer [pprint]]
|
[clojure.pprint :as pprint]
|
||||||
[clojure.data.xml :as xml]
|
[clojure.data.xml :as xml]
|
||||||
[clojure.string :as str]
|
[clojure.string :as str]
|
||||||
[clojure.java.io :as io])
|
[clojure.java.io :as io]))
|
||||||
(:import
|
|
||||||
'java.io.StringReader))
|
|
||||||
|
|
||||||
(defn load-tiled-xml-file [path]
|
(def walls-tsx (xml/parse (io/reader "./tiled/walls.tsx")))
|
||||||
(-> (slurp (io/reader path))
|
(def wall-colliders
|
||||||
(str/replace #">\s+" ">")
|
|
||||||
(StringReader.)
|
|
||||||
(xml/parse)))
|
|
||||||
|
|
||||||
(defn parse-tile-set [tsx]
|
|
||||||
(->>
|
(->>
|
||||||
(:content tsx)
|
(:content walls-tsx)
|
||||||
|
(remove string?)
|
||||||
(filter #(= (:tag %) :tile))
|
(filter #(= (:tag %) :tile))
|
||||||
(reduce (fn [acc tile]
|
(reduce (fn [acc tile]
|
||||||
(conj acc
|
(conj acc
|
||||||
{(Integer/parseInt (get-in tile [:attrs :id]))
|
{(Integer/parseInt (get-in tile [:attrs :id]))
|
||||||
(vec
|
(vec
|
||||||
(->> (:content tile)
|
(->> (:content tile)
|
||||||
|
(remove string?)
|
||||||
(map :content)
|
(map :content)
|
||||||
(flatten)
|
(flatten)
|
||||||
|
(remove string?)
|
||||||
(map :attrs)
|
(map :attrs)
|
||||||
(map #(select-keys % [:x :y :height :width]))
|
(map #(select-keys % [:x :y :height :width]))
|
||||||
(map #(update-vals % (fn [v] (Math/round (Double/parseDouble v)))))))}))
|
(map #(update-vals % (fn [v] (Math/round (Double/parseDouble v)))))))}))
|
||||||
|
|
||||||
{})))
|
{})))
|
||||||
|
(pprint/pprint wall-colliders)
|
||||||
|
|
||||||
(def walls-tsx (load-tiled-xml-file "./tiled/walls.tsx"))
|
(def level-001-tmx (xml/parse (io/reader "./tiled/level_001.tmx")))
|
||||||
(def objects-tsx (load-tiled-xml-file "./tiled/objects.tsx"))
|
(def level-001
|
||||||
;; (def colliders {:walls (parse-tile-set walls-tsx) :objects (parse-tile-set objects-tsx)})
|
(let [tags (remove string? (:content level-001-tmx))
|
||||||
;; (into {} (map (fn [[k v]]
|
tile-nums (-> (filter #(= (:tag %) :layer) tags)
|
||||||
;; [(transform-key k) v])
|
first
|
||||||
;; your-map))
|
:content
|
||||||
(def colliders
|
second
|
||||||
"collider boxes by tile GID"
|
:content
|
||||||
(let [walls (into {} (map (fn [[k v]] [(+ k 1) v]) (parse-tile-set walls-tsx)))
|
first
|
||||||
objects (into {} (map (fn [[k v]] [(+ k 21) v]) (parse-tile-set objects-tsx)))]
|
str/split-lines
|
||||||
(merge walls objects)))
|
|
||||||
|
|
||||||
(def tutorial-map-tmx (load-tiled-xml-file "./tiled/tutorial.tmx"))
|
|
||||||
(def level-001-map-tmx (load-tiled-xml-file "./tiled/level_001.tmx"))
|
|
||||||
|
|
||||||
(defn parse-gid [gid]
|
|
||||||
(let [gid-long (Long/parseLong (str gid))
|
|
||||||
h-flip (bit-test gid-long 31) ; Bit 32 (horizontal flip)
|
|
||||||
v-flip (bit-test gid-long 30) ; Bit 31 (vertical flip)
|
|
||||||
d-flip (bit-test gid-long 29) ; Bit 30 (diagonal flip)
|
|
||||||
tile-id (bit-and gid-long 0x0FFFFFFF)] ; Clear top 4 bits to get actual tile ID
|
|
||||||
{:tile-id tile-id
|
|
||||||
:colliders (get colliders tile-id)
|
|
||||||
:h-flip h-flip
|
|
||||||
:v-flip v-flip
|
|
||||||
:d-flip d-flip}))
|
|
||||||
|
|
||||||
(defn parse-map
|
|
||||||
[map-tmx]
|
|
||||||
(let [tiles (-> (filter #(= (:tag %) :layer) (:content map-tmx))
|
|
||||||
first :content first :content first str/split-lines
|
|
||||||
(->>
|
(->>
|
||||||
(mapv #(str/split % #","))
|
(remove empty?)
|
||||||
(mapv (fn [row] (mapv parse-gid row)))))]
|
(map #(str/split % #","))
|
||||||
{:tiles
|
(map (fn [row] (map Integer/parseInt row)))))
|
||||||
(vec (map-indexed
|
|
||||||
|
tiles (vec (map-indexed
|
||||||
(fn [y row]
|
(fn [y row]
|
||||||
(vec (map-indexed
|
(vec (map-indexed
|
||||||
(fn [x tile] (conj {:x (* x 25) :y (* y 25)} tile))
|
(fn [x tile-id] {:x (* x 25) :y (* y 25) :tile-id tile-id})
|
||||||
row))) tiles))
|
row))) tile-nums))]
|
||||||
|
|
||||||
:objects
|
(hash-map
|
||||||
(let [objects (:content (first (filter #(= (get-in % [:attrs :name]) "objects") (:content map-tmx))))]
|
:tiles tiles
|
||||||
(vec (map (fn [obj]
|
:wall-colliders wall-colliders
|
||||||
(let [attrs (dissoc (:attrs obj) :id)
|
:spawns (vec (reduce (fn [acc tag]
|
||||||
tags (flatten (map :content (:content obj)))
|
(if (= (:tag tag) :object)
|
||||||
properties (map #(hash-map (keyword (get-in % [:attrs :name])) (get-in % [:attrs :value])) tags)]
|
(conj acc
|
||||||
|
(hash-map
|
||||||
|
:name (get-in tag [:attrs :name])
|
||||||
|
:x (Math/round (Double/parseDouble (get-in tag [:attrs :x])))
|
||||||
|
:y (Math/round (Double/parseDouble (get-in tag [:attrs :y])))))
|
||||||
|
acc)) '() (:content (first (filter #(= (get-in % [:attrs :name]) "spawns") tags))))))))
|
||||||
|
|
||||||
(into attrs properties))) objects)))
|
|
||||||
:spawns
|
|
||||||
(let [spawns (first (filter #(= (get-in % [:attrs :name]) "spawns") (:content map-tmx)))
|
|
||||||
player-1 (first (filter #(= (get-in % [:attrs :name]) "player_1") (:content spawns)))
|
|
||||||
player-2 (first (filter #(= (get-in % [:attrs :name]) "player_2") (:content spawns)))
|
|
||||||
round-num-str #(Double/parseDouble (format "%.2f" (Double/parseDouble %)))
|
|
||||||
extract-pos (fn [tag] {:x (round-num-str (get-in tag [:attrs :x]))
|
|
||||||
:y (round-num-str (get-in tag [:attrs :y]))})]
|
|
||||||
{:player-1 (extract-pos player-1)
|
|
||||||
:player-2 (extract-pos player-2)})}))
|
|
||||||
(def levels {:levels {:level-001 (parse-map level-001-map-tmx)
|
|
||||||
:tutorial (parse-map tutorial-map-tmx)}
|
|
||||||
:colliders colliders})
|
|
||||||
|
|
||||||
(pprint (get-in levels [:levels :tutorial :objects]))
|
|
||||||
;; (pprint/pprint {:row (first (:tiles level-001))})
|
;; (pprint/pprint {:row (first (:tiles level-001))})
|
||||||
;; (pprint/pprint (pr-str level-001))
|
;; (pprint/pprint (pr-str level-001))
|
||||||
|
|
||||||
(fs/write-lines "../levels.fnl" ["(local levels"
|
(fs/write-lines "../levels.fnl" ["(local levels"
|
||||||
(str/replace (pr-str levels) #",+" "")
|
(str/replace (pr-str {:level01 level-001}) #",+" "")
|
||||||
")\n\n"
|
")\n\n"
|
||||||
"{ :levels levels }"])
|
"{ :levels levels }"])
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 649 B After Width: | Height: | Size: 549 B |
|
|
@ -31,7 +31,7 @@
|
||||||
10,11,11,11,11,11,11,11,11,11,11,11,10,11,11,11,11,11,11,11,10,11,11,11,11,11,11,11,11,10,11,11,11,10,11,11,11,11,11,11,11,11,10,11,11,11,11,11,11,10,
|
10,11,11,11,11,11,11,11,11,11,11,11,10,11,11,11,11,11,11,11,10,11,11,11,11,11,11,11,11,10,11,11,11,10,11,11,11,11,11,11,11,11,10,11,11,11,11,11,11,10,
|
||||||
10,11,11,11,11,11,11,11,11,11,11,11,10,11,11,11,11,11,11,11,10,11,11,11,11,11,11,11,11,10,11,11,11,10,11,11,11,11,11,11,11,11,8,11,11,11,11,11,11,10,
|
10,11,11,11,11,11,11,11,11,11,11,11,10,11,11,11,11,11,11,11,10,11,11,11,11,11,11,11,11,10,11,11,11,10,11,11,11,11,11,11,11,11,8,11,11,11,11,11,11,10,
|
||||||
10,11,11,11,11,11,11,11,11,11,11,11,8,11,11,11,11,11,11,11,8,11,11,11,11,11,11,11,11,8,11,11,11,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10,
|
10,11,11,11,11,11,11,11,11,11,11,11,8,11,11,11,11,11,11,11,8,11,11,11,11,11,11,11,11,8,11,11,11,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10,
|
||||||
10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,2147483650,11,11,11,11,11,11,11,11,11,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10,
|
10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10,
|
||||||
10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,9,11,11,11,11,11,11,11,11,9,11,11,11,14,18,11,11,17,1,1,1,1,12,1,1,1,18,11,11,20,
|
10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,9,11,11,11,11,11,11,11,11,9,11,11,11,14,18,11,11,17,1,1,1,1,12,1,1,1,18,11,11,20,
|
||||||
10,11,11,11,11,11,11,11,11,11,11,11,9,11,11,11,11,11,11,11,10,11,11,11,11,11,11,11,11,10,11,11,11,10,11,11,11,11,11,11,11,11,10,11,11,11,11,11,11,10,
|
10,11,11,11,11,11,11,11,11,11,11,11,9,11,11,11,11,11,11,11,10,11,11,11,11,11,11,11,11,10,11,11,11,10,11,11,11,11,11,11,11,11,10,11,11,11,11,11,11,10,
|
||||||
10,11,11,11,11,11,11,11,11,11,11,11,10,11,11,11,11,11,11,11,10,11,11,11,11,11,11,11,11,10,11,11,11,10,11,11,11,11,11,11,11,11,10,11,11,11,11,11,11,10,
|
10,11,11,11,11,11,11,11,11,11,11,11,10,11,11,11,11,11,11,11,10,11,11,11,11,11,11,11,11,10,11,11,11,10,11,11,11,11,11,11,11,11,10,11,11,11,11,11,11,10,
|
||||||
|
|
@ -60,10 +60,10 @@
|
||||||
</data>
|
</data>
|
||||||
</layer>
|
</layer>
|
||||||
<objectgroup id="2" name="spawns">
|
<objectgroup id="2" name="spawns">
|
||||||
<object id="1" name="player_1" x="569.372" y="697.644">
|
<object id="1" name="spawn1" x="569.372" y="697.644">
|
||||||
<point/>
|
<point/>
|
||||||
</object>
|
</object>
|
||||||
<object id="3" name="player_2" x="679.319" y="590.314">
|
<object id="3" name="spawn2" x="679.319" y="590.314">
|
||||||
<point/>
|
<point/>
|
||||||
</object>
|
</object>
|
||||||
</objectgroup>
|
</objectgroup>
|
||||||
|
|
|
||||||
|
|
@ -17,14 +17,4 @@
|
||||||
<object id="1" x="1.12811" y="2.08266" width="22.8225" height="20.6531"/>
|
<object id="1" x="1.12811" y="2.08266" width="22.8225" height="20.6531"/>
|
||||||
</objectgroup>
|
</objectgroup>
|
||||||
</tile>
|
</tile>
|
||||||
<tile id="3">
|
|
||||||
<objectgroup draworder="index" id="2">
|
|
||||||
<object id="1" x="2.0113" y="1.87259" width="20.9453" height="21.084"/>
|
|
||||||
</objectgroup>
|
|
||||||
</tile>
|
|
||||||
<tile id="5">
|
|
||||||
<objectgroup draworder="index" id="2">
|
|
||||||
<object id="1" x="0.0892797" y="4.0196" width="24.8292" height="17.8243"/>
|
|
||||||
</objectgroup>
|
|
||||||
</tile>
|
|
||||||
</tileset>
|
</tileset>
|
||||||
|
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<map version="1.10" tiledversion="1.12.1" orientation="orthogonal" renderorder="right-down" width="50" height="50" tilewidth="25" tileheight="25" infinite="0" nextlayerid="5" nextobjectid="19">
|
|
||||||
<tileset firstgid="1" source="walls.tsx"/>
|
|
||||||
<tileset firstgid="21" source="objects.tsx"/>
|
|
||||||
<layer id="1" name="bg" width="50" height="50">
|
|
||||||
<data encoding="csv">
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2147483651,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,2,1,1,1,1,1,1,1,1,1,8,1,1,1,0,0,1,1,1,8,1,1,1,1,1,1,1,1,1,2147483650,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,1,1,1,1,1,2147483658,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,1,1,1,1,1,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,1,1,1,1,1,2147483658,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,1,1,1,1,1,0,0,1,2147483658,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1,1,1,1,1,1,1,1,2147483650,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
|
||||||
</data>
|
|
||||||
</layer>
|
|
||||||
<objectgroup id="2" name="spawns">
|
|
||||||
<object id="1" name="player_1" x="562.031" y="1164.44">
|
|
||||||
<point/>
|
|
||||||
</object>
|
|
||||||
<object id="2" name="player_2" x="687.512" y="1162.99">
|
|
||||||
<point/>
|
|
||||||
</object>
|
|
||||||
</objectgroup>
|
|
||||||
<objectgroup id="3" name="objects">
|
|
||||||
<object id="3" type="charging_station" x="550" y="1025" width="75" height="50">
|
|
||||||
<properties>
|
|
||||||
<property name="dir" value="n"/>
|
|
||||||
</properties>
|
|
||||||
</object>
|
|
||||||
<object id="4" name="wait_for_2_players" type="h_door" x="650" y="1000" width="50" height="25"/>
|
|
||||||
<object id="6" name="waiting_for_player2" type="info_pad" x="650" y="1025" width="25" height="25"/>
|
|
||||||
<object id="7" name="waiting_for_player2" type="info_pad" x="675" y="1025" width="25" height="25"/>
|
|
||||||
<object id="15" name="controls" type="info_pad" x="550" y="1150" width="25" height="25"/>
|
|
||||||
<object id="16" name="controls" type="info_pad" x="675" y="1150" width="25" height="25"/>
|
|
||||||
</objectgroup>
|
|
||||||
<objectgroup id="4" name="Layer via Copy">
|
|
||||||
<object id="18" name="waiting_for_player2" type="info_pad" x="675" y="1025" width="25" height="25"/>
|
|
||||||
</objectgroup>
|
|
||||||
</map>
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"activeFile": "tutorial.tmx",
|
"activeFile": "level_001.tmx",
|
||||||
"expandedProjectPaths": [
|
"expandedProjectPaths": [
|
||||||
"/Users/she0001t/personal_projects/fennel_love2d_experiments/two_player_cleaning_game",
|
"/Users/she0001t/personal_projects/fennel_love2d_experiments/two_player_cleaning_game",
|
||||||
".",
|
".",
|
||||||
|
|
@ -7,14 +7,11 @@
|
||||||
],
|
],
|
||||||
"fileStates": {
|
"fileStates": {
|
||||||
"level_001.tmx": {
|
"level_001.tmx": {
|
||||||
"expandedObjectLayers": [
|
"scale": 2.4998,
|
||||||
2
|
"selectedLayer": 0,
|
||||||
],
|
|
||||||
"scale": 0.5497,
|
|
||||||
"selectedLayer": 1,
|
|
||||||
"viewCenter": {
|
"viewCenter": {
|
||||||
"x": 624.886301619065,
|
"x": 149.0119209536763,
|
||||||
"y": 614.8808440967802
|
"y": 73.8059044723578
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"map_tileset.tsx": {
|
"map_tileset.tsx": {
|
||||||
|
|
@ -24,22 +21,11 @@
|
||||||
},
|
},
|
||||||
"objects.tsx": {
|
"objects.tsx": {
|
||||||
"scaleInDock": 1,
|
"scaleInDock": 1,
|
||||||
"scaleInEditor": 2.8063
|
"scaleInEditor": 6.3771
|
||||||
},
|
|
||||||
"tutorial.tmx": {
|
|
||||||
"expandedObjectLayers": [
|
|
||||||
2
|
|
||||||
],
|
|
||||||
"scale": 1.1429,
|
|
||||||
"selectedLayer": 0,
|
|
||||||
"viewCenter": {
|
|
||||||
"x": 657.5378423309126,
|
|
||||||
"y": 883.7168606177268
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"walls.tsx": {
|
"walls.tsx": {
|
||||||
"scaleInDock": 1.4397,
|
"scaleInDock": 1.4397,
|
||||||
"scaleInEditor": 5.9643
|
"scaleInEditor": 8.2977
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"last.exportedFilePath": "/Users/she0001t/personal_projects/fennel_love2d_experiments/two_player_cleaning_game/assets/tiled",
|
"last.exportedFilePath": "/Users/she0001t/personal_projects/fennel_love2d_experiments/two_player_cleaning_game/assets/tiled",
|
||||||
|
|
@ -53,15 +39,13 @@
|
||||||
"openFiles": [
|
"openFiles": [
|
||||||
"level_001.tmx",
|
"level_001.tmx",
|
||||||
"walls.tsx",
|
"walls.tsx",
|
||||||
"objects.tsx",
|
"objects.tsx"
|
||||||
"tutorial.tmx"
|
|
||||||
],
|
],
|
||||||
"project": "untitled.tiled-project",
|
"project": "untitled.tiled-project",
|
||||||
"recentFiles": [
|
"recentFiles": [
|
||||||
"level_001.tmx",
|
|
||||||
"walls.tsx",
|
|
||||||
"objects.tsx",
|
"objects.tsx",
|
||||||
"tutorial.tmx",
|
"walls.tsx",
|
||||||
|
"level_001.tmx",
|
||||||
"map_tileset.tsx"
|
"map_tileset.tsx"
|
||||||
],
|
],
|
||||||
"tileset.lastUsedFormat": "tsx",
|
"tileset.lastUsedFormat": "tsx",
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<tileset version="1.10" tiledversion="1.12.1" name="walls" tilewidth="25" tileheight="25" tilecount="20" columns="13">
|
<tileset version="1.10" tiledversion="1.12.1" name="walls" tilewidth="25" tileheight="25" tilecount="20" columns="20">
|
||||||
<image source="../walls.png" width="325" height="25"/>
|
<image source="../walls.png" width="500" height="25"/>
|
||||||
<tile id="0">
|
<tile id="0">
|
||||||
<objectgroup draworder="index" id="2">
|
<objectgroup draworder="index" id="2">
|
||||||
<object id="1" x="0.141111" y="4.02527" width="24.9767" height="17.7059"/>
|
<object id="1" x="0.141111" y="4.23334" width="24.9767" height="17.4978"/>
|
||||||
</objectgroup>
|
</objectgroup>
|
||||||
</tile>
|
</tile>
|
||||||
<tile id="1">
|
<tile id="1">
|
||||||
|
|
@ -14,63 +14,57 @@
|
||||||
</tile>
|
</tile>
|
||||||
<tile id="2">
|
<tile id="2">
|
||||||
<objectgroup draworder="index" id="2">
|
<objectgroup draworder="index" id="2">
|
||||||
<object id="1" x="10.0692" y="4.10365" width="15.0247" height="17.8045"/>
|
<object id="1" x="-0.264752" y="4.10365" width="15.0247" height="17.8045"/>
|
||||||
<object id="2" x="10.13" y="20.5323" width="4.83172" height="4.30221"/>
|
<object id="2" x="10.0606" y="-0.0661879" width="4.83172" height="4.30221"/>
|
||||||
</objectgroup>
|
</objectgroup>
|
||||||
</tile>
|
</tile>
|
||||||
<tile id="3">
|
<tile id="3">
|
||||||
<objectgroup draworder="index" id="2">
|
<objectgroup draworder="index" id="2">
|
||||||
<object id="1" x="9.99984" y="3.9681" width="15.157" height="17.8707"/>
|
<object id="1" x="-0.264752" y="4.03746" width="15.157" height="17.8707"/>
|
||||||
|
<object id="2" x="10.0606" y="20.1873" width="4.76553" height="4.96409"/>
|
||||||
</objectgroup>
|
</objectgroup>
|
||||||
</tile>
|
</tile>
|
||||||
<tile id="4">
|
<tile id="4">
|
||||||
<objectgroup draworder="index" id="2">
|
<objectgroup draworder="index" id="2">
|
||||||
<object id="2" x="10.13" y="0.0242897" width="4.83172" height="21.8801"/>
|
<object id="1" x="10.1267" y="4.16984" width="14.7599" height="17.6722"/>
|
||||||
|
<object id="2" x="10.0606" y="18.3341" width="4.83172" height="6.5526"/>
|
||||||
</objectgroup>
|
</objectgroup>
|
||||||
</tile>
|
</tile>
|
||||||
<tile id="5">
|
<tile id="5">
|
||||||
<objectgroup draworder="index" id="2">
|
<objectgroup draworder="index" id="2">
|
||||||
<object id="1" x="9.99437" y="4.10365" width="4.89882" height="20.853"/>
|
<object id="1" x="9.99437" y="4.10365" width="15.0247" height="17.8707"/>
|
||||||
</objectgroup>
|
</objectgroup>
|
||||||
</tile>
|
</tile>
|
||||||
<tile id="6">
|
<tile id="6">
|
||||||
<objectgroup draworder="index" id="2">
|
<objectgroup draworder="index" id="2">
|
||||||
<object id="1" x="10.1259" y="0.0116848" width="4.70022" height="24.9386"/>
|
<object id="1" x="1.77636e-15" y="4.10365" width="14.8261" height="18.0031"/>
|
||||||
</objectgroup>
|
</objectgroup>
|
||||||
</tile>
|
</tile>
|
||||||
<tile id="7">
|
<tile id="7">
|
||||||
<objectgroup draworder="index" id="2">
|
<objectgroup draworder="index" id="2">
|
||||||
<object id="1" x="0.0701754" y="4.0888" width="24.8722" height="17.7532"/>
|
<object id="1" x="10.1267" y="0.0661879" width="4.89791" height="21.7758"/>
|
||||||
<object id="2" x="10.0565" y="16.9227" width="4.85487" height="7.90651"/>
|
|
||||||
</objectgroup>
|
</objectgroup>
|
||||||
</tile>
|
</tile>
|
||||||
<tile id="8">
|
<tile id="8">
|
||||||
<objectgroup draworder="index" id="2">
|
<objectgroup draworder="index" id="2">
|
||||||
<object id="1" x="0.0734307" y="4.10365" width="24.7335" height="17.9363"/>
|
<object id="1" x="10.0606" y="4.10365" width="4.89791" height="20.8492"/>
|
||||||
<object id="2" x="10.1952" y="0" width="4.71616" height="7.21296"/>
|
|
||||||
</objectgroup>
|
</objectgroup>
|
||||||
</tile>
|
</tile>
|
||||||
<tile id="9">
|
<tile id="9">
|
||||||
<objectgroup draworder="index" id="2">
|
<objectgroup draworder="index" id="2">
|
||||||
<object id="1" x="9.99437" y="-0.0661879" width="4.96409" height="24.8867"/>
|
<object id="1" x="9.99437" y="-0.0661879" width="4.96409" height="24.8867"/>
|
||||||
<object id="2" x="14.0098" y="4.09197" width="10.9581" height="17.963"/>
|
|
||||||
</objectgroup>
|
|
||||||
</tile>
|
|
||||||
<tile id="10">
|
|
||||||
<objectgroup draworder="index" id="2">
|
|
||||||
<object id="1" x="10.0565" y="1.77636e-15" width="4.85487" height="24.9679"/>
|
|
||||||
<object id="2" x="0.0693553" y="4.02261" width="24.9679" height="17.963"/>
|
|
||||||
</objectgroup>
|
</objectgroup>
|
||||||
</tile>
|
</tile>
|
||||||
<tile id="11">
|
<tile id="11">
|
||||||
<objectgroup draworder="index" id="2">
|
<objectgroup draworder="index" id="2">
|
||||||
<object id="1" x="2.98861" y="4.03746" width="21.898" height="17.7384"/>
|
<object id="1" x="-0.132376" y="4.03746" width="25.019" height="17.7384"/>
|
||||||
|
<object id="2" x="10.1929" y="13.9656" width="4.76553" height="11.1858"/>
|
||||||
</objectgroup>
|
</objectgroup>
|
||||||
</tile>
|
</tile>
|
||||||
<tile id="12">
|
<tile id="12">
|
||||||
<objectgroup draworder="index" id="2">
|
<objectgroup draworder="index" id="2">
|
||||||
<object id="1" x="9.984" y="3.88925" width="11.9834" height="18.1041"/>
|
<object id="1" x="0.0661879" y="4.23603" width="24.9528" height="17.3412"/>
|
||||||
<object id="2" x="9.99437" y="0.0661879" width="4.89791" height="24.83"/>
|
<object id="2" x="9.99437" y="0.0661879" width="4.89791" height="10.1267"/>
|
||||||
</objectgroup>
|
</objectgroup>
|
||||||
</tile>
|
</tile>
|
||||||
<tile id="13">
|
<tile id="13">
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 419 B After Width: | Height: | Size: 559 B |
File diff suppressed because one or more lines are too long
|
|
@ -1,7 +1,6 @@
|
||||||
;; deps
|
;; deps
|
||||||
(local levels (. (require "levels.fnl") :levels))
|
(local levels (. (require "levels.fnl") :levels))
|
||||||
(local bump (require "bump"))
|
(local bump (require "bump"))
|
||||||
(local network (require "network.fnl"))
|
|
||||||
|
|
||||||
;; colors
|
;; colors
|
||||||
(lambda color [full-r full-g full-b]
|
(lambda color [full-r full-g full-b]
|
||||||
|
|
@ -29,7 +28,6 @@
|
||||||
(love.graphics.setColor (unpack (. color-pallet color))))
|
(love.graphics.setColor (unpack (. color-pallet color))))
|
||||||
|
|
||||||
;; global vars
|
;; global vars
|
||||||
(var level-key "tutorial")
|
|
||||||
(var player-art nil) ; 25x50 pixels each player is 25x25
|
(var player-art nil) ; 25x50 pixels each player is 25x25
|
||||||
(var dust-sprite nil) ; 35x35 pixels
|
(var dust-sprite nil) ; 35x35 pixels
|
||||||
(var battery-bar-sprite nil)
|
(var battery-bar-sprite nil)
|
||||||
|
|
@ -53,7 +51,7 @@
|
||||||
:scale scale
|
:scale scale
|
||||||
:canvas nil}))
|
:canvas nil}))
|
||||||
(local camera {:x 0 :y 0})
|
(local camera {:x 0 :y 0})
|
||||||
(local debug true)
|
(local debug false)
|
||||||
(lambda debug-print [obj]
|
(lambda debug-print [obj]
|
||||||
(print (fennel.view obj)))
|
(print (fennel.view obj)))
|
||||||
|
|
||||||
|
|
@ -61,46 +59,34 @@
|
||||||
(lambda add-collider-debug-box [x y w h]
|
(lambda add-collider-debug-box [x y w h]
|
||||||
(table.insert collider-debug-boxes {: x : y :width w :height h}))
|
(table.insert collider-debug-boxes {: x : y :width w :height h}))
|
||||||
|
|
||||||
(var bump-world nil)
|
(local bump-world (bump.newWorld 25))
|
||||||
(local player { :x 50 :y 50 :w 25 :h 25 :speed 80 :battery 100 :rot 0 })
|
(local player { :x 50 :y 50 :w 25 :h 25 :speed 80 :battery 100 :rot 0 })
|
||||||
|
|
||||||
(lambda mirror-collider [collider]
|
|
||||||
"Mirror a collider box horizontally within a 25-unit tile (center is at 12.5)
|
|
||||||
Transforms collider positions so they're reflected across the vertical center line."
|
|
||||||
{
|
|
||||||
:x (- 25 collider.x collider.width)
|
|
||||||
:y collider.y
|
|
||||||
:width collider.width
|
|
||||||
:height collider.height
|
|
||||||
})
|
|
||||||
|
|
||||||
(fn load-walls []
|
(fn load-walls []
|
||||||
|
(set walls.sprite (love.graphics.newImage "assets/walls.png"))
|
||||||
(set walls.batch (love.graphics.newSpriteBatch walls.sprite 2500))
|
(set walls.batch (love.graphics.newSpriteBatch walls.sprite 2500))
|
||||||
;; load quads
|
;; load quads
|
||||||
(let [(w h) (walls.sprite:getDimensions)]
|
(let [(w h) (walls.sprite:getDimensions)]
|
||||||
(for [i 0 19 1]
|
(for [i 0 19 1]
|
||||||
(table.insert walls.quads (love.graphics.newQuad (* i 25) 0 25 25 w h))))
|
(table.insert walls.quads (love.graphics.newQuad (* i 25) 0 25 25 w h))))
|
||||||
;; fill batch
|
;; fill batch
|
||||||
(each [_ row (pairs (. levels :levels level-key :tiles))]
|
(each [_ row (pairs (. levels :level01 :tiles))]
|
||||||
(each [_ tile (pairs row)]
|
(each [_ tile (pairs row)]
|
||||||
(let [
|
(let [
|
||||||
x tile.x
|
x tile.x
|
||||||
y tile.y
|
y tile.y
|
||||||
id tile.tile-id
|
id tile.tile-id
|
||||||
colliders tile.colliders]
|
colliders (or (. levels.level01.wall-colliders (- id 1)) [])]
|
||||||
(if (and (> id 0) (< id 21)) ;; 1-20 are wall tiles
|
(if (and (> id 0) (< id 21)) ;; 1-20 are wall tiles
|
||||||
(do
|
(do
|
||||||
; (print (fennel.view {:quad (. walls.quads id) : x : y : id}))
|
; (print (fennel.view {:quad (. walls.quads id) : x : y : id}))
|
||||||
(walls.batch:add (. walls.quads id) (if tile.h-flip (+ x 25) x) y 0 (if tile.h-flip -1 1) 1)
|
(walls.batch:add (. walls.quads id) x y)
|
||||||
(each [_ collider (pairs colliders)]
|
(each [_ collider (pairs colliders)]
|
||||||
(let [
|
(bump-world:add {: x : y :name :wall :behavior :block} (+ x collider.x) (+ y collider.y) collider.width collider.height)
|
||||||
mirrored-collider (if tile.h-flip (mirror-collider collider) collider)
|
|
||||||
collider-x (+ x mirrored-collider.x)
|
|
||||||
collider-y (+ y mirrored-collider.y)]
|
|
||||||
(bump-world:add {: x : y :name :wall :behavior :block} collider-x collider-y mirrored-collider.width mirrored-collider.height)
|
|
||||||
(table.insert
|
(table.insert
|
||||||
collider-debug-boxes
|
collider-debug-boxes
|
||||||
{:x collider-x :y collider-y :width mirrored-collider.width :height mirrored-collider.height})))))))))
|
{:x (+ x collider.x) :y (+ y collider.y) :width collider.width :height collider.height}))
|
||||||
|
))))))
|
||||||
|
|
||||||
(lambda create-charging-station [x y]
|
(lambda create-charging-station [x y]
|
||||||
(let [
|
(let [
|
||||||
|
|
@ -136,29 +122,33 @@
|
||||||
(table.insert objects.list station)))
|
(table.insert objects.list station)))
|
||||||
|
|
||||||
(fn load-objects []
|
(fn load-objects []
|
||||||
|
(set objects.sprite (love.graphics.newImage "assets/objects.png"))
|
||||||
(let [(w h) (objects.sprite:getDimensions)]
|
(let [(w h) (objects.sprite:getDimensions)]
|
||||||
(set objects.quads.charging-pad (love.graphics.newQuad 0 0 25 25 w h))
|
(set objects.quads.charging-pad (love.graphics.newQuad 0 0 25 25 w h))
|
||||||
(set objects.quads.charging-pad-active (love.graphics.newQuad 50 0 25 25 w h))
|
(set objects.quads.charging-pad-active (love.graphics.newQuad 50 0 25 25 w h))
|
||||||
(set objects.quads.charging-station (love.graphics.newQuad 25 0 25 25 w h))))
|
(set objects.quads.charging-station (love.graphics.newQuad 25 0 25 25 w h)))
|
||||||
|
|
||||||
(fn load-player []
|
(each [_ row (pairs (. levels :level01 :tiles))]
|
||||||
(set player.battery 100)
|
(each [_ tile (pairs row)]
|
||||||
(set player.x (. levels :levels level-key :spawns :player-1 :x))
|
(let [
|
||||||
(set player.y (. levels :levels level-key :spawns :player-1 :y))
|
x tile.x
|
||||||
(bump-world:add player player.x player.y player.w player.h))
|
y tile.y
|
||||||
|
id tile.tile-id]
|
||||||
|
(when (> id 20) ;; 21+ are object tiles
|
||||||
|
(when (= id 22) (create-charging-station x y)))))))
|
||||||
|
|
||||||
|
|
||||||
|
(fn love.load []
|
||||||
|
(love.window.setMode screen.screen-w screen.screen-h)
|
||||||
|
(tset screen :canvas (love.graphics.newCanvas screen.canvas-w screen.canvas-h))
|
||||||
|
(bump-world:add player player.x player.y player.w player.h)
|
||||||
|
|
||||||
(lambda load-level [lvl-name]
|
|
||||||
(set bump-world (bump.newWorld 25))
|
|
||||||
(set level-key :tutorial)
|
|
||||||
;; set player 1 location
|
|
||||||
(load-player)
|
|
||||||
(load-walls)
|
(load-walls)
|
||||||
(load-objects))
|
(load-objects)
|
||||||
|
|
||||||
(fn load-assets []
|
;; load world images
|
||||||
(set objects.sprite (love.graphics.newImage "assets/objects.png"))
|
|
||||||
(set walls.sprite (love.graphics.newImage "assets/walls.png"))
|
|
||||||
(set battery-bar-sprite (love.graphics.newImage "assets/battery_bar.png"))
|
(set battery-bar-sprite (love.graphics.newImage "assets/battery_bar.png"))
|
||||||
|
|
||||||
(set player-art
|
(set player-art
|
||||||
(let [
|
(let [
|
||||||
player-sprite (love.graphics.newImage "assets/player.png")
|
player-sprite (love.graphics.newImage "assets/player.png")
|
||||||
|
|
@ -179,18 +169,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
(fn love.load []
|
|
||||||
(love.window.setMode screen.screen-w screen.screen-h)
|
|
||||||
(tset screen :canvas (love.graphics.newCanvas screen.canvas-w screen.canvas-h))
|
|
||||||
(load-assets)
|
|
||||||
(load-level :tutorial)
|
|
||||||
;; Initialize network
|
|
||||||
(network.init)
|
|
||||||
(network.send-msg "REGISTER")
|
|
||||||
|
|
||||||
|
(set dust-sprite (love.graphics.newImage "assets/dust_001.png"))
|
||||||
|
; (print (fennel.view game-state))
|
||||||
;; start a thread listening on stdin
|
;; start a thread listening on stdin
|
||||||
(: (love.thread.newThread "require('love.event')
|
(: (love.thread.newThread "require('love.event')
|
||||||
while 1 do love.event.push('stdin', io.read('*line')) end") :start))
|
while 1 do love.event.push('stdin', io.read('*line')) end") :start))
|
||||||
|
|
@ -201,6 +182,7 @@ while 1 do love.event.push('stdin', io.read('*line')) end") :start))
|
||||||
(print (if ok (fennel.view val) val))))
|
(print (if ok (fennel.view val) val))))
|
||||||
|
|
||||||
;; drawing
|
;; drawing
|
||||||
|
|
||||||
(fn draw-world []
|
(fn draw-world []
|
||||||
(reset-color)
|
(reset-color)
|
||||||
(love.graphics.draw walls.batch)
|
(love.graphics.draw walls.batch)
|
||||||
|
|
@ -246,15 +228,7 @@ while 1 do love.event.push('stdin', io.read('*line')) end") :start))
|
||||||
(let
|
(let
|
||||||
[(items len) (bump-world:queryRect player.x player.y 25 25 #(= $1.behavior "hover"))]
|
[(items len) (bump-world:queryRect player.x player.y 25 25 #(= $1.behavior "hover"))]
|
||||||
(each [_ item (pairs items)]
|
(each [_ item (pairs items)]
|
||||||
(item:hover-cb dt)))
|
(item:hover-cb dt))))
|
||||||
|
|
||||||
;; Network updates
|
|
||||||
(network.update dt))
|
|
||||||
; (set net-state.net-update-timer (+ net-state.net-update-timer dt))
|
|
||||||
; (when (>= net-state.net-update-timer net-state.net-update-interval)
|
|
||||||
; (when net-state.connected
|
|
||||||
; (network.send-update player.x player.y player.rot player.battery))
|
|
||||||
; (set net-state.net-update-timer 0)))
|
|
||||||
|
|
||||||
(fn draw-ui []
|
(fn draw-ui []
|
||||||
(love.graphics.push)
|
(love.graphics.push)
|
||||||
|
|
@ -339,8 +313,3 @@ while 1 do love.event.push('stdin', io.read('*line')) end") :start))
|
||||||
; (love.graphics.print "Hello from Fennel!\nPress any key to quit" 10 10))
|
; (love.graphics.print "Hello from Fennel!\nPress any key to quit" 10 10))
|
||||||
|
|
||||||
(fn love.keypressed [key] nil )
|
(fn love.keypressed [key] nil )
|
||||||
|
|
||||||
(fn love.quit []
|
|
||||||
"Clean up before game closes"
|
|
||||||
(network.close)
|
|
||||||
false)
|
|
||||||
|
|
|
||||||
|
|
@ -1,133 +0,0 @@
|
||||||
(local socket (require "socket"))
|
|
||||||
|
|
||||||
;; Guide: https://www.love2d.org/wiki/Tutorial:Networking_with_UDP
|
|
||||||
|
|
||||||
;; Configuration
|
|
||||||
(local config {
|
|
||||||
:host "localhost"
|
|
||||||
:port 9999
|
|
||||||
})
|
|
||||||
|
|
||||||
;; Network state
|
|
||||||
(local state {
|
|
||||||
:socket nil
|
|
||||||
:connected false
|
|
||||||
; :player-id nil
|
|
||||||
; :last-error nil
|
|
||||||
})
|
|
||||||
|
|
||||||
;; Callback functions for network events
|
|
||||||
; (local callbacks {
|
|
||||||
; :on-ready (fn [])
|
|
||||||
; :on-wait (fn [])
|
|
||||||
; :on-opponent-update (fn [data])
|
|
||||||
; :on-error (fn [error])
|
|
||||||
; })
|
|
||||||
|
|
||||||
(fn init []
|
|
||||||
"Initialize UDP connection to server
|
|
||||||
Returns true on success, false on error"
|
|
||||||
(let [udp (socket.udp)]
|
|
||||||
(udp:setpeername config.host config.port)
|
|
||||||
(udp:settimeout 0) ; non-blocking
|
|
||||||
|
|
||||||
(set state.socket udp)
|
|
||||||
(set state.connected true)))
|
|
||||||
|
|
||||||
(fn send-msg [dg]
|
|
||||||
(state.socket:send dg))
|
|
||||||
|
|
||||||
(fn update [dt]
|
|
||||||
(let [(data msg) (state.socket:receive)]
|
|
||||||
(when data
|
|
||||||
(print (fennel.view {: data : msg})))))
|
|
||||||
|
|
||||||
; (fn register []
|
|
||||||
; "Send REGISTER message to server"
|
|
||||||
; (when state.socket
|
|
||||||
; (try
|
|
||||||
; (state.socket:send "REGISTER")
|
|
||||||
; (catch err
|
|
||||||
; (set state.last-error err)
|
|
||||||
; (when callbacks.on-error (callbacks.on-error err))))))
|
|
||||||
|
|
||||||
; (fn split-string [str delimiter]
|
|
||||||
; "Split string by delimiter into table"
|
|
||||||
; (local result [])
|
|
||||||
; (local current "")
|
|
||||||
; (each [i 1 (length str) 1]
|
|
||||||
; (let [char (string.sub str i i)]
|
|
||||||
; (if (= char delimiter)
|
|
||||||
; (do
|
|
||||||
; (table.insert result current)
|
|
||||||
; (set current ""))
|
|
||||||
; (set current (.. current char)))))
|
|
||||||
; (table.insert result current)
|
|
||||||
; result)
|
|
||||||
|
|
||||||
; (fn parse-message [msg]
|
|
||||||
; "Parse message in format: COMMAND#arg1#arg2#..."
|
|
||||||
; (split-string msg "#"))
|
|
||||||
|
|
||||||
; (fn send-update [x y rotation battery]
|
|
||||||
; "Send player state to server in format: UPDATE#x#y#rotation#battery"
|
|
||||||
; (when state.socket
|
|
||||||
; (try
|
|
||||||
; (let [msg (.. "UPDATE#" x "#" y "#" rotation "#" battery)]
|
|
||||||
; (state.socket:send msg))
|
|
||||||
; (catch err
|
|
||||||
; (set state.last-error err)))))
|
|
||||||
|
|
||||||
; (fn update [dt]
|
|
||||||
; "Call from love.update to process incoming messages"
|
|
||||||
; (when state.socket
|
|
||||||
; (let [(msg _src-ip _src-port) (state.socket:receivefrom)]
|
|
||||||
; (when msg
|
|
||||||
; (let [parts (parse-message msg)
|
|
||||||
; command (. parts 1)]
|
|
||||||
; (match command
|
|
||||||
; "READY_TO_PLAY" (callbacks.on-ready)
|
|
||||||
; "WAIT" (callbacks.on-wait)
|
|
||||||
; "UPDATE" (callbacks.on-opponent-update {
|
|
||||||
; :x (tonumber (. parts 2))
|
|
||||||
; :y (tonumber (. parts 3))
|
|
||||||
; :rotation (tonumber (. parts 4))
|
|
||||||
; :battery (tonumber (. parts 5))
|
|
||||||
; })
|
|
||||||
; _ (print (.. "← Message: " msg))))))))
|
|
||||||
|
|
||||||
; (fn set-callback [event cb]
|
|
||||||
; "Register callback for network events
|
|
||||||
; Available events: :on-ready :on-wait :on-opponent-update :on-error"
|
|
||||||
; (tset callbacks event cb))
|
|
||||||
|
|
||||||
(fn close []
|
|
||||||
"Close network connection"
|
|
||||||
(when state.socket
|
|
||||||
(state.socket:close)
|
|
||||||
(set state.socket nil)
|
|
||||||
(set state.connected false)))
|
|
||||||
|
|
||||||
; (fn is-connected []
|
|
||||||
; "Check if currently connected to server"
|
|
||||||
; state.connected)
|
|
||||||
|
|
||||||
; (fn get-last-error []
|
|
||||||
; "Get the last error that occurred"
|
|
||||||
; state.last-error)
|
|
||||||
|
|
||||||
;; Export public API
|
|
||||||
{
|
|
||||||
: init
|
|
||||||
: update
|
|
||||||
: state
|
|
||||||
: send-msg
|
|
||||||
: close
|
|
||||||
; :register register
|
|
||||||
; :send-update send-update
|
|
||||||
; :update update
|
|
||||||
; :set-callback set-callback
|
|
||||||
; :close close
|
|
||||||
; :is-connected is-connected
|
|
||||||
; :get-last-error get-last-error
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue