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;
|
||||
}
|
||||
|
||||
#debug-query-log {
|
||||
.debug-query-log {
|
||||
padding: 2rem;
|
||||
font-size: 14px;
|
||||
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
|
||||
*/
|
||||
router_get($r, '/world', function () {
|
||||
auth_only_and_must_have_character();
|
||||
$GLOBALS['active_nav_tab'] = 'world';
|
||||
echo page('world/base');
|
||||
});
|
||||
router_get($r, '/world', 'world_controller_get');
|
||||
router_post($r, '/move', 'world_controller_move_post');
|
||||
|
||||
/*
|
||||
Router
|
||||
|
|
|
@ -6,22 +6,23 @@ session_start();
|
|||
|
||||
// Source libraries
|
||||
require_once SRC . '/helpers.php';
|
||||
require_once SRC . '/env.php';
|
||||
require_once SRC . '/database.php';
|
||||
require_once SRC . '/auth.php';
|
||||
require_once SRC . '/router.php';
|
||||
require_once SRC . '/components.php';
|
||||
require_once SRC . '/render.php';
|
||||
require_once SRC . '/util/env.php';
|
||||
require_once SRC . '/util/database.php';
|
||||
require_once SRC . '/util/auth.php';
|
||||
require_once SRC . '/util/router.php';
|
||||
require_once SRC . '/util/components.php';
|
||||
require_once SRC . '/util/render.php';
|
||||
|
||||
// Database models
|
||||
require_once SRC . '/models/user.php';
|
||||
require_once SRC . '/models/session.php';
|
||||
require_once SRC . '/models/token.php';
|
||||
require_once SRC . '/models/char.php';
|
||||
require_once SRC . '/model/user.php';
|
||||
require_once SRC . '/model/session.php';
|
||||
require_once SRC . '/model/token.php';
|
||||
require_once SRC . '/model/char.php';
|
||||
|
||||
// Controllers
|
||||
require_once SRC . '/controllers/char.php';
|
||||
require_once SRC . '/controllers/auth.php';
|
||||
require_once SRC . '/controller/char.php';
|
||||
require_once SRC . '/controller/auth.php';
|
||||
require_once SRC . '/controller/world.php';
|
||||
|
||||
// Track the start time of the request
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
@ -231,3 +250,11 @@ function ce($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()
|
||||
{
|
||||
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()
|
||||
{
|
||||
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()
|
||||
{
|
||||
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()
|
||||
{
|
||||
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)
|
||||
{
|
||||
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>
|
||||
<p class="mb-2"><?= $GLOBALS['queries'] ?> queries were executed.</p>
|
||||
<?php
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<div id="debug-query-log">
|
||||
<div class="debug-query-log">
|
||||
<h3>Stopwatches</h3>
|
||||
<p class="mb-2">Page execution took <?= number_format((microtime(true) - START_TIME), 10) ?> 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">
|
||||
<title>Dragon Knight</title>
|
||||
<link rel="stylesheet" href="/assets/css/dragon.css">
|
||||
<script src="/assets/scripts/htmx.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
|
|
|
@ -1,3 +1,113 @@
|
|||
<div id="target_world_map">
|
||||
<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