src restructure, world map
This commit is contained in:
parent
a564a96625
commit
b9a54859ce
BIN
database/auth.db
BIN
database/auth.db
Binary file not shown.
BIN
database/live.db-shm
Normal file
BIN
database/live.db-shm
Normal file
Binary file not shown.
BIN
database/live.db-wal
Normal file
BIN
database/live.db-wal
Normal file
Binary file not shown.
|
@ -414,7 +414,7 @@ span.badge {
|
||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#debug-query-log {
|
.debug-query-log {
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #666;
|
color: #666;
|
||||||
|
|
1
public/assets/scripts/htmx.js
Normal file
1
public/assets/scripts/htmx.js
Normal file
File diff suppressed because one or more lines are too long
|
@ -38,11 +38,8 @@ router_post($r, '/character/delete', 'char_controller_delete_post');
|
||||||
/*
|
/*
|
||||||
World
|
World
|
||||||
*/
|
*/
|
||||||
router_get($r, '/world', function () {
|
router_get($r, '/world', 'world_controller_get');
|
||||||
auth_only_and_must_have_character();
|
router_post($r, '/move', 'world_controller_move_post');
|
||||||
$GLOBALS['active_nav_tab'] = 'world';
|
|
||||||
echo page('world/base');
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Router
|
Router
|
||||||
|
|
|
@ -6,22 +6,23 @@ session_start();
|
||||||
|
|
||||||
// Source libraries
|
// Source libraries
|
||||||
require_once SRC . '/helpers.php';
|
require_once SRC . '/helpers.php';
|
||||||
require_once SRC . '/env.php';
|
require_once SRC . '/util/env.php';
|
||||||
require_once SRC . '/database.php';
|
require_once SRC . '/util/database.php';
|
||||||
require_once SRC . '/auth.php';
|
require_once SRC . '/util/auth.php';
|
||||||
require_once SRC . '/router.php';
|
require_once SRC . '/util/router.php';
|
||||||
require_once SRC . '/components.php';
|
require_once SRC . '/util/components.php';
|
||||||
require_once SRC . '/render.php';
|
require_once SRC . '/util/render.php';
|
||||||
|
|
||||||
// Database models
|
// Database models
|
||||||
require_once SRC . '/models/user.php';
|
require_once SRC . '/model/user.php';
|
||||||
require_once SRC . '/models/session.php';
|
require_once SRC . '/model/session.php';
|
||||||
require_once SRC . '/models/token.php';
|
require_once SRC . '/model/token.php';
|
||||||
require_once SRC . '/models/char.php';
|
require_once SRC . '/model/char.php';
|
||||||
|
|
||||||
// Controllers
|
// Controllers
|
||||||
require_once SRC . '/controllers/char.php';
|
require_once SRC . '/controller/char.php';
|
||||||
require_once SRC . '/controllers/auth.php';
|
require_once SRC . '/controller/auth.php';
|
||||||
|
require_once SRC . '/controller/world.php';
|
||||||
|
|
||||||
// Track the start time of the request
|
// Track the start time of the request
|
||||||
define('START_TIME', microtime(true));
|
define('START_TIME', microtime(true));
|
||||||
|
|
56
src/controller/world.php
Normal file
56
src/controller/world.php
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print the world page.
|
||||||
|
*/
|
||||||
|
function world_controller_get()
|
||||||
|
{
|
||||||
|
auth_only_and_must_have_character();
|
||||||
|
$GLOBALS['active_nav_tab'] = 'world';
|
||||||
|
echo page('world/base');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle a request to move a character.
|
||||||
|
*/
|
||||||
|
function world_controller_move_post()
|
||||||
|
{
|
||||||
|
auth_only_and_must_have_character(); csrf_ensure();
|
||||||
|
|
||||||
|
// direction must exist
|
||||||
|
$direction = $_POST['direction'] ?? false;
|
||||||
|
|
||||||
|
// direction must be valid; 0-3 are sent from the client
|
||||||
|
if (!is_numeric($direction) || $direction < 0 || $direction > 3 || $direction === false) router_error(999);
|
||||||
|
|
||||||
|
// Update the character's position
|
||||||
|
// 0 = up, 1 = down, 2 = left, 3 = right
|
||||||
|
$x = location('x');
|
||||||
|
$y = location('y');
|
||||||
|
|
||||||
|
switch ($direction) {
|
||||||
|
case 0: $y--; break;
|
||||||
|
case 1: $y++; break;
|
||||||
|
case 2: $x--; break;
|
||||||
|
case 3: $x++; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the character's position
|
||||||
|
$r = db_query(db_live(), 'UPDATE char_locations SET x = :x, y = :y WHERE char_id = :c', [
|
||||||
|
':x' => $x,
|
||||||
|
':y' => $y,
|
||||||
|
':c' => char('id')
|
||||||
|
]);
|
||||||
|
|
||||||
|
// If the query failed, throw an error
|
||||||
|
if ($r === false) throw new Exception('Failed to move character. (wcmp)');
|
||||||
|
|
||||||
|
// If this is an HTMX request, return the new world page
|
||||||
|
if (is_htmx()) {
|
||||||
|
echo render('pages/world/base');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirect back to the world page
|
||||||
|
redirect('/world');
|
||||||
|
}
|
|
@ -185,6 +185,25 @@ function wallet($field = '')
|
||||||
return $GLOBALS['wallet'][$field] ?? false;
|
return $GLOBALS['wallet'][$field] ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access the character location. On first execution it will populate $GLOBALS['location'] with the location data. This
|
||||||
|
* way the data is up to date with every request without having to query the database every use within, for example, a
|
||||||
|
* template. Will return false if the field does not exist, or the entire location array if no field is specified.
|
||||||
|
*/
|
||||||
|
function location($field = '')
|
||||||
|
{
|
||||||
|
if (empty($GLOBALS['location'])) {
|
||||||
|
$GLOBALS['location'] = db_query(
|
||||||
|
db_live(),
|
||||||
|
"SELECT * FROM char_locations WHERE char_id = :c",
|
||||||
|
[':c' => char('id')]
|
||||||
|
)->fetchArray(SQLITE3_ASSOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($field === '') return $GLOBALS['location'];
|
||||||
|
return $GLOBALS['location'][$field] ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format an array of strings to a ul element.
|
* Format an array of strings to a ul element.
|
||||||
*/
|
*/
|
||||||
|
@ -231,3 +250,11 @@ function ce($condition, $value, $or = '')
|
||||||
{
|
{
|
||||||
echo $condition ? $value : $or;
|
echo $condition ? $value : $or;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get whether the request is an HTMX request.
|
||||||
|
*/
|
||||||
|
function is_htmx(): bool
|
||||||
|
{
|
||||||
|
return isset($_SERVER['HTTP_HX_REQUEST']);
|
||||||
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ function db_open($path)
|
||||||
*/
|
*/
|
||||||
function db_auth()
|
function db_auth()
|
||||||
{
|
{
|
||||||
return $GLOBALS['db_auth'] ??= db_open(__DIR__ . '/../database/auth.db');
|
return $GLOBALS['db_auth'] ??= db_open(SRC . '/../database/auth.db');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -30,7 +30,7 @@ function db_auth()
|
||||||
*/
|
*/
|
||||||
function db_live()
|
function db_live()
|
||||||
{
|
{
|
||||||
return $GLOBALS['db_live'] ??= db_open(__DIR__ . '/../database/live.db');
|
return $GLOBALS['db_live'] ??= db_open(SRC . '/../database/live.db');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ function db_live()
|
||||||
*/
|
*/
|
||||||
function db_fights()
|
function db_fights()
|
||||||
{
|
{
|
||||||
return $GLOBALS['db_fights'] ??= db_open(__DIR__ . '/../database/fights.db');
|
return $GLOBALS['db_fights'] ??= db_open(SRC . '/../database/fights.db');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ function db_fights()
|
||||||
*/
|
*/
|
||||||
function db_blueprints()
|
function db_blueprints()
|
||||||
{
|
{
|
||||||
return $GLOBALS['db_blueprints'] ??= db_open(__DIR__ . '/../database/blueprints.db');
|
return $GLOBALS['db_blueprints'] ??= db_open(SRC . '/../database/blueprints.db');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -5,7 +5,7 @@
|
||||||
*/
|
*/
|
||||||
function template($name)
|
function template($name)
|
||||||
{
|
{
|
||||||
return __DIR__ . "/../templates/$name.php";
|
return SRC . "/../templates/$name.php";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,4 +1,4 @@
|
||||||
<div id="debug-query-log">
|
<div class="debug-query-log">
|
||||||
<h3>Query Log</h3>
|
<h3>Query Log</h3>
|
||||||
<p class="mb-2"><?= $GLOBALS['queries'] ?> queries were executed.</p>
|
<p class="mb-2"><?= $GLOBALS['queries'] ?> queries were executed.</p>
|
||||||
<?php
|
<?php
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<div id="debug-query-log">
|
<div class="debug-query-log">
|
||||||
<h3>Stopwatches</h3>
|
<h3>Stopwatches</h3>
|
||||||
<p class="mb-2">Page execution took <?= number_format((microtime(true) - START_TIME), 10) ?> seconds.</p>
|
<p class="mb-2">Page execution took <?= number_format((microtime(true) - START_TIME), 10) ?> seconds.</p>
|
||||||
<p>Bootstrap: <?= stopwatch_get('bootstrap') ?> seconds</p>
|
<p>Bootstrap: <?= stopwatch_get('bootstrap') ?> seconds</p>
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
<canvas></canvas>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
const canvas = document.querySelector('canvas');
|
|
||||||
const ctx = canvas.getContext('2d');
|
|
||||||
|
|
||||||
canvas.width = 800;
|
|
||||||
canvas.height = 600;
|
|
||||||
|
|
||||||
const tile_height = 32;
|
|
||||||
const tile_width = 32;
|
|
||||||
|
|
||||||
const map = [
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 0,
|
|
||||||
0, 1, 0, 0, 0, 0, 0, 0, 1, 0,
|
|
||||||
0, 1, 0, 0, 0, 0, 0, 0, 1, 0,
|
|
||||||
0, 1, 0, 0, 0, 0, 0, 0, 1, 0,
|
|
||||||
0, 1, 0, 0, 0, 0, 0, 0, 1, 0,
|
|
||||||
0, 1, 0, 0, 0, 0, 0, 0, 1, 0,
|
|
||||||
0, 1, 0, 0, 0, 0, 0, 0, 1, 0,
|
|
||||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
];
|
|
||||||
|
|
||||||
// render the map
|
|
||||||
map.forEach((tile, index) => {
|
|
||||||
const x = (index % 10) * tile_width;
|
|
||||||
const y = Math.floor(index / 10) * tile_height;
|
|
||||||
|
|
||||||
ctx.fillStyle = tile === 0 ? 'black' : 'white';
|
|
||||||
ctx.fillRect(x, y, tile_width, tile_height);
|
|
||||||
});
|
|
||||||
</script>
|
|
|
@ -5,6 +5,7 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Dragon Knight</title>
|
<title>Dragon Knight</title>
|
||||||
<link rel="stylesheet" href="/assets/css/dragon.css">
|
<link rel="stylesheet" href="/assets/css/dragon.css">
|
||||||
|
<script src="/assets/scripts/htmx.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
|
|
|
@ -1,3 +1,113 @@
|
||||||
|
<div id="target_world_map">
|
||||||
<h1>World</h1>
|
<h1>World</h1>
|
||||||
|
<p>Use WASD keys to move the character</p>
|
||||||
|
<p>Current location: <?= location('x') ?>, <?= location('y') ?></p>
|
||||||
|
|
||||||
<?= render('components/world_map') ?>
|
<canvas id="canvas"></canvas>
|
||||||
|
|
||||||
|
<form hx-post="/move" hx-target="target_world_map" hx-swap="outerHTML" id="form_move">
|
||||||
|
<?= csrf_field() ?>
|
||||||
|
<input type="hidden" name="direction" value="">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const canvas = document.getElementById('canvas');
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
let char_x = <?= location('x') ?>;
|
||||||
|
let char_y = <?= location('y') ?>;
|
||||||
|
|
||||||
|
// Configuration
|
||||||
|
const TILE_SIZE = 40; // Fixed size for each tile
|
||||||
|
|
||||||
|
// Sample tile map (0 = empty, 1 = filled)
|
||||||
|
const tileMap = [
|
||||||
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
|
];
|
||||||
|
|
||||||
|
// Turn the value at the player's coordinates to be 1 in the map array
|
||||||
|
tileMap[char_y][char_x] = 1;
|
||||||
|
|
||||||
|
const tileColors = {
|
||||||
|
0: '#fff', // Empty tile
|
||||||
|
1: '#333' // Wall tile
|
||||||
|
};
|
||||||
|
|
||||||
|
function resizeCanvas() {
|
||||||
|
// Calculate the actual dimensions needed for the map
|
||||||
|
const mapWidth = tileMap[0].length * TILE_SIZE;
|
||||||
|
const mapHeight = tileMap.length * TILE_SIZE;
|
||||||
|
|
||||||
|
// Get the container's width
|
||||||
|
const containerWidth = canvas.parentElement.clientWidth;
|
||||||
|
|
||||||
|
// Calculate the scale factor to fit the map width to the container
|
||||||
|
const scale = Math.min(1, containerWidth / mapWidth);
|
||||||
|
|
||||||
|
// Set canvas size using the scale factor
|
||||||
|
canvas.width = mapWidth * scale;
|
||||||
|
canvas.height = mapHeight * scale;
|
||||||
|
|
||||||
|
// Scale the context to maintain tile dimensions
|
||||||
|
ctx.scale(scale, scale);
|
||||||
|
|
||||||
|
// Render the map after resize
|
||||||
|
renderMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderMap() {
|
||||||
|
// Clear the canvas
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
// Draw each tile
|
||||||
|
for (let row = 0; row < tileMap.length; row++) {
|
||||||
|
for (let col = 0; col < tileMap[row].length; col++) {
|
||||||
|
const tileType = tileMap[row][col];
|
||||||
|
const x = col * TILE_SIZE;
|
||||||
|
const y = row * TILE_SIZE;
|
||||||
|
|
||||||
|
ctx.fillStyle = tileColors[tileType];
|
||||||
|
ctx.fillRect(x, y, TILE_SIZE, TILE_SIZE);
|
||||||
|
|
||||||
|
// Draw tile borders
|
||||||
|
ctx.strokeStyle = '#999';
|
||||||
|
ctx.strokeRect(x, y, TILE_SIZE, TILE_SIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initial setup
|
||||||
|
resizeCanvas();
|
||||||
|
|
||||||
|
// Handle window resizing
|
||||||
|
window.addEventListener('resize', resizeCanvas);
|
||||||
|
|
||||||
|
// On WASD key press, send a POST request to move the character using the form
|
||||||
|
window.addEventListener('keydown', (e) => {
|
||||||
|
if (!['w', 'a', 's', 'd'].includes(e.key)) return;
|
||||||
|
|
||||||
|
el = document.querySelector('input[name="direction"]');
|
||||||
|
|
||||||
|
// update the direction input to be 0-3 based on the key pressed
|
||||||
|
// 0 = up, 1 = down, 2 = left, 3 = right
|
||||||
|
el.value = {
|
||||||
|
'w': 0,
|
||||||
|
's': 1,
|
||||||
|
'a': 2,
|
||||||
|
'd': 3
|
||||||
|
}[e.key];
|
||||||
|
|
||||||
|
htmx.trigger(document.getElementById('form_move'), 'submit');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user