ava borders, controllers, item boxes
|
@ -276,6 +276,7 @@ span.badge {
|
|||
}
|
||||
|
||||
.tooltip {
|
||||
position: absolute;
|
||||
background-color: black;
|
||||
color: white;
|
||||
border: 1px solid #666;
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
section.profile {
|
||||
header {
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
margin-bottom: 3rem;
|
||||
|
||||
h3 {
|
||||
color: rgba(0, 0, 0, 0.3);
|
||||
text-transform: uppercase;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
h4 {
|
||||
h5 {
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +21,7 @@ section.profile {
|
|||
width: 50%;
|
||||
|
||||
& > div:not(:last-child) {
|
||||
margin-bottom: 1rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,8 +30,17 @@ section.profile {
|
|||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding-bottom: 1rem;
|
||||
|
||||
img {
|
||||
max-width: 250px;
|
||||
height: 185px;
|
||||
width: 185px;
|
||||
}
|
||||
|
||||
.border {
|
||||
width: 250px;
|
||||
height: 250px;
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,4 +75,58 @@ section.profile {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#equipped-gear {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
|
||||
div.item {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
&.i-1x1 {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
background-image: url('/assets/img/ui/1x1.png');
|
||||
}
|
||||
|
||||
&.i-2x2 {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
background-image: url('/assets/img/ui/2x2.png');
|
||||
}
|
||||
|
||||
&.i-2x3 {
|
||||
width: 60px;
|
||||
height: 90px;
|
||||
background-image: url('/assets/img/ui/2x3.png');
|
||||
}
|
||||
}
|
||||
|
||||
& > div {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
|
||||
& > div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 60px;
|
||||
|
||||
&.top, &.bot {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
&.mid {
|
||||
width: 60px;
|
||||
height: 90px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
BIN
public/assets/img/ui/1x1.png
Normal file
After Width: | Height: | Size: 155 B |
BIN
public/assets/img/ui/2x2.png
Normal file
After Width: | Height: | Size: 178 B |
BIN
public/assets/img/ui/2x3.png
Normal file
After Width: | Height: | Size: 186 B |
BIN
public/assets/img/ui/borders/alchemist.webp
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
public/assets/img/ui/borders/divine.webp
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
public/assets/img/ui/borders/dragon_slaying.webp
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
public/assets/img/ui/borders/dwarven_smiths.webp
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
public/assets/img/ui/borders/earth.webp
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
public/assets/img/ui/borders/egyptian.webp
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
public/assets/img/ui/borders/elven_archer.webp
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
public/assets/img/ui/borders/fire.webp
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
public/assets/img/ui/borders/gladiator.webp
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
public/assets/img/ui/borders/gold_mine.webp
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
public/assets/img/ui/borders/golden.webp
Normal file
After Width: | Height: | Size: 8.4 KiB |
BIN
public/assets/img/ui/borders/ice.webp
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
public/assets/img/ui/borders/knight.webp
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
public/assets/img/ui/borders/mage.webp
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
public/assets/img/ui/borders/metal.webp
Normal file
After Width: | Height: | Size: 7.0 KiB |
BIN
public/assets/img/ui/borders/necromancer.webp
Normal file
After Width: | Height: | Size: 53 KiB |
BIN
public/assets/img/ui/borders/orcs.webp
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
public/assets/img/ui/borders/rock.webp
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
public/assets/img/ui/borders/royal.webp
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
public/assets/img/ui/borders/rune.webp
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
public/assets/img/ui/borders/tavern.webp
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
public/assets/img/ui/borders/vikings.webp
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
public/assets/img/ui/borders/water.webp
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
public/assets/img/ui/borders/wind.webp
Normal file
After Width: | Height: | Size: 55 KiB |
BIN
public/assets/img/ui/borders/wooden.webp
Normal file
After Width: | Height: | Size: 10 KiB |
|
@ -6,12 +6,12 @@
|
|||
define('SRC', __DIR__ . '/../src');
|
||||
require_once SRC . '/bootstrap.php';
|
||||
|
||||
$r = [];
|
||||
$r = new Router;
|
||||
|
||||
/*
|
||||
Home
|
||||
*/
|
||||
router_get($r, '/', function () {
|
||||
$r->get('/', function () {
|
||||
if (user()) must_have_character();
|
||||
$GLOBALS['active_nav_tab'] = 'home';
|
||||
echo render('layouts/basic', ['view' => 'pages/home']);
|
||||
|
@ -20,54 +20,54 @@ router_get($r, '/', function () {
|
|||
/*
|
||||
Auth
|
||||
*/
|
||||
router_get($r, '/auth/register', 'auth_controller_register_get');
|
||||
router_post($r, '/auth/register', 'auth_controller_register_post');
|
||||
router_get($r, '/auth/login', 'auth_controller_login_get');
|
||||
router_post($r, '/auth/login', 'auth_controller_login_post');
|
||||
router_post($r, '/auth/logout', 'auth_controller_logout_post');
|
||||
$r->get('/auth/register', 'auth_controller_register_get');
|
||||
$r->post('/auth/register', 'auth_controller_register_post');
|
||||
$r->get('/auth/login', 'auth_controller_login_get');
|
||||
$r->post('/auth/login', 'auth_controller_login_post');
|
||||
$r->post('/auth/logout', 'auth_controller_logout_post');
|
||||
|
||||
/*
|
||||
Characters
|
||||
*/
|
||||
router_get($r, '/characters', 'char_controller_list_get');
|
||||
router_post($r, '/characters', 'char_controller_list_post');
|
||||
router_get($r, '/character/create-first', 'char_controller_create_first_get');
|
||||
router_post($r, '/character/create', 'char_controller_create_post');
|
||||
router_post($r, '/character/delete', 'char_controller_delete_post');
|
||||
$r->get('/characters', 'char_controller_list_get');
|
||||
$r->post('/characters', 'char_controller_list_post');
|
||||
$r->get('/character/create-first', 'char_controller_create_first_get');
|
||||
$r->post('/character/create', 'char_controller_create_post');
|
||||
$r->post('/character/delete', 'char_controller_delete_post');
|
||||
|
||||
/*
|
||||
World
|
||||
*/
|
||||
router_get($r, '/world', 'world_controller_get');
|
||||
router_post($r, '/move', 'world_controller_move_post');
|
||||
$r->get('/world', 'world_controller_get');
|
||||
$r->post('/move', 'world_controller_move_post');
|
||||
|
||||
/*
|
||||
Profile
|
||||
*/
|
||||
router_get($r, '/profile', 'profile_controller_get');
|
||||
router_get($r, '/profile/:id', 'profile_controller_show_get');
|
||||
$r->get('/profile', 'profile_controller_get');
|
||||
$r->get('/profile/:id', 'profile_controller_show_get');
|
||||
|
||||
/*
|
||||
Settings
|
||||
*/
|
||||
router_get($r, '/settings', 'settings_controller_get');
|
||||
$r->get('/settings', 'settings_controller_get');
|
||||
|
||||
/*
|
||||
Auctions
|
||||
*/
|
||||
router_get($r, '/auctions', 'auctions_controller_get');
|
||||
$r->get('/auctions', 'auctions_controller_get');
|
||||
|
||||
/*
|
||||
Testing
|
||||
*/
|
||||
if (env('debug')) {
|
||||
router_get($r, '/give_silver/:x', function (int $amt) {
|
||||
$r->get('/give_silver/:x', function (int $amt) {
|
||||
auth_only_and_must_have_character();
|
||||
wallet()->give(Currency::Silver, $amt);
|
||||
redirect('/');
|
||||
});
|
||||
|
||||
router_get($r, '/take_silver/:x', function (int $amt) {
|
||||
$r->get('/take_silver/:x', function (int $amt) {
|
||||
auth_only_and_must_have_character();
|
||||
wallet()->take(Currency::Silver, $amt);
|
||||
redirect('/');
|
||||
|
@ -79,11 +79,11 @@ if (env('debug')) {
|
|||
*/
|
||||
// [code, handler, params]
|
||||
stopwatch_start('router');
|
||||
$l = router_lookup($r, $_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']);
|
||||
$l = $r->lookup($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']);
|
||||
stopwatch_stop('router');
|
||||
|
||||
stopwatch_start('handler');
|
||||
if ($l['code'] !== 200) router_error($l['code']);
|
||||
if ($l['code'] !== 200) error_response($l['code']);
|
||||
$l['handler'](...$l['params'] ?? []);
|
||||
stopwatch_stop('handler');
|
||||
|
||||
|
|
|
@ -25,12 +25,12 @@ require_once SRC . '/models/session.php';
|
|||
require_once SRC . '/models/token.php';
|
||||
|
||||
// Controllers
|
||||
require_once SRC . '/controller/char.php';
|
||||
require_once SRC . '/controller/auth.php';
|
||||
require_once SRC . '/controller/world.php';
|
||||
require_once SRC . '/controller/settings.php';
|
||||
require_once SRC . '/controller/auctions.php';
|
||||
require_once SRC . '/controller/profile.php';
|
||||
require_once SRC . '/controllers/char.php';
|
||||
require_once SRC . '/controllers/auth.php';
|
||||
require_once SRC . '/controllers/world.php';
|
||||
require_once SRC . '/controllers/settings.php';
|
||||
require_once SRC . '/controllers/auctions.php';
|
||||
require_once SRC . '/controllers/profile.php';
|
||||
|
||||
spl_autoload_register(function (string $class) {
|
||||
if (array_key_exists($class, CLASS_MAP)) require_once SRC . CLASS_MAP[$class];
|
||||
|
|
|
@ -71,7 +71,7 @@ function auth_controller_register_post()
|
|||
exit;
|
||||
}
|
||||
|
||||
if (User::create($u, $e, $p) === false) router_error(400);
|
||||
if (User::create($u, $e, $p) === false) error_response(400);
|
||||
|
||||
$_SESSION['user'] = serialize(User::find($u));
|
||||
Wallet::create(user()->id);
|
||||
|
@ -128,7 +128,7 @@ function auth_controller_login_post()
|
|||
"INSERT INTO sessions (token, user_id, expires) VALUES (:t, :u, :e)",
|
||||
[':t' => $token, ':u' => user()->id, ':e' => $expires]
|
||||
);
|
||||
if (!$result) router_error(400);
|
||||
if (!$result) error_response(400);
|
||||
set_cookie('remember_me', $token, $expires);
|
||||
}
|
||||
|
||||
|
@ -136,7 +136,7 @@ function auth_controller_login_post()
|
|||
redirect('/character/create-first');
|
||||
} elseif (!change_user_character(user()->char_id)) {
|
||||
echo "failed to change user character (aclp)";
|
||||
router_error(999);
|
||||
error_response(999);
|
||||
}
|
||||
|
||||
redirect('/');
|
|
@ -24,7 +24,7 @@ function char_controller_list_post()
|
|||
$action = $_POST['action'] ?? '';
|
||||
|
||||
// If the character ID is not a number, or the action is not a string, return a 400.
|
||||
if (!is_numeric($char_id) || !is_string($action)) router_error(400);
|
||||
if (!is_numeric($char_id) || !is_string($action)) error_response(400);
|
||||
|
||||
// If the character ID is 0, return to the list.
|
||||
if ($char_id === 0) {
|
||||
|
@ -33,7 +33,7 @@ function char_controller_list_post()
|
|||
}
|
||||
|
||||
// If the action is not one of the allowed actions, return a 400.
|
||||
if (!in_array($action, ['select', 'delete'])) router_error(400);
|
||||
if (!in_array($action, ['select', 'delete'])) error_response(400);
|
||||
|
||||
// If the action is to select a character, change the user's selected character.
|
||||
if ($action === 'select') {
|
||||
|
@ -43,7 +43,7 @@ function char_controller_list_post()
|
|||
redirect('/characters');
|
||||
}
|
||||
|
||||
if (!Character::belongs_to($char_id, user()->id)) router_error(999);
|
||||
if (!Character::belongs_to($char_id, user()->id)) error_response(999);
|
||||
|
||||
change_user_character($char_id);
|
||||
|
||||
|
@ -52,7 +52,7 @@ function char_controller_list_post()
|
|||
|
||||
// If the action is to delete a character, move to the confirmation page.
|
||||
if ($action === 'delete') {
|
||||
if (!Character::belongs_to($char_id, user()->id)) router_error(999);
|
||||
if (!Character::belongs_to($char_id, user()->id)) error_response(999);
|
||||
|
||||
echo page('chars/delete', ['char' => Character::find($char_id)]);
|
||||
exit;
|
||||
|
@ -71,10 +71,10 @@ function char_controller_delete_post()
|
|||
$char_id = (int) ($_POST['char_id'] ?? 0);
|
||||
|
||||
// If the character ID is not a number, return a 400.
|
||||
if (!is_numeric($char_id)) router_error(400);
|
||||
if (!is_numeric($char_id)) error_response(400);
|
||||
|
||||
// Ensure the character ID is valid and belongs to the user.
|
||||
if (!Character::belongs_to($char_id, user()->id)) router_error(999);
|
||||
if (!Character::belongs_to($char_id, user()->id)) error_response(999);
|
||||
|
||||
$char = Character::find($char_id);
|
||||
|
||||
|
@ -154,7 +154,7 @@ function char_controller_create_post()
|
|||
}
|
||||
}
|
||||
|
||||
if (($char = Character::create(user()->id, $name)) === false) router_error(400);
|
||||
if (($char = Character::create(user()->id, $name)) === false) error_response(400);
|
||||
|
||||
// Create the auxiliary tables
|
||||
$char->create_location();
|
|
@ -18,7 +18,7 @@ function profile_controller_show_get($id)
|
|||
{
|
||||
auth_only_and_must_have_character();
|
||||
|
||||
if (($char = Character::find($id)) == false) router_error(999);
|
||||
if (($char = Character::find($id)) == false) error_response(999);
|
||||
if (user()->char_id == $id) redirect('/profile');
|
||||
echo page('profile/show', ['c' => $char]);
|
||||
}
|
|
@ -45,7 +45,7 @@ function world_controller_move_post()
|
|||
$x += directions[$d][0];
|
||||
$y += directions[$d][1];
|
||||
} else {
|
||||
router_error(999);
|
||||
error_response(999);
|
||||
}
|
||||
|
||||
$r = db_query(db_live(), 'UPDATE char_locations SET x = :x, y = :y WHERE char_id = :c', [
|
|
@ -75,7 +75,7 @@ function csrf_field()
|
|||
*/
|
||||
function csrf_ensure()
|
||||
{
|
||||
if (!csrf_verify($_POST['csrf'] ?? '')) router_error(418);
|
||||
if (!csrf_verify($_POST['csrf'] ?? '')) error_response(418);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -121,8 +121,8 @@ function modify_user_session(string $field, mixed $value): bool
|
|||
function char(): Character|false
|
||||
{
|
||||
if (empty($_SESSION['user'])) return false;
|
||||
if (empty($GLOBALS['char'])) $GLOBALS['char'] = serialize(user()->current_char());
|
||||
return unserialize($GLOBALS['char']);
|
||||
if (empty($GLOBALS['char'])) $GLOBALS['char'] = user()->current_char();
|
||||
return $GLOBALS['char'];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -133,7 +133,7 @@ function change_user_character(int $char_id): bool
|
|||
{
|
||||
// If the character does not exist, return false
|
||||
if (($char = Character::find($char_id)) === false) return false;
|
||||
$GLOBALS['char'] = serialize($char);
|
||||
$GLOBALS['char'] = $char;
|
||||
|
||||
// If the character ID is different, update the session and database
|
||||
if (user()->char_id !== $char_id) {
|
||||
|
@ -218,17 +218,17 @@ function stopwatch_stop($key)
|
|||
/**
|
||||
* Get the stopwatch value and format it to within 10 digits.
|
||||
*/
|
||||
function stopwatch_get($key)
|
||||
function stopwatch_get(string $key): string
|
||||
{
|
||||
if (!env('debug', false)) return;
|
||||
if (empty($GLOBALS['stopwatch'][$key])) return 0;
|
||||
if (!env('debug', false)) return '';
|
||||
if (empty($GLOBALS['stopwatch'][$key])) return '';
|
||||
return number_format($GLOBALS['stopwatch'][$key], 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Conditional Echo; if the condition is true, echo the value. If the condition is false, echo the $or value.
|
||||
*/
|
||||
function ce($condition, $value, $or = '')
|
||||
function ce(bool $condition, mixed $value, mixed $or = ''): void
|
||||
{
|
||||
echo $condition ? $value : $or;
|
||||
}
|
||||
|
@ -236,7 +236,7 @@ function ce($condition, $value, $or = '')
|
|||
/**
|
||||
* Get whether the request is an HTMX request.
|
||||
*/
|
||||
function is_htmx()
|
||||
function is_htmx(): bool
|
||||
{
|
||||
return isset($_SERVER['HTTP_HX_REQUEST']);
|
||||
}
|
||||
|
@ -244,7 +244,7 @@ function is_htmx()
|
|||
/**
|
||||
* Get whether the request is an AJAX (fetch) request.
|
||||
*/
|
||||
function is_ajax()
|
||||
function is_ajax(): bool
|
||||
{
|
||||
return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest';
|
||||
}
|
||||
|
@ -252,33 +252,50 @@ function is_ajax()
|
|||
/**
|
||||
* Limit a request to AJAX only.
|
||||
*/
|
||||
function ajax_only()
|
||||
function ajax_only(): void
|
||||
{
|
||||
if (!is_ajax()) router_error(418);
|
||||
if (!is_ajax()) error_response(418);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a JSON response with the given data.
|
||||
*/
|
||||
function json_response($data)
|
||||
function json_response(mixed $data): void
|
||||
{
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
echo json_encode($data);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an error by setting the response code and echoing an error message
|
||||
*/
|
||||
function error_response(int $code): void
|
||||
{
|
||||
http_response_code($code);
|
||||
echo match ($code) {
|
||||
403 => 'Forbidden',
|
||||
404 => 'Not Found',
|
||||
405 => 'Method Not Allowed',
|
||||
418 => 'I\'m a teapot',
|
||||
999 => 'Cheating attempt detected',
|
||||
default => 'Unknown Error',
|
||||
};
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a title's [name, lore].
|
||||
*/
|
||||
function title($title_id)
|
||||
function title(int $title_id): array|false
|
||||
{
|
||||
return db_query(db_live(), 'SELECT * FROM titles WHERE id = :i', [':i' => $title_id])->fetchArray();
|
||||
return db_query(db_live(), 'SELECT * FROM titles WHERE id = ?', [$title_id])->fetchArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Abbreviate a number in text notation.
|
||||
*/
|
||||
function abb_num($num)
|
||||
function abb_num(int $num): string
|
||||
{
|
||||
$d = $num % 100 === 0 ? 0 : 1;
|
||||
return match(true) {
|
||||
|
|
|
@ -36,7 +36,15 @@ class Character
|
|||
public int $pre; // Precision
|
||||
public int $fer; // Ferocity
|
||||
public int $luck; // Luck
|
||||
|
||||
/**
|
||||
* Max number of slots this Character has in their inventory.
|
||||
*/
|
||||
public int $inv_slots;
|
||||
|
||||
/**
|
||||
* Number of points available to spend on attributes.
|
||||
*/
|
||||
public int $att_points;
|
||||
public string $bio;
|
||||
|
||||
|
@ -45,6 +53,11 @@ class Character
|
|||
*/
|
||||
private User $user;
|
||||
|
||||
/**
|
||||
* The current title of the Character.
|
||||
*/
|
||||
private array $title;
|
||||
|
||||
public function __construct(array $data)
|
||||
{
|
||||
foreach ($data as $k => $v) {
|
||||
|
@ -52,7 +65,10 @@ class Character
|
|||
}
|
||||
}
|
||||
|
||||
public static function find(int $id): Character|false
|
||||
/**
|
||||
* Find a Character by their name or ID.
|
||||
*/
|
||||
public static function find(int|string $id): Character|false
|
||||
{
|
||||
$q = db_query(
|
||||
db_live(),
|
||||
|
@ -63,6 +79,10 @@ class Character
|
|||
return ($c = $q->fetchArray(SQLITE3_ASSOC)) === false ? false : new Character($c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Character in the database using a User ID and a name. Pass optional overrides to adjust the
|
||||
* defaults for other fields.
|
||||
*/
|
||||
public static function create(int $user_id, string $name, array $overrides = []): Character|false
|
||||
{
|
||||
// Prep the data and merge in any overrides
|
||||
|
@ -254,12 +274,14 @@ class Character
|
|||
*/
|
||||
public function title(): array|false
|
||||
{
|
||||
if (isset($this->title)) return $this->title;
|
||||
|
||||
$t = title($this->title_id);
|
||||
|
||||
$q = db_query(
|
||||
db_live(),
|
||||
'SELECT awarded FROM owned_titles WHERE char_id = :c AND title_id = :t LIMIT 1',
|
||||
[':c' => $this->id, ':t' => $this->title_id]
|
||||
'SELECT awarded FROM owned_titles WHERE char_id = ? AND title_id = ? LIMIT 1',
|
||||
[$this->id, $this->title_id]
|
||||
);
|
||||
if ($q === false) throw new Exception('Failed to query title. (C::t)');
|
||||
|
||||
|
@ -267,6 +289,6 @@ class Character
|
|||
if ($a === false) return false;
|
||||
|
||||
$t['awarded'] = $a['awarded']; // add the awarded date to the title info
|
||||
return $t;
|
||||
return $this->title = $t;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
/**
|
||||
* Render the logout button's form.
|
||||
*/
|
||||
function c_logout_button()
|
||||
function c_logout_button(): string
|
||||
{
|
||||
return render('components/logout_button');
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ function c_logout_button()
|
|||
* Render the character bar. Relies on there being a character in the session. Without one, this will return an empty
|
||||
* string.
|
||||
*/
|
||||
function c_char_bar()
|
||||
function c_char_bar(): string
|
||||
{
|
||||
if (char() === false) return '';
|
||||
return render('components/char_bar', ['char' => char()]);
|
||||
|
@ -22,7 +22,7 @@ function c_char_bar()
|
|||
* Render the left sidebar navigation menu. Provide the active tab to highlight it. Will retrieve the current
|
||||
* tab from the GLOBALS.
|
||||
*/
|
||||
function c_left_nav()
|
||||
function c_left_nav(): string
|
||||
{
|
||||
return render('components/left_nav');
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ function c_left_nav()
|
|||
/**
|
||||
* Render the right sidebar menu.
|
||||
*/
|
||||
function c_right_nav()
|
||||
function c_right_nav(): string
|
||||
{
|
||||
return render('components/right_nav', ['c' => char()]);
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ function c_right_nav()
|
|||
/**
|
||||
* Render the debug query log.
|
||||
*/
|
||||
function c_debug_query_log()
|
||||
function c_debug_query_log(): string
|
||||
{
|
||||
return render('components/debug_query_log');
|
||||
}
|
||||
|
@ -46,15 +46,15 @@ function c_debug_query_log()
|
|||
/**
|
||||
* Render the character select radio buttons.
|
||||
*/
|
||||
function c_char_select_box($id, $char)
|
||||
function c_char_select_box(int $id, array $info): string
|
||||
{
|
||||
return render('components/char_select_box', ['id' => $id, 'char' => $char]);
|
||||
return render('components/char_select_box', ['id' => $id, 'c' => $info]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render an alert with a given type and message.
|
||||
*/
|
||||
function c_alert($type, $message)
|
||||
function c_alert(string $type, string $message): string
|
||||
{
|
||||
$a = $type !== 'danger' ? ' auto-close="5000"' : '';
|
||||
return "<div class=\"alert $type\"$a><div>$message</div> <a alert-close>×</a></div>";
|
||||
|
@ -63,7 +63,7 @@ function c_alert($type, $message)
|
|||
/**
|
||||
* Renders a danger alert with form errors, if there are any. Add an optional placement id.
|
||||
*/
|
||||
function c_form_errors($placement = '')
|
||||
function c_form_errors(string $placement = ''): string
|
||||
{
|
||||
$errors = $GLOBALS[($placement !== '' ? "form-errors-$placement" : 'form-errors')] ?? false;
|
||||
if ($errors === false) return '';
|
||||
|
@ -78,8 +78,14 @@ function c_form_errors($placement = '')
|
|||
* Generate a text form field. This component will automatically add the error class if there are errors for this field,
|
||||
* depending on the form-errors GLOBAL. Pass an optional form ID to target a specific form GLOBAL.
|
||||
*/
|
||||
function c_form_field($type, $name, $placeholder, $required = false, $autocomplete = "off", $formId = '')
|
||||
{
|
||||
function c_form_field(
|
||||
string $type,
|
||||
string $name,
|
||||
string $placeholder,
|
||||
bool $required = false,
|
||||
string $autocomplete = "off",
|
||||
string $formId = ''
|
||||
) {
|
||||
$errors = $GLOBALS[($formId !== '' ? "form-errors-$formId" : 'form-errors')] ?? false;
|
||||
$html = "<input type=\"$type\" name=\"$name\" id=\"$name\" placeholder=\"$placeholder\"";
|
||||
if ($required) $html .= ' required';
|
||||
|
@ -99,7 +105,15 @@ function c_debug_stopwatch()
|
|||
/**
|
||||
* Render a profile's stat grid using a character array.
|
||||
*/
|
||||
function c_profile_stats($char)
|
||||
function c_profile_stats(Character $char): string
|
||||
{
|
||||
return render('components/profile_stats', ['char' => $char]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a character's equipped gear for their profile.
|
||||
*/
|
||||
function c_equipped_gear(Character $char): string
|
||||
{
|
||||
return render('components/equipped_gear', ['char' => $char]);
|
||||
}
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
<?php
|
||||
|
||||
class Router
|
||||
{
|
||||
private array $routes = [];
|
||||
|
||||
/**
|
||||
* Add a route to the route tree. The route must be a URI path, and contain dynamic segments
|
||||
* using a colon prefix. (:id, :slug, etc)
|
||||
*
|
||||
* Example:
|
||||
* `router_add($routes, 'GET', '/posts/:id', function($id) { echo "Viewing post $id"; });`
|
||||
* `$r->add($routes, 'GET', '/posts/:id', function($id) { echo "Viewing post $id"; });`
|
||||
*/
|
||||
function router_add(&$routes, $method, $route, $handler)
|
||||
public function add(string $method, string $route, callable $handler): Router
|
||||
{
|
||||
// Expand the route into segments and make dynamic segments into a common placeholder
|
||||
$segments = array_map(function($segment) {
|
||||
|
@ -15,7 +19,7 @@ function router_add(&$routes, $method, $route, $handler)
|
|||
}, explode('/', trim($route, '/')));
|
||||
|
||||
// Push each segment into the routes array as a node, except if this is the root node
|
||||
$node = &$routes;
|
||||
$node = &$this->routes;
|
||||
foreach ($segments as $segment) {
|
||||
// skip an empty segment, which allows us to register handlers for the root node
|
||||
if ($segment === '') continue;
|
||||
|
@ -24,6 +28,8 @@ function router_add(&$routes, $method, $route, $handler)
|
|||
|
||||
// Add the handler to the last node
|
||||
$node[$method] = $handler;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -33,10 +39,10 @@ function router_add(&$routes, $method, $route, $handler)
|
|||
*
|
||||
* @return array ['code', 'handler', 'params']
|
||||
*/
|
||||
function router_lookup($routes, $method, $uri)
|
||||
public function lookup(string $method, string $uri): array
|
||||
{
|
||||
// node is a reference to our current location in the node tree
|
||||
$node = $routes;
|
||||
$node = $this->routes;
|
||||
|
||||
// params will hold any dynamic segments we find
|
||||
$params = [];
|
||||
|
@ -73,59 +79,13 @@ function router_lookup($routes, $method, $uri)
|
|||
: ['code' => 405, 'handler' => null, 'params' => []];
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a GET route
|
||||
*/
|
||||
function router_get(array &$routes, $route, callable $handler)
|
||||
public function get(string $route, callable $handler): Router
|
||||
{
|
||||
router_add($routes, 'GET', $route, $handler);
|
||||
return $this->add('GET', $route, $handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a POST route
|
||||
*/
|
||||
function router_post(array &$routes, $route, callable $handler)
|
||||
public function post(string $route, callable $handler): Router
|
||||
{
|
||||
router_add($routes, 'POST', $route, $handler);
|
||||
return $this->add('POST', $route, $handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a PUT route
|
||||
*/
|
||||
function router_put(array &$routes, $route, callable $handler)
|
||||
{
|
||||
router_add($routes, 'PUT', $route, $handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a DELETE route
|
||||
*/
|
||||
function router_delete(array &$routes, $route, callable $handler)
|
||||
{
|
||||
router_add($routes, 'DELETE', $route, $handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a PATCH route
|
||||
*/
|
||||
function router_patch(array &$routes, $route, callable $handler)
|
||||
{
|
||||
router_add($routes, 'PATCH', $route, $handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a router error by setting the response code and echoing an error message
|
||||
*/
|
||||
function router_error($code)
|
||||
{
|
||||
http_response_code($code);
|
||||
echo match ($code) {
|
||||
403 => 'Forbidden',
|
||||
404 => 'Not Found',
|
||||
405 => 'Method Not Allowed',
|
||||
418 => 'I\'m a teapot',
|
||||
999 => 'Cheating attempt detected',
|
||||
default => 'Unknown Error',
|
||||
};
|
||||
exit;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<div class="radio-block <?= $id === user()->char_id ? 'active' : '' ?>">
|
||||
<input type="radio" name="char_id" value="<?= $id ?>" id="char_<?= $id ?>"<?= $id === user()->char_id ? 'disabled' : '' ?>>
|
||||
<label for="char_<?= $id ?>">
|
||||
<?= $char['name'] ?>
|
||||
<span class="badge"><?= $char['level'] ?></span>
|
||||
<?= $c['name'] ?>
|
||||
<span class="badge"><?= $c['level'] ?></span>
|
||||
<span class="badge green selected">Active</span>
|
||||
</label>
|
||||
</div>
|
||||
|
|
22
templates/components/equipped_gear.php
Normal file
|
@ -0,0 +1,22 @@
|
|||
<div id="equipped-gear">
|
||||
<!-- Top tow; gloves, head, amulet -->
|
||||
<div class="top">
|
||||
<div><div class="item i-2x2"></div></div>
|
||||
<div><div class="item i-2x2"></div></div>
|
||||
<div><div class="item i-1x1"></div></div>
|
||||
</div>
|
||||
|
||||
<!-- Mid row; main hand, chest, off hand -->
|
||||
<div class="mid">
|
||||
<div><div class="item i-2x3"></div></div>
|
||||
<div><div class="item i-2x3"></div></div>
|
||||
<div><div class="item i-2x3"></div></div>
|
||||
</div>
|
||||
|
||||
<!-- Bot row; ring, boots, relic -->
|
||||
<div class="bot">
|
||||
<div><div class="item i-1x1"></div></div>
|
||||
<div><div class="item i-2x2"></div></div>
|
||||
<div><div class="item i-1x1"></div></div>
|
||||
</div>
|
||||
</div>
|
|
@ -54,7 +54,9 @@
|
|||
|
||||
<script type="module">
|
||||
import Tooltip from '/assets/scripts/tooltip.js';
|
||||
Tooltip.init();
|
||||
Tooltip.init({
|
||||
removalDelay: 0
|
||||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<input type="hidden" name="csrf" value="<?= csrf() ?>">
|
||||
|
||||
<div class="my-2 character-select">
|
||||
<?php foreach ($chars as $id => $char) echo c_char_select_box($id, $char); ?>
|
||||
<?php foreach ($chars as $i => $c) echo c_char_select_box($i, $c); ?>
|
||||
|
||||
<div class="mt-4 buttons">
|
||||
<button type="submit" class="ui button primary" name="action" value="select">Select</button>
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
<section class="left">
|
||||
<div class="avatar">
|
||||
<img src="/assets/img/rathalos.webp">
|
||||
<img class="border" src="/assets/img/ui/borders/water.webp">
|
||||
</div>
|
||||
|
||||
<?= c_profile_stats(char()) ?>
|
||||
|
@ -32,12 +33,16 @@
|
|||
|
||||
<div class="gear">
|
||||
<h4>Equipped Gear</h4>
|
||||
@TODO
|
||||
<?= c_equipped_gear(char()) ?>
|
||||
</div>
|
||||
|
||||
<div class="inv">
|
||||
<h4>Inventory</h4>
|
||||
@TODO
|
||||
<?php
|
||||
for ($i = 0; $i < char()->inv_slots; $i++) {
|
||||
echo '<img class="mr-1" src="/assets/img/ui/2x3.png">';
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
<section class="left">
|
||||
<div class="avatar">
|
||||
<img src="/assets/img/rathalos.webp">
|
||||
<img class="border" src="/assets/img/ui/borders/water.webp">
|
||||
</div>
|
||||
|
||||
<?= c_profile_stats($c) ?>
|
||||
|
@ -32,12 +33,16 @@
|
|||
|
||||
<div class="gear">
|
||||
<h4>Equipped Gear</h4>
|
||||
@TODO
|
||||
<?= c_equipped_gear($c) ?>
|
||||
</div>
|
||||
|
||||
<div class="inv">
|
||||
<h4>Inventory</h4>
|
||||
@TODO
|
||||
<?php
|
||||
for ($i = 0; $i < $c->inv_slots; $i++) {
|
||||
echo '<img class="mr-1" src="/assets/img/ui/2x3.png">';
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
|