ava borders, controllers, item boxes

This commit is contained in:
Sky Johnson 2024-10-24 18:23:55 -05:00
parent 69e057a96e
commit 0e0a4c8e38
49 changed files with 313 additions and 203 deletions

File diff suppressed because one or more lines are too long

View File

@ -276,6 +276,7 @@ span.badge {
}
.tooltip {
position: absolute;
background-color: black;
color: white;
border: 1px solid #666;

View File

@ -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;
}
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -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');

View File

@ -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];

View File

@ -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('/');

View File

@ -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();

View File

@ -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]);
}

View File

@ -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', [

View File

@ -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) {

View File

@ -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;
}
}

View File

@ -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>&times;</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]);
}

View File

@ -1,21 +1,25 @@
<?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) {
return str_starts_with($segment, ':') ? ':x' : $segment;
}, 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,19 +28,21 @@ function router_add(&$routes, $method, $route, $handler)
// Add the handler to the last node
$node[$method] = $handler;
}
/**
return $this;
}
/**
* Perform a lookup in the route tree for a given method and URI. Returns an array with a result code,
* a handler if found, and any dynamic parameters. Codes are 200 for success, 404 for not found, and
* 405 for method not allowed.
*
* @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 = [];
@ -71,61 +77,15 @@ function router_lookup($routes, $method, $uri)
return isset($node[$method])
? ['code' => 200, 'handler' => $node[$method], 'params' => $params ?? []]
: ['code' => 405, 'handler' => null, 'params' => []];
}
}
/**
* Register a GET route
*/
function router_get(array &$routes, $route, callable $handler)
{
router_add($routes, 'GET', $route, $handler);
}
public function get(string $route, callable $handler): Router
{
return $this->add('GET', $route, $handler);
}
/**
* Register a POST route
*/
function router_post(array &$routes, $route, callable $handler)
{
router_add($routes, '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;
public function post(string $route, callable $handler): Router
{
return $this->add('POST', $route, $handler);
}
}

View File

@ -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>

View 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>

View File

@ -54,7 +54,9 @@
<script type="module">
import Tooltip from '/assets/scripts/tooltip.js';
Tooltip.init();
Tooltip.init({
removalDelay: 0
});
</script>
<script>

View File

@ -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>

View File

@ -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>

View File

@ -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>