Compare commits
23
Commits
a3e49dffb9
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8742ec5bb | ||
|
|
1bab945dcd | ||
|
|
eb9908839d | ||
|
|
94d371d901 | ||
|
|
2f07d74c78 | ||
|
|
ef70c6324d | ||
|
|
800880ed30 | ||
|
|
6a57b9dcfa | ||
|
|
d57951466e | ||
|
|
2377d4fa71 | ||
|
|
50671b483f | ||
|
|
52717f67a9 | ||
|
|
e4a1849669 | ||
|
|
85e2bbb48b | ||
|
|
e106d8ea65 | ||
|
|
be519d515e | ||
|
|
a848a6854b | ||
|
|
df3f1e513e | ||
|
|
20f4f4fbc2 | ||
|
|
d433141ccb | ||
|
|
4470266234 | ||
|
|
b68f71beb2 | ||
|
|
38ec8bb1ef |
@@ -0,0 +1,69 @@
|
||||
# Dino Land
|
||||
|
||||
No-build browser game using plain **HTML/CSS/JS/PHP**.
|
||||
|
||||
## Run locally
|
||||
|
||||
```bash
|
||||
cd "/Users/lawsonawhittington/.openclaw/workspace/Dino Land"
|
||||
php -S 0.0.0.0:8000
|
||||
```
|
||||
|
||||
Open on the same machine: http://localhost:8000
|
||||
|
||||
LAN test URL (from another device on your network):
|
||||
1. Find your LAN IP:
|
||||
`python3 -c "import socket;s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM);s.connect(('8.8.8.8',80));print(s.getsockname()[0]);s.close()"`
|
||||
2. Open: `http://<YOUR_LAN_IP>:8000` (example: `http://192.168.1.25:8000`)
|
||||
|
||||
## USB / thumbdrive distribution
|
||||
|
||||
Need to run Dino Land from a USB drive on unknown machines (Windows/macOS/ChromeOS)?
|
||||
See: **[thumbdrive-install.md](./thumbdrive-install.md)**
|
||||
|
||||
## Controls
|
||||
|
||||
- **Up Arrow**: jump
|
||||
- **Down Arrow**: duck
|
||||
- **Left Arrow**: walk backward
|
||||
- **Right Arrow**: walk forward
|
||||
|
||||
## Gameplay Rules
|
||||
|
||||
- TRex starts with **5 hearts** (or **10 hearts** in VIP mode).
|
||||
- Easy and Medium end with a portal that advances to the next difficulty while preserving score.
|
||||
- Moving forward earns score (**+1 per forward step unit**).
|
||||
- Water gaps cause instant death if landed in.
|
||||
- Ant collision costs 1 heart; jumping on ants crushes them.
|
||||
- Pterodactyls unlock after score > 100 (duck to avoid).
|
||||
- Meteors unlock after score > 250.
|
||||
- On death: top-10 leaderboard is shown; top-10 runs can be saved.
|
||||
|
||||
## Leaderboard persistence
|
||||
|
||||
- Backend endpoint: `api/scores.php`
|
||||
- Data file: `data/scores.json`
|
||||
|
||||
## Audio / music toggle behavior
|
||||
|
||||
- Music defaults to **OFF** when no preference exists in localStorage.
|
||||
- Toggle preference key: `dinoLand.musicEnabled` (`'1'` on, `'0'` off).
|
||||
- First user click on the music toggle resumes/unlocks Web Audio and starts procedural music when turned ON.
|
||||
- Music state is context-aware:
|
||||
- **Title/start screen** uses title music profile.
|
||||
- **Gameplay** switches profile by biome (Plains, Desert, Jungle, Snow, and Lava mapped to the Volcano profile).
|
||||
- **Death screen** switches to death music profile.
|
||||
- **Restart/respawn to start screen** returns to title profile.
|
||||
- Turning music OFF performs a short fade to mute; turning back ON fades in the current state profile.
|
||||
|
||||
## Quick verification checklist
|
||||
|
||||
1. Start server and open game.
|
||||
2. Confirm score increases while moving right.
|
||||
3. Confirm hearts drop on ant / pterodactyl / meteor collisions.
|
||||
4. Confirm water causes immediate game over.
|
||||
5. Confirm leaderboard loads and score save works.
|
||||
6. With a fresh browser profile, confirm music button starts as `🔇 Music: Off`.
|
||||
7. Click toggle to ON and confirm audible looping background music starts.
|
||||
8. Click toggle to OFF and confirm music fades/mutes.
|
||||
9. Reload page and confirm toggle state persists.
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
$storageDir = __DIR__ . '/../data';
|
||||
$storageFile = $storageDir . '/scores.json';
|
||||
|
||||
if (!is_dir($storageDir)) {
|
||||
mkdir($storageDir, 0777, true);
|
||||
}
|
||||
if (!file_exists($storageFile)) {
|
||||
file_put_contents($storageFile, json_encode([], JSON_PRETTY_PRINT));
|
||||
}
|
||||
|
||||
function loadScores(string $file): array {
|
||||
$raw = file_get_contents($file);
|
||||
if ($raw === false || trim($raw) === '') {
|
||||
return [];
|
||||
}
|
||||
$decoded = json_decode($raw, true);
|
||||
return is_array($decoded) ? $decoded : [];
|
||||
}
|
||||
|
||||
function saveScores(string $file, array $scores): void {
|
||||
usort($scores, fn($a, $b) => ($b['score'] ?? 0) <=> ($a['score'] ?? 0));
|
||||
$scores = array_slice($scores, 0, 10);
|
||||
file_put_contents($file, json_encode($scores, JSON_PRETTY_PRINT));
|
||||
}
|
||||
|
||||
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
|
||||
|
||||
if ($method === 'GET') {
|
||||
$scores = loadScores($storageFile);
|
||||
usort($scores, fn($a, $b) => ($b['score'] ?? 0) <=> ($a['score'] ?? 0));
|
||||
echo json_encode(array_slice($scores, 0, 10));
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($method === 'POST') {
|
||||
$payload = json_decode(file_get_contents('php://input') ?: '{}', true);
|
||||
$name = trim((string)($payload['name'] ?? 'Anonymous'));
|
||||
$score = (int)($payload['score'] ?? 0);
|
||||
|
||||
if ($name === '') $name = 'Anonymous';
|
||||
$name = mb_substr($name, 0, 20);
|
||||
if ($score < 0) $score = 0;
|
||||
|
||||
$scores = loadScores($storageFile);
|
||||
$scores[] = [
|
||||
'name' => $name,
|
||||
'score' => $score,
|
||||
'date' => date('c')
|
||||
];
|
||||
|
||||
saveScores($storageFile, $scores);
|
||||
|
||||
echo json_encode(['ok' => true]);
|
||||
exit;
|
||||
}
|
||||
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method Not Allowed']);
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
@@ -0,0 +1 @@
|
||||
[]
|
||||
@@ -13,6 +13,42 @@
|
||||
</div>
|
||||
|
||||
<canvas id="game" width="960" height="540" aria-label="Dino Land game canvas"></canvas>
|
||||
<button id="musicToggle" type="button" aria-pressed="false" aria-label="Toggle music">🔇 Music: Off</button>
|
||||
|
||||
<div id="startScreen">
|
||||
<div class="panel">
|
||||
<img
|
||||
class="start-logo"
|
||||
src="assets/images/dinoland-logo.png"
|
||||
alt="Dino Land logo"
|
||||
width="320"
|
||||
height="180"
|
||||
/>
|
||||
<h1>Start Dino Land</h1>
|
||||
<form id="startForm">
|
||||
<label for="startPassword">Password</label>
|
||||
<input id="startPassword" type="password" required placeholder="Enter password" />
|
||||
|
||||
<label for="accessMode">Access Mode</label>
|
||||
<select id="accessMode">
|
||||
<option value="standard" selected>Standard (5 hearts)</option>
|
||||
<option value="vip">VIP (10 hearts)</option>
|
||||
</select>
|
||||
|
||||
<div style="display: none; !important;">
|
||||
<label for="difficulty" style="display: none; !important;">Difficulty</label>
|
||||
<select id="difficulty" style="display: none; !important;">
|
||||
<option value="easy" selected>Easy</option>
|
||||
<option value="medium">Medium</option>
|
||||
<option value="hard">Hard</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<p id="startError" class="error"></p>
|
||||
<button type="submit">Start Game</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="deathScreen" class="hidden">
|
||||
<div class="panel">
|
||||
@@ -25,7 +61,7 @@
|
||||
<input id="playerName" maxlength="20" required />
|
||||
<button type="submit">Save Score</button>
|
||||
</form>
|
||||
<button id="restartBtn">Play Again</button>
|
||||
<button id="restartBtn">Restart</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
+907
-68
File diff suppressed because it is too large
Load Diff
+56
-2
@@ -25,7 +25,25 @@ body {
|
||||
border: 2px solid #1b3a47;
|
||||
background: #b5ecff;
|
||||
}
|
||||
#deathScreen {
|
||||
#musicToggle {
|
||||
position: fixed;
|
||||
right: 16px;
|
||||
bottom: 16px;
|
||||
z-index: 9;
|
||||
width: auto;
|
||||
min-width: 132px;
|
||||
padding: 10px 12px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid rgba(255,255,255,0.24);
|
||||
background: rgba(0,0,0,0.65);
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#deathScreen,
|
||||
#startScreen {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0,0,0,0.65);
|
||||
@@ -39,10 +57,46 @@ body {
|
||||
padding: 20px;
|
||||
width: min(92vw, 460px);
|
||||
}
|
||||
|
||||
.start-logo {
|
||||
display: block;
|
||||
width: min(100%, 320px);
|
||||
height: auto;
|
||||
margin: 0 auto 8px;
|
||||
}
|
||||
|
||||
.hidden { display: none !important; }
|
||||
button, input {
|
||||
button, input, select {
|
||||
font-size: 16px;
|
||||
padding: 8px 10px;
|
||||
margin-top: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
margin-top: 10px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.inline-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.inline-option input {
|
||||
width: auto;
|
||||
margin: 0;
|
||||
}
|
||||
.error {
|
||||
color: #c62828;
|
||||
min-height: 20px;
|
||||
}
|
||||
ol { padding-left: 22px; }
|
||||
|
||||
/* removed unused play-again-row styles */
|
||||
|
||||
.heart {
|
||||
display: inline-block;
|
||||
margin-right: 2px;
|
||||
}
|
||||
.heart-active { color: #f2c94c; }
|
||||
.heart-lost { color: #111; }
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
# Dino Land USB / Thumbdrive Install & Run Guide
|
||||
|
||||
For non-technical users running Dino Land from a USB drive on unknown computers (Windows, macOS, ChromeOS).
|
||||
|
||||
---
|
||||
|
||||
## 1) Quick decision tree
|
||||
|
||||
```text
|
||||
Start
|
||||
├─ Can you run a local web server (PHP or portable server) from USB?
|
||||
│ ├─ Yes → Use FULL LOCAL MODE (leaderboard + API should work)
|
||||
│ └─ No
|
||||
│ ├─ Can you at least open files in a browser from USB?
|
||||
│ │ ├─ Yes → Use OFFLINE QUICK PLAY (gameplay only, no score save)
|
||||
│ │ └─ No → Host machine is locked down; ask for a machine with browser file access
|
||||
└─ If security warning appears (SmartScreen/Gatekeeper), use browser-only mode or approved machine
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2) Recommendation matrix
|
||||
|
||||
| Mode | Needs install/admin? | Works from USB only? | Scoreboard/API | Best when |
|
||||
|---|---|---|---|---|
|
||||
| **Works with no install** (open file in browser) | No | Yes | **No** (PHP API unavailable) | Locked-down machines, schools, kiosks |
|
||||
| **Works with portable binaries** (USB-contained server) | Usually No (if executable allowed) | Yes | **Yes** (if local server starts) | Unknown machine, no admin, but executables allowed |
|
||||
| **Requires host runtime/install** (host PHP/Python/etc.) | Often Yes (or preinstalled tools) | Not strictly | **Yes** | Managed machine with approved dev/runtime tools |
|
||||
|
||||
---
|
||||
|
||||
## 3) Packaging options to put on USB
|
||||
|
||||
## Option A: **Offline quick play** (no scores)
|
||||
Use existing project files as-is.
|
||||
|
||||
Required on USB:
|
||||
- `index.php` (or optional static fallback page if you create one)
|
||||
- `js/`, `assets/`, `styles.css`
|
||||
|
||||
What works:
|
||||
- Core gameplay usually works in browser file mode (`file://...`)
|
||||
|
||||
What does **not** work reliably:
|
||||
- Leaderboard save/load via `api/scores.php`
|
||||
|
||||
## Option B: **Full local mode** (with local server)
|
||||
Put full project folder on USB, including:
|
||||
- `api/`
|
||||
- `data/`
|
||||
- all game assets/files
|
||||
|
||||
Then run a local server (portable or host runtime) and open `http://127.0.0.1:PORT`.
|
||||
|
||||
---
|
||||
|
||||
## 4) Per-OS run steps
|
||||
|
||||
## Windows
|
||||
|
||||
### A) No install (quick play)
|
||||
1. Insert USB.
|
||||
2. Open USB in File Explorer.
|
||||
3. Right-click `index.php` -> **Open with** -> Chrome/Edge.
|
||||
- If this opens raw PHP text or broken page, use Full Local Mode.
|
||||
|
||||
### B) Portable binaries (preferred if allowed)
|
||||
If your USB has `php\php.exe` bundled:
|
||||
|
||||
```bat
|
||||
cd /d "%~dp0"
|
||||
php\php.exe -S 127.0.0.1:8000
|
||||
```
|
||||
|
||||
Then open:
|
||||
- `http://127.0.0.1:8000`
|
||||
|
||||
If SmartScreen appears:
|
||||
- Click **More info** -> **Run anyway** only if USB is trusted.
|
||||
- On locked corporate machines, this option may be blocked.
|
||||
|
||||
### C) Host runtime already installed
|
||||
In Command Prompt (inside Dino Land folder):
|
||||
|
||||
```bat
|
||||
php -S 127.0.0.1:8000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## macOS
|
||||
|
||||
### A) No install (quick play)
|
||||
1. Insert USB.
|
||||
2. In Finder, open Dino Land folder.
|
||||
3. Drag `index.php` into Chrome/Safari (or File -> Open File).
|
||||
|
||||
### B) Portable binary from USB
|
||||
If USB includes a PHP binary (example path `./php/bin/php`):
|
||||
|
||||
```bash
|
||||
cd "/Volumes/<USB_NAME>/Dino Land"
|
||||
./php/bin/php -S 127.0.0.1:8000
|
||||
```
|
||||
|
||||
Open `http://127.0.0.1:8000`
|
||||
|
||||
If Gatekeeper blocks the binary:
|
||||
- Right-click binary -> **Open** (one-time allow), or
|
||||
- If policy blocks unknown binaries, use quick play mode.
|
||||
|
||||
If quarantine flag blocks execution, advanced users can remove it:
|
||||
|
||||
```bash
|
||||
xattr -dr com.apple.quarantine "/Volumes/<USB_NAME>/Dino Land/php"
|
||||
```
|
||||
|
||||
(Only do this for trusted files.)
|
||||
|
||||
### C) Host runtime (if preinstalled)
|
||||
|
||||
```bash
|
||||
cd "/Volumes/<USB_NAME>/Dino Land"
|
||||
php -S 127.0.0.1:8000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ChromeOS
|
||||
|
||||
### A) No install (quick play)
|
||||
- Open Files app -> USB -> game files.
|
||||
- Open with browser.
|
||||
- If browser blocks local JS/file access behavior, use another machine or Linux mode.
|
||||
|
||||
### B) Portable/server mode on ChromeOS
|
||||
- Usually restricted unless Linux (Crostini) is enabled.
|
||||
- If Linux terminal is available:
|
||||
|
||||
```bash
|
||||
cd "/mnt/chromeos/removable/<USB_NAME>/Dino Land"
|
||||
php -S 127.0.0.1:8000
|
||||
```
|
||||
|
||||
Then open `http://127.0.0.1:8000` in Chrome.
|
||||
|
||||
### C) Host runtime/install required
|
||||
- On managed school/work Chromebooks, installing/enabling Linux may be disabled by policy.
|
||||
- In that case, only quick play (if allowed) is feasible.
|
||||
|
||||
---
|
||||
|
||||
## 5) Limitations, risks, and behavior to expect
|
||||
|
||||
- **`file://` origin restrictions:** browsers may block some fetch/XHR/module behaviors from local files.
|
||||
- **Scoreboard/API dependency:** leaderboard save/load needs `api/scores.php` via server-side PHP. No running PHP server = no persistent scores.
|
||||
- **Windows SmartScreen:** may warn/block unsigned portable executables from USB.
|
||||
- **macOS Gatekeeper/quarantine:** may block downloaded/unsigned binaries on USB until manually allowed.
|
||||
- **ChromeOS constraints:** managed devices often block runtime installs, local servers, or executable permissions.
|
||||
- **USB write permissions:** if USB is read-only or restricted, `data/scores.json` cannot update even with server running.
|
||||
|
||||
---
|
||||
|
||||
## 6) Practical fallback order (recommended)
|
||||
|
||||
1. Try **Full local mode** (portable PHP on USB).
|
||||
2. If blocked, try **host-installed PHP** (if already present).
|
||||
3. If still blocked, use **Offline quick play** (no scores).
|
||||
|
||||
---
|
||||
|
||||
## 7) Minimal launcher scripts (optional, put on USB)
|
||||
|
||||
### `start-windows.bat`
|
||||
```bat
|
||||
@echo off
|
||||
cd /d "%~dp0"
|
||||
if exist php\php.exe (
|
||||
php\php.exe -S 127.0.0.1:8000
|
||||
) else (
|
||||
php -S 127.0.0.1:8000
|
||||
)
|
||||
```
|
||||
|
||||
### `start-macos.sh`
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
cd "$(dirname "$0")"
|
||||
if [ -x "./php/bin/php" ]; then
|
||||
./php/bin/php -S 127.0.0.1:8000
|
||||
else
|
||||
php -S 127.0.0.1:8000
|
||||
fi
|
||||
```
|
||||
|
||||
Make executable (once):
|
||||
|
||||
```bash
|
||||
chmod +x start-macos.sh
|
||||
```
|
||||
Reference in New Issue
Block a user