-
Posts
2,123 -
Joined
-
Last visited
-
Days Won
143
Content Type
Profiles
Forums
Events
Everything posted by Magictallguy
-
I really like the ability to visualise how the crime formulae may result; so I took your idea and added the ability to select from an existing crime, or assume the default formula ((WILL*0.8)/2.5)+(LEVEL/4) and calculate as desired. I also stripped the jQuery dependence; pure vanilla JS, woo! Fair note to our more sensitive-eyed programmers; there's no dark mode by default. Note: Written in a PHP8.4 environment. Older versions may need to change the match() call to a switch() equivalent crime-formula.php <?php declare(strict_types=1); $_GET['action'] ??= null; $_GET['id'] = array_key_exists('id', $_GET) && is_numeric($_GET['id']) && (int)$_GET['id'] > 0 ? (int)$_GET['id'] : null; class CrimeFormula { private static ?self $inst = null; private ?database $db; private ?array $settings; /** * @param database $db * @param array $settings */ public function __construct(database $db, array $settings) { $this->db = $db; $this->settings = $settings; $this->run(); } /** * @return void */ private function run(): void { $response = match ($_GET['action']) { 'get-crime-by-id' => $this->getCrimeById($_GET['id']), default => [ 'type' => 'error', 'message' => 'No action given', ], }; if ($this->isAjax()) { header('Content-type: application/json'); echo json_encode($response); exit; } if (array_key_exists('location', $response)) { header('Location: ' . $response['location']); exit; } echo $this->template('crime-select', [ '%id%' => $_GET['id'], '%menu.opts:crimes%' => $this->renderMenuOptsCrimes($_GET['id']), ]); } /** * @param int|null $id * * @return array|null */ private function getCrimeById(?int $id): ?array { if (empty($id)) { return null; } $get_crime = $this->db->query( 'SELECT * FROM crimes WHERE crimeID = ' . $id . ' LIMIT 1', ); $row = $this->db->fetch_row($get_crime); $this->db->free_result($get_crime); return $row ?? null; } /** * @return bool */ private function isAjax(): bool { return array_key_exists('HTTP_X_REQUESTED_WITH', $_SERVER) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest'; } /** * @param string $fileName * @param array $replacements * * @return string|null */ private function template(string $fileName, array $replacements = []): ?string { $path = str_replace('/', DIRECTORY_SEPARATOR, __DIR__ . '/' . $fileName . '.html'); if (!file_exists($path)) { return null; } $content = file_get_contents($path); return strtr($content, $replacements); } /** * @param int|null $selected * * @return string */ private function renderMenuOptsCrimes(?int $selected = null): string { $ret = ''; $rows = $this->db->query( 'SELECT crimeID, crimeNAME, crimeBRAVE FROM crimes ORDER BY crimeBRAVE', ); while ($row = $this->db->fetch_row($rows)) { $ret .= sprintf( '<option value="%u" %s>%s [%s brave]</option>%s', $row['crimeID'], (int)$row['crimeID'] === $selected ? 'selected' : '', stripslashes(htmlspecialchars($row['crimeNAME'])), number_format((int)$row['crimeBRAVE']), PHP_EOL, ); } $this->db->free_result($rows); return $ret; } /** * @param database $db * @param array $settings * * @return self|null */ public static function getInstance(database $db, array $settings): ?self { if (self::$inst === null) { self::$inst = new self($db, $settings); } return self::$inst; } } global $db, $set; require_once __DIR__ . '/globals_nonauth.php'; $module = CrimeFormula::getInstance($db, $set); crime-select.html <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Crime Formula Tests</title> </head> <body> <div class="container" style="margin: 0 auto;"> <form id="crime-selection-form" method="get"> <div class="row py-2"> <div class="col"> <div class="form-group"> <label for="crime">Select Crime</label> <select name="crime" id="crime" class="form-control"> <option value="0" selected>--- None ---</option> %menu.opts:crimes% </select> </div> </div> </div> <div class="row py-2"> <div class="col-lg-6 col-md"> <div class="form-group"> <label for="level">Level</label> <input type="number" name="level" id="level" class="form-control" value="1" step="1"> </div> </div> <div class="col-lg-6 col-md"> <div class="form-group"> <label for="will">Will</label> <input type="number" name="will" id="will" class="form-control" value="100" step="1"> </div> </div> </div> </form> </div> <div class="container"> <span class="d-block m-1" id="crime-formula-raw"></span> <span class="d-block m-1" id="crime-formula-formatted"></span> <span class="d-block m-1" id="crime-response"></span> </div> <script> const apiCall = (path) => { return fetch(path, { headers: { 'credentials': 'same-origin', 'X-Requested-With': 'XMLHttpRequest', 'Content-Type': 'application/json' }, signal: AbortSignal.timeout(5000) }); }; const getCrime = (id, callback) => { apiCall(`crime-formula.php?action=get-crime-by-id&id=${id}`) .then(response => response.json()) .then(data => callback(data)); }; const responseElem = document.getElementById("crime-response"); const formulaRawElem = document.getElementById("crime-formula-raw"); const formulaFormattedElem = document.getElementById("crime-formula-formatted"); const updateCrimeInfo = (formula, will, level) => { let formulaFormatted = formula.replace('WILL', will).replace('LEVEL', level); formulaRawElem.innerText = formula; formulaFormattedElem.innerText = formulaFormatted; /* Dirty. Don't do this if you can avoid it. And certainly *never* trust user input with it. */ let amnt = eval(formulaFormatted); responseElem.innerText = amnt.toLocaleString(undefined, {maximumFractionDigits: 3}) + "%"; }; const getCrimeFullEvent = (e) => { e.stopPropagation(); e.preventDefault(); if ([undefined, null].includes(responseElem) || [undefined, null].includes(formulaRawElem) || [undefined, null].includes(formulaFormattedElem)) { console.error("Form element missing: crime-response/crime-formula-raw/crime-formula-formatted"); return; } let will = document.getElementById("will").value; let level = document.getElementById("level").value; let id = document.getElementById("crime").value; if ([undefined, null].includes(will)) { will = 100; } if ([undefined, null].includes(level)) { level = 1; } if ([undefined, null].includes(id)) { id = 0; } let formula = '((WILL*0.8)/2.5)+(LEVEL/4)'; if (id > 0) { getCrime(id, (data) => { if ([undefined, null].includes(data)) { console.error("Blank crime data response"); return false; } if (data.hasOwnProperty("type") && data.type !== "success") { console.error(data); return false; } updateCrimeInfo(data.crimePERCFORM, will, level); return true; }); } else { updateCrimeInfo(formula, will, level); } }; window.addEventListener("DOMContentLoaded", () => { const formElem = document.getElementById("crime-selection-form"); if ([undefined, null].includes(formElem)) { console.error("Form element missing: crime-selection-form"); return; } formElem.addEventListener("keyup", (e) => getCrimeFullEvent(e)); formElem.addEventListener("mouseup", (e) => getCrimeFullEvent(e)); }); </script> </body> </html> 2025-09-22 18-58-18.mp4
-
For anyone wanting to take this on, you're lookin' at CrateJS to re-wrap
-
Note: In higher-traffic areas, this may introduce race conditions. In that scenario, I'd recommend using the database (the one your project is already using) to track this information; amongst other things, most RDBMS support query queuing as default. For low-traffic areas and areas where you don't foresee an upsurge in activity (refer to your metrics), then this file-based approach may be perfectly sufficient for your needs
-
Technically, yes! It is certainly possible to run multiple sites that reference a single database. More often than not, though, the question is "why"? If, for example, I was to start a mafia-style game with MCC then I decided I wanted a sci-fi style game with gRPG; I could do the leg-work and make them compatible. That being said, what of the players? Someone playing a mafia-style game with [x] currency will likely be confused by having [y] currency in the sci-fi game. I'm a firm believer of "fit for purpose"; each project gets their own database(s) as necessary. If you want to make a merge of the open-source engines, you go right ahead 😄
-
These can be added into the Dockerfile as RUN directives
-
Its getting kinda Lonely at MWG
Magictallguy replied to Uridium's topic in Feedback and Site Support
Summer! Life generally tends to get louder in the summer. People are out (and away from their keyboards) more often. We're still just a message away though. As for gRPG, I've already framed a structure for the multi-PHP-version supports and have begun tweaking the various branches -
I made my first browser game! A simple Tetris game! What do you think?
Magictallguy replied to wlr_inc's topic in General
Minor control issue; W responds (piece rotation), but ASD do not. All arrow keys work. -
I have not as I wasn't thinking of backwards compatibility. This change is part of a rolling set of changes I intend to make to suit more recent technologies. As for the claim that "nobody" has PHP 8.4 yet; SiteEU, WebWiz, HawkHost, Kinsta, SwitchWeb, NameCheap, GoDaddy, Google Cloud Hosting and AWS absolutely do support it. Honourable mention to cPanel's EasyApache which added PHP 8.4 support in December just gone. Again, I will add backward compatibility for PHP 8.0. Might send up a couple of branched releases so people can select their desired version.
-
You changed setenv to getenv? Of course that wouldn't work
-
Back when I was on Windows, I loved mLocati's PowerShell PHP Manager - you may wish to consider looking into it. Its primary purpose is to provide an easy way to install multiple PHP versions and choose whichever one the system considers active. As for your host, perhaps a request to their support might be fruitful.
-
PHP 8.4 required. I'll push an 8.0-friendly version shortly
-
I decided it's time to blow off the dust and give this project some TLC. So, a small update list! now comes with a Docker configuration no longer relies on Composer There will be more updates to come as the longer I look, the more I kick my younger self 😄
-
Head up to the object/property declarations and initialise the $querystring property Find: protected string $querystring; Replace with: protected string $querystring = '';
-
That's the installation of Composer itself. You now need to run `composer install` on the command line in the project's root to grab the 3rd-party dependencies. A future release will factor out the Composer reliance. But for now, that's the way
-
The autoload file is generated after running `composer install` in the project's root directory.
-
Instructions are available at https://getcomposer.org/
-
You'll probably need to your files over to the Docker with COPY
-
Oop, my mistake, forgot to include the watch directive. Assuming the docker-compose.yml you posted earlier is up to date services: # PHP service (for Laravel) php: build: context: . dockerfile: Dockerfile container_name: php volumes: - .:/var/www/ networks: - portfolio_network working_dir: /var/www/ # Nginx service nginx: image: nginx:latest container_name: nginx volumes: - ./:/var/www - ./nginx/:/etc/nginx/conf.d/ ports: - "80:80" depends_on: - php networks: - portfolio_network develop: watch: - action: sync path: . target: /var/www # MySQL service mysql: image: mysql:5.7 container_name: mysql environment: MYSQL_ROOT_PASSWORD: MySQLPass MYSQL_DATABASE: portfolio_db MYSQL_USER: portfolio_user MYSQL_PASSWORD: portfolio_password volumes: - mysql_data:/var/lib/mysql networks: - portfolio_network volumes: mysql_data: networks: portfolio_network: driver: bridge Addition started line 27
-
docker compose watch Ctrl + C Does it build?
-
OS: Linux (Ubuntu) Editors: Depends on what the active project's requirements are. Most likely, JetBrains PhpStorm, JetBrains WebStorm, or VSCode for the smaller things. Database: The SQL extension bundled with JetBrains. Deployment: Docker for local dev deployment and personal testing, CI/CD via GitLab (pref.)/GitHub which includes a number of sanity checks before actually deploying to live. On PHP's side of things, I'll swear by Composer. For Node, it is (of course) npm For things that are usually single-purpose (an "I need a thing for this, and only this" thing), I'll either search Google for something close enough or just write the utility myself. It's rare that I have to (S)FTP into a site. When I do, I'll quickly hop in with the bundled SFTP extension in JetBrains, or the SFTP extension by Natizyskunk for VSCode. A decent editor is a good place to start, but there are times when you may find that your feature-rich editor doesn't have a feature you want/need. For example, the String Manipulation extension (for IntelliJ (the JetBrains people)) is a nifty tool for quick-swapping ", ', and `, lowercase, Sentence case, UPPERCASE, Title Case, snake_case and CamelCase, and a couple of other little helper utilities; complete with hotkey support! I use EnvVars throughout my projects (love me some .env), so I like the .env files support extension which does exactly what you think it does: provides "native" support for .env files. An honourable mention for the Prisma ORM extension which I'm using for one of my other projects (yup, cheeky plug time - that project is KumaBot Defender, an anti-bot for use on Twitch), which adds full support for the Prisma syntax.
-
A little MCC history - dabomstew was a Torn City player and wanted his own version, so he and ColdBlooded wrote MCC. Torn City came first and inspired MCC
-
I said PowerShell, not CMD 😉