Jump to content
MakeWebGames

Magictallguy

Administrators
  • Posts

    2,142
  • Joined

  • Last visited

  • Days Won

    148

Everything posted by Magictallguy

  1. If you're already using Docker, why not Dockerize your project? If you're not using containers, why not?! Once over the initial hurdle of "whaddafuqisthisballs?!", Docker/Podman/Portainer can be span up in 1 command. There are a few forks of the now open-source MCCv2 which include variations of configs to run in containers. On a personal note, since writing a drop-in Docker config (copy files -> paste files, done), I haven't touched LAMP/XAMPP/WAMP (couldn't anyway, I'm on Linux) and don't bloody want to!
  2. Agreed! However, the question of "how far away from original MCC do we want to take this?" was raised. I otherwise swear by dependency injection
  3. There's still quite a large following for this style of game. It would stand to evidence that people still consider it fun. Also, these games are making a resurgence, so they must be wanted.
  4. You'd be surprised what motivation does for one's desire to code. In one day, I refactored MCCv2 in its entirety to use ParagonIE's EasyDB - last night. Here's the branch 😉 Edit: And here's a Dockerized version
  5. If nobody beats me to it, consider it done. I'm not as experienced with Eloquent as I'd like to be, so I'll be avoiding implementation. If the concept gains traction, I'll bite the bullet and blow off the dust, as it were. That being said, replacing an ORM with another on a project this arguably small would be a small feat. As for PDO (which @ags_cs4 mentioned), I quite like ParagonIE's EasyDB. Its performance - while I can't provide statistics right now - has been exactly what I'm looking for; easy to use, fast, (relatively) light on resources. .. which, thinking about it, also opens another question - do we want to rely on package managers (npm, composer, etc.) or ship it all in-house?
  6. While that isn't my choice, I admit I'm curious! Semi-related; So, what about .. transactions? 2 method additions to the database class and we've got easy-enough transactions. I'm aware that MyISAM is a non-transactional storage engine, whereas InnoDB would support them fine (not to worry, MyISAM will just ignore the commit/rollback and run as "normal"). /** * @param string $name * @param array $arguments * @return void */ public function __call(string $name, array $arguments) { call_user_func_array([$this->connection_id, $name], $arguments); } /** * @param Closure $func * @return void */ public function try_transaction(Closure $func): void { try { $this->begin_transaction(); $func(); $this->commit(); } catch (mysqli_sql_exception $e) { $this->rollBack(); throw $e; } } Then, for usage, and using the new staff roles as example; /* BEFORE */ $this->db->query( 'DELETE FROM users_roles WHERE staff_role = ' . $role_id, ); $this->db->query( 'DELETE FROM staff_roles WHERE id = ' . $role_id, ); stafflog_add(ucfirst($log)); /* AFTER (where $role_id is the role's ID and $log is a string) */ $save = function () use ($role_id, $log) { $this->db->query( 'DELETE FROM users_roles WHERE staff_role = ' . $role_id, ); $this->db->query( 'DELETE FROM staff_roles WHERE id = ' . $role_id, ); stafflog_add(ucfirst($log)); }; $this->db->try_transaction($save); Do we want?
  7. Oh, sick! Gimme a moment to update the cronless crons to use the new staff roles and I'll fire PRs 🙂 Branches merged, PR sent
  8. So yeah, about having a place to send all these. The open-sourcing has renewed the soft spot for MCC
  9. So, thought I'd start the ball rolling on releasing up-to-date mods for MCCv2-PHP8-Community (or whatever Dave's calling it) - here's a staff roles overhaul, loosely based on Discord's Roles, and on a feature branch. Want it? (I may or may not be hinting at the possibility of an MVC-ish release too) For anyone else here, feel free to use/upgrade these concepts 🙂 Edit: Just finished overhauling the crons. Now with optional "use timestamps instead" alternative to crons, the ability to force-run a cron, and status logging
  10. This was fun. More, please! 😄 Ooh! Here's a potentially-horrifying idea. Lets go through and update all MCCv2 mods to be PHP8-compliant and release them under one mass repo (with author's permission if not considered abandonware, of course) >:D Ooooh! Can we do a module installer? plzplzplz
  11. As much as I would like to, I wasn't sure on just how far away from MCCv2's "origins" we wanted to pull it. So this PR is primarily PHP8-compliance (and a minor optimisation to the viewuser->staff->gethostbyaddr calls) Huh, second time I've heard that project today - and nope, but I'm curious to see what that would chuck back. Little bits, here and there. There's much more I'd like to do, but again, I don't want to pull it too far away from what it was (without community approval, at least) -- Edit: On a related note, I did write up a docker-compose.yml - runs OOTB with `docker compose up` (with optional -d flag to detach) - if we want to be able to dockerize it (great for dev *cough cough*)
  12. Done. PR sent. PR set as draft while I run tests. PR re-sent!
  13. I sure do - Orsokuma
  14. Without wanting to seem like I'm fanning the flames here; Ali, you've been posting in this topic with updates to the engine as you've gone along and responded to people posting here too. Does that not imply you've been checking as you go along? I can certainly understand the convenience of instant messaging platforms as I much prefer them myself. That being said, I've found public transparency to be useful. Not only does it allow people to read the answers to questions already asked (which might include their own), it also shows you're actively engaging the people using your stuff. Uridium raises a valid point. This is your own project-based thread in a programming-oriented forum, it makes perfect sense to talk about your project here
  15. This implies you've entered the wrong connection details. Hi, I'm one of the guys who wrote it 🙂
  16. Det høres ut som et "du"-problem kekekek
  17. You previously have successfully installed this mod. The error message you're getting, in this context, means that there isn't a match in your switch() statement You've probably seen something like this.. switch ($_GET['action']) { case 'something': doSomething(); break; case 'else': doSomethingElse(); break; default: echo 'Error, this script requires an action.'; break; } ..and in your URL, you've probably seen something like: yoursite.com/staff_users.php?action=example If you're trying to give an "action" that doesn't exist (i.e., isn't in your switch() statement), then you're gonna see that "Error, action required" message. In a nutshell, make sure that whatever you're clicking on your site is going to the right place. Note: GETDATA (in this case, the $_GET['action'] bit) is case-sensitive. I.e., "example" is not a match to "Example".
  18. Based almost entirely on this query INSERT INTO `loan_shark` (ls_id, `ls_userid`, `ls_amount`, `ls_interest`, ls_daily, `ls_remaining`, `ls_time`) VALUES ('', {$ir['userid']}, {$amount}, {$interest}, {$dailyPayment}, {$remaining}, {$time}) and a cursory glance at the rest of the code, I'm throwing a guess at this: CREATE TABLE loan_shark ( ls_id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, ls_userid INT NOT NULL, ls_amount INT NOT NULL DEFAULT 0, ls_daily INT NOT NULL DEFAULT 0, ls_remaining INT NOT NULL DEFAULT 0, ls_time INT NOT NULL DEFAULT 0, FOREIGN KEY (ls_userid) REFERENCES users(userid) );
  19. I'm certainly curious - please feel free to share your experiences. I'd like to learn ^.^
  20. If running on PHP 8.2 and above, you can use null coalescence $userid = $_SESSION['switch_to'] ?? $_SESSION['userid'];
  21. Welcome (back) to MWG! There's definitely still a love for the various styles of games found here on this site, along with programmers of varying degrees itching to get into projects. We will never die! 😄
  22. Quick tip for ya; if you need to dynamically resize an image on output, but want to keep its aspect ratio, avoid using the deprecated height and width attributes. Instead, a little CSS goes a long way. .panel-image { max-width: 150px; max-height: 100px; } <img src="{pic}" alt="A basic image title" class="panel-image"> Alternatively, you can inline the styling <img src="{pic}" alt="A basic title" style="max-width: 150px; max-height: 100px;">
  23. Ooh, I'm in!
  24. Soooo, I had some free time and wrote this. It clocks in at 1,149 lines, and combines 9 files (imadd, inventory, itembuy, iteminfo, itemsell, itemsend, itemuse, equip, unequip) into 1. It's PHP 7.4-compatible. <?php declare(strict_types=1); /** * For use on MCCodes Version 2.0.5b * We shouldn't just redistribute licensed software, so here's a rewrite ;) * I do *not* claim this to be efficient or better than the original, it's just a different take on things * The length and calculable complexity of this class is ugly. I would recommend splitting this into extendable classes and consider autoloading * * See the ItemHandler constants for configurable text outputs */ global $db, $ir, $h; require_once __DIR__ . '/globals.php'; ?> <!-- Remove this if you're using Bootstrap. I just like the naming scheme --> <!--suppress CssUnusedSymbol --> <style> .text-end { text-align: end; } .form-group { display: block; padding: 5px 0; } .text-danger { color: #f00; } .text-success { color: #050; } .table-responsive { min-height: 20vh; max-height: 99.9vh; min-width: 20vh; max-width: 99.9vh; overflow: auto; } .btn { margin: 0.75vh 1vw; padding: 0.5vh 1vw; } .btn-primary { color: #fff; background-color: #337ab7; border-color: #2e6da4; border-radius: 5%; } .alert { display: block; height: auto; line-height: 1em; max-width: 50%; padding: 0.6vh 3vw; margin: 0.5vh 1vw; border-radius: 5%; } .alert-danger { color: #a94442; background-color: #f2dede; border-color: #ebccd1; } .alert-success { color: #3c763d; background-color: #dff0d8; border-color: #d6e9c6; } .alert-info { color: #31708f; background-color: #d9edf7; border-color: #bce8f1; } .alert .alert-links { margin-top: 1.1em; } .alert .alert-title { display: block; font-weight: 700; font-size: 1.125em; line-height: 1em; margin-bottom: 0.2vh; } </style> <?php /** * ItemHandler * all-in-one class for imadd, inventory, itembuy, iteminfo, itemsell, itemsend, itemuse, equip, unequip */ class ItemHandler { // Don't touch these private database $db; private array $ir; private headers $headers; private ?string $action; private ?int $id; // Do touch these! private const SECURITY_TIMEOUT = 'Your request has timed out for security reasons'; private const NO_INVENTORY = 'You don\'t have any items in your inventory'; private const INVALID_ITEM = 'You didn\'t select a valid item'; private const INVALID_QUANTITY = 'You didn\'t enter a valid quantity'; private const INVALID_EQUIP_SLOT = 'You didn\'t provide a valid equipment slot'; private const INVALID_PRICE = 'You didn\'t enter a valid price'; private const INVALID_CURRENCY = 'You didn\'t selected a valid currency'; private const INVALID_USER = 'You didn\'t select a valid user'; private const USER_NOT_EXISTS = 'Your intended recipient doesn\'t exist'; private const ITEM_NOT_EXISTS = 'The item you selected doesn\'t exist'; private const EQUIP_NOT_EXISTS = 'You don\'t have that item equipped'; private const ITEM_NOT_BUYABLE = 'You can\'t buy that item'; private const ITEM_NOT_EQUIPPABLE = 'You can\'t equip that item'; private const ITEM_NOT_USABLE = 'You can\'t use that item'; private const INSUFFICIENT_FUNDS = 'You don\'t have enough money'; private const NOT_ENOUGH_ITEMS = 'You don\'t have enough of those'; private const WORTHLESS_ITEMS = 'You can\'t sell worthless items'; private const TRANSFER_NOT_PERMITTED = 'You can\'t transfer items to that player'; private const SELF_TRANSFER_NOT_PERMITTED = 'You can\'t transfer items to yourself'; private const ITEM_PLAYER_LOCATION_MISMATCH = 'You can\'t buy items from other locations'; private const ITEM_ADDED_TO_MARKET = 'You\'ve added %sx %s to the item market for %s'; private const ITEM_PURCHASED = 'You bought %sx %s for %s'; private const ITEM_SOLD = 'You sold %sx %s for %s'; private const ITEM_TRANSFERRED = 'You sent %sx %s to %s'; private const ITEM_USED = 'You used %s'; /** * true: prevents the player from selling items if the sell value is 0 * false: allows the player to use the sell items as a trashing function; permitting sales of items regardless of sell value * @var bool preventWorthlessSales */ private bool $preventWorthlessSales = false; private string $table_users_stats = 'userstats'; private string $table_users = 'users'; private array $bar_cols = ['energy', 'will', 'brave', 'hp']; private array $stat_cols = ['strength', 'agility', 'guard', 'labour', 'IQ']; private array $currencies = ['money', 'crystals']; private array $equipSlots = ['equip_primary', 'equip_secondary', 'equip_armor']; /** * @param database $db * @param array $ir * @param headers $h * @param string|null $action * @param int|null $id */ public function __construct(database $db, array $ir, headers $h, ?string $action = null, ?int $id = null) { $this->db = $db; $this->ir = $ir; $this->headers = $h; $this->action = $action; $this->id = $id; } /** * @return void */ public function run() { switch ($this->action) { case 'buy': $this->itemPurchase(); break; case 'info': $this->itemInfo(); break; case 'sell': $this->itemSell(); break; case 'send': $this->itemSend(); break; case 'use': $this->itemUse(); break; case 'market-add': $this->itemMarketAdd(); break; case 'unequip': $this->doItemUnequip(); break; case 'equip': $this->itemEquip(); break; default: $this->inventory(); break; } } /** * @return array */ private function getEquippedItems(): array { $items = []; // Maps the equipSlots to the user's equipped items, removes 0 values and duplicates $equipped_ids = array_unique(array_filter(array_map(function ($slot) { return $this->ir[$slot]; }, $this->equipSlots))); if (!empty($equipped_ids)) { $get_items = $this->db->query( 'SELECT itmid, itmname FROM items WHERE itmid IN (' . implode(', ', $equipped_ids) . ')', ); while ($row = $this->db->fetch_row($get_items)) { $items[$row['itmid']] = $row['itmname']; } $this->db->free_result($get_items); } return $items; } /** * @param array $row * @return bool */ private function hasEffect(array $row): bool { return $row['effect1_on'] || $row['effect2_on'] || $row['effect3_on']; } // ----------------------------------------- /** * @param array $equip * @return void */ private function renderEquips(array $equip): void { $slots = [ 'primary' => $equip[$this->ir['equip_primary']] ?? '', 'secondary' => $equip[$this->ir['equip_secondary']] ?? '', 'armor' => $equip[$this->ir['equip_armor']] ?? '', ]; $code = request_csrf_code('verf'); ?> <div class="table-responsive"> <table class="table"> <?php foreach ($slots as $slot => $item) { ?> <tr> <th scope="row"><?php echo $slot === 'armor' ? 'Armor' : ucfirst($slot) . ' Weapon'; ?></th> <?php if (!empty($item)) { $slot_col = 'equip_' . $slot; ?> <td><?php echo $item; ?></td> <td><a href="/inventory.php?action=unequip&type=<?php echo $slot_col; ?>&id=<?php echo $this->ir[$slot_col]; ?>&verf=<?php echo $code; ?>">Unequip Item</a></td> <?php } else { ?> <td>None equipped.</td> <td>&nbsp;</td> <?php } ?> </tr> <?php } ?> </table> </div> <hr> <?php } /** * @return void */ private function renderInventory(): void { $get_inventory = $this->db->query( 'SELECT inv.inv_id, inv.inv_qty, i.itmid, i.itmname, i.itmsellprice, i.effect1_on, i.effect2_on, i.effect3_on, i.weapon, i.armor, it.itmtypename FROM inventory AS inv INNER JOIN items AS i ON inv.inv_itemid = i.itmid INNER JOIN itemtypes AS it ON it.itmtypeid = i.itmtype WHERE inv.inv_userid = ' . $this->ir['userid'] . ' ORDER BY i.itmtype, i.itmname' ); if (!$this->db->num_rows($get_inventory)) { $this->alert('info', self::NO_INVENTORY); } $code = request_csrf_code('verf'); ?> <h3>Inventory</h3> <div class="table-responsive"> <table class="table"> <thead> <tr> <th scope="col">Item</th> <th scope="col">Sell Value</th> <th scope="col">Total Sell Value</th> <th scope="col">Links</th> </tr> </thead> <tbody> <?php $last_type = ''; $total_inventory_value = 0; while ($row = $this->db->fetch_row($get_inventory)) { $total_inventory_value += $row['itmsellprice'] * $row['inv_qty']; if ($last_type !== $row['itmtypename']) { $last_type = $row['itmtypename']; ?> <tr> <td colspan="4"> <strong><?php echo $last_type; ?></strong> </td> </tr> <?php } $prefix = ''; if ($row['weapon'] > 0) { $prefix .= '<span class="text-danger">*</span>'; } if ($row['armor'] > 0) { $prefix .= '<span class="text-success">*</span>'; } ?> <tr> <td><?php echo $prefix . $row['itmname'] . ($row['inv_qty'] > 1 ? ' x' . number_format((int)$row['inv_qty']) : ''); ?></td> <td><?php echo money_formatter($row['itmsellprice']); ?></td> <td><?php echo money_formatter($row['itmsellprice'] * $row['inv_qty']); ?></td> <td> [<a href="inventory.php?action=info&id=<?php echo $row['itmid']; ?>">Info</a>] [<a href="inventory.php?action=send&id=<?php echo $row['inv_id']; ?>">Send</a>] [<a href="inventory.php?action=sell&id=<?php echo $row['inv_id']; ?>">Sell</a>] [<a href="inventory.php?action=market-add&id=<?php echo $row['inv_id']; ?>">Add To Market</a>] <?php if ($this->hasEffect($row)) { ?> [<a href="inventory.php?action=use&id=<?php echo $row['inv_id']; ?>">Use</a>] <?php } if ($row['weapon'] > 0) { ?> [<a href="inventory.php?action=equip&id=<?php echo $row['inv_id']; ?>&verf=<?php echo $code; ?>&type=weapon">Equip as Weapon</a>] <?php } if ($row['armor'] > 0) { ?> [<a href="inventory.php?action=equip&id=<?php echo $row['inv_id']; ?>&verf=<?php echo $code; ?>&type=armor">Equip as Armor</a>] <?php } ?> </td> </tr> <?php } ?> <tr> <td colspan="4">&nbsp;</td> </tr> <tr> <td colspan="4" class="text-end"> <strong>Total Inventory Value:</strong> <?php echo money_formatter($total_inventory_value); ?> </td> </tr> </tbody> </table> </div> <p> <small><strong>NB:</strong> Items with a small red </small><span class="text-danger">*</span><small> next to their name can be used as weapons in combat.</small><br> <small>Items with a small green </small><span class="text-success">*</span><small> next to their name can be used as armor in combat.</small> </p> <?php $this->db->free_result($get_inventory); } /** * @return void */ private function inventory() { $this->renderEquips($this->getEquippedItems()); $this->renderInventory(); } // ----------------------------------------- /** * @param array $row * @return void */ private function renderItemInfo(array $row): void { ?> <h3>Item Information</h3> <table class="table" style="width: 75%;"> <thead> <tr> <th colspan="2" style="font-weight: 700;"> Looking up info on <?php echo $row['itmname']; ?> </th> </tr> </thead> <tbody> <tr> <td colspan="2"> The <?php echo $row['itmname']; ?> is <?php echo $this->aAn($row['itmtypename']); ?> Item<br> <p> <?php echo $row['itmdesc']; ?> </p> </td> </tr> </tbody> <thead> <tr> <th colspan="2">Item Info</th> </tr> <tr> <th scope="col">Item Buy Price</th> <th scope="col">Item Sell Price</th> </tr> </thead> <tbody> <tr> <td><?php echo $row['itmbuyprice'] > 0 ? money_formatter($row['itmbuyprice']) : 'N/A'; ?></td> <td><?php echo $row['itmsellprice'] > 0 ? money_formatter($row['itmsellprice']) : 'N/A'; ?></td> </tr> </tbody> </table> <p> <a href="inventory.php">Back to Inventory</a> </p> <?php } /** * @return void */ private function itemInfo(): void { if (!$this->id) { $this->alert('error', self::INVALID_ITEM); } $get_item = $this->db->query( 'SELECT i.itmid, i.itmname, i.itmdesc, i.itmbuyprice, i.itmsellprice, it.itmtypename FROM items AS i INNER JOIN itemtypes AS it ON it.itmtypeid = i.itmtype WHERE i.itmid = ' . $this->id . ' LIMIT 1' ); if (!$this->db->num_rows($get_item)) { $this->alert('error', self::ITEM_NOT_EXISTS); } $row = $this->db->fetch_row($get_item); $this->db->free_result($get_item); $this->renderItemInfo($row); } // ----------------------------------------- /** * @param array $row * @return void */ private function renderItemSell(array $row): void { $code = request_csrf_code('sell_item_' . $row['inv_id']); ?> <h3>Sell Item</h3> <p> Enter the amount of <?php echo $this->pluralize($row['itmname'], 0); ?> you want to sell. You have <strong><?php echo $row['inv_qty']; ?></strong>.<br> <?php if ($row['inv_qty'] > 1) { ?> Altogether, they are worth <?php echo money_formatter($row['inv_qty'] * $row['itmsellprice']); ?>.<br> <?php } ?> <span class="text-italic">Note: There is no confirmation. Be sure you've entered the amount you want to sell.</span> </p> <form action="inventory.php?action=sell&id=<?php echo $row['inv_id']; ?>" method="post"> <input type="hidden" name="verf" value="<?php echo $code; ?>" /> <div class="form-group"> <label for="qty">Quantity</label> <input type="number" name="qty" id="qty" class="form-control" placeholder="0" required> </div> <button type="submit" class="btn btn-primary"> Sell Items </button> </form> <?php } /** * @param array $row * @return array */ private function doItemSell(array $row): array { $qty = array_key_exists('qty', $_POST) && is_numeric($_POST['qty']) && (int)$_POST['qty'] > 0 ? (int)$_POST['qty'] : null; if (empty($_POST['qty'])) { return [ 'type' => 'error', 'content' => self::INVALID_QUANTITY, ]; } if ($qty > $row['inv_qty']) { return [ 'type' => 'error', 'content' => self::NOT_ENOUGH_ITEMS, ]; } $value = $qty * $row['itmsellprice']; if ($this->preventWorthlessSales && !$value) { return [ 'type' => 'error', 'content' => self::WORTHLESS_ITEMS, ]; } $formatted_value = money_formatter($value); item_remove($this->ir['userid'], $row['itmid'], $qty); $this->db->query( 'UPDATE users SET money = money + ' . $value . ' WHERE userid = ' . $this->ir['userid'] ); // At this point, consider parameterised queries. $this->db->query( sprintf( 'INSERT INTO itemselllogs (isUSER, isITEM, isTOTALPRICE, isQTY, isTIME, isCONTENT) VALUES (%u, %u, %u, %u, %u, \'%s\')', $this->ir['userid'], $row['itmid'], $value, $qty, time(), $this->db->escape($this->ir['username'] . ' sold ' . $qty . ' ' . $this->pluralize($row['itmname'], $qty) . ' for ' . $formatted_value) ) ); return [ 'type' => 'success', 'content' => sprintf(self::ITEM_SOLD, number_format($qty), $this->pluralize($row['itmname'], $qty), $formatted_value), ]; } /** * @return void */ private function itemSell(): void { if (!$this->id) { $this->alert('error', self::INVALID_ITEM); } $get_item = $this->db->query( 'SELECT inv.inv_id, inv.inv_qty, i.itmid, i.itmname, i.itmsellprice FROM inventory AS inv INNER JOIN items AS i ON i.itmid = inv.inv_itemid WHERE inv.inv_id = ' . $this->id . ' AND inv.inv_userid = ' . $this->ir['userid'] . ' LIMIT 1' ); if (!$this->db->num_rows($get_item)) { $this->alert('error', self::ITEM_NOT_EXISTS); } $row = $this->db->fetch_row($get_item); $this->db->free_result($get_item); $response = array_key_exists('verf', $_POST) && verify_csrf_code('sell_item_' . $row['inv_id'], stripslashes($_POST['verf'])) ? $this->doItemSell($row) : null; if (!empty($response)) { $this->alert($response['type'], $response['content']); } $this->renderItemSell($row); } // ----------------------------------------- /** * @return void */ private function itemPurchase(): void { $qty = array_key_exists('qty', $_POST) && is_numeric($_POST['qty']) && (int)$_POST['qty'] > 0 ? (int)$_POST['qty'] : null; if (empty($qty)) { $this->alert('error', self::INVALID_QUANTITY); } if (!$this->id) { $this->alert('error', self::INVALID_ITEM); } $get_item = $this->db->query( 'SELECT s.shopLOCATION, i.itmid, i.itmname, i.itmbuyable, i.itmbuyprice FROM shopitems AS si INNER JOIN shops AS s ON s.shopID = si.sitemSHOP INNER JOIN items AS i ON i.itmid = si.sitemITEMID WHERE si.sitemITEMID = ' . $this->id ); if (!$this->db->num_rows($get_item)) { $this->alert('error', self::ITEM_NOT_EXISTS); } $row = $this->db->fetch_row($get_item); $this->db->free_result($get_item); if (!$row['itmbuyable']) { $this->alert('error', self::ITEM_NOT_BUYABLE); } if ($row['shopLOCATION'] !== $this->ir['location']) { $this->alert('error', self::ITEM_PLAYER_LOCATION_MISMATCH); } $total_value = $qty * (int)$row['itmbuyprice']; if ($total_value > $this->ir['money']) { $this->alert('error', self::INSUFFICIENT_FUNDS); } item_add($this->ir['userid'], $row['itmid'], $qty); $this->db->query( 'UPDATE users SET money = money - ' . $total_value . ' WHERE userid = ' . $this->ir['userid'] ); $this->db->query( sprintf( 'INSERT INTO itembuylogs (ibUSER, ibITEM, ibTOTALPRICE, ibQTY, ibTIME, ibCONTENT) VALUES (%u, %u, %u, %u, %u, \'%s\')', $this->ir['userid'], $row['itmid'], $total_value, $qty, time(), $this->db->escape($this->ir['username'] . ' purchased ' . $qty . ' ' . $this->pluralize($row['itmname'], $qty) . ' for ' . money_formatter($total_value)) ) ); $this->alert('success', sprintf(self::ITEM_PURCHASED, number_format($qty), $this->pluralize($row['itmname'], $qty), money_formatter($total_value))); } // ----------------------------------------- /** * @param array $row * @return string[] */ private function doItemSend(array $row): array { $target_id = array_key_exists('user', $_POST) && is_numeric($_POST['user']) && (int)$_POST['user'] > 0 ? (int)$_POST['user'] : null; $qty = array_key_exists('qty', $_POST) && is_numeric($_POST['qty']) && (int)$_POST['qty'] > 0 ? (int)$_POST['qty'] : null; if ($qty > $row['inv_qty']) { return [ 'type' => 'error', 'content' => self::NOT_ENOUGH_ITEMS ]; } if (empty($target_id)) { return [ 'type' => 'error', 'content' => self::INVALID_USER ]; } if ($target_id === $this->ir['userid']) { return [ 'type' => 'error', 'content' => self::SELF_TRANSFER_NOT_PERMITTED ]; } $get_target = $this->db->query( 'SELECT userid, username, lastip FROM users WHERE userid = ' . $target_id ); if (!$this->db->num_rows($get_target)) { return [ 'type' => 'error', 'content' => self::USER_NOT_EXISTS ]; } $target = $this->db->fetch_row($get_target); $this->db->free_result($get_target); if ($target['lastip'] === $this->ir['lastip']) { return [ 'type' => 'error', 'content' => self::TRANSFER_NOT_PERMITTED ]; } item_remove($this->ir['userid'], $row['itmid'], $qty); item_add($target['userid'], $row['itmid'], $qty); event_add($target['userid'], 'You received ' . number_format($qty) . ' ' . $this->pluralize($row['itmname'], $qty) . ' from ' . $this->ir['username']); $this->db->query( sprintf( 'INSERT INTO itemxferlogs (ixFROM, ixTO, ixITEM, ixQTY, ixTIME, ixFROMIP, ixTOIP) VALUES (%u, %u, %u, %u, %u, \'%s\', \'%s\')', $this->ir['userid'], $target['userid'], $row['itmid'], $qty, time(), $this->ir['lastip'], $target['lastip'] ) ); return [ 'type' => 'success', 'content' => sprintf(self::ITEM_TRANSFERRED, number_format($qty), $this->pluralize($row['itmname'], $qty), $target['username']) ]; } /** * @param array $row * @param int|null $get_target_id * @return void */ private function renderItemSend(array $row, ?int $get_target_id = null): void { $code = request_csrf_code('send_item_' . $row['inv_id']); ?> <h3>Send Item</h3> <p> Enter the amount of <?php echo $this->pluralize($row['itmname'], 0); ?> you want to send. You have <strong><?php echo $row['inv_qty']; ?></strong>.<br> <span class="text-italic">Note: There is no confirmation. Be sure you've entered the amount you want to send.</span> </p> <form action="inventory.php?action=send&id=<?php echo $row['inv_id']; ?>" method="post"> <input type="hidden" name="verf" value="<?php echo $code; ?>" /> <div class="form-group"> <label for="user">User ID:</label> <input type="number" name="user" id="user" class="form-control" placeholder="0" value="<?php echo $get_target_id; ?>" required> </div> <div class="form-group"> <label for="qty">Quantity</label> <input type="number" name="qty" id="qty" class="form-control" placeholder="0" required> </div> <button type="submit" class="btn btn-primary"> Send Items </button> </form> <?php } /** * @return void */ private function itemSend(): void { if (!$this->id) { $this->alert('error', self::INVALID_ITEM); } $get_item = $this->db->query( 'SELECT inv.inv_id, inv.inv_qty, inv.inv_itemid, i.itmid, i.itmname FROM inventory AS inv INNER JOIN items AS i ON inv.inv_itemid = i.itmid WHERE inv.inv_id = ' . $this->id . ' AND inv.inv_userid = ' . $this->ir['userid'] . ' LIMIT 1' ); if (!$this->db->num_rows($get_item)) { $this->alert('error', self::ITEM_NOT_EXISTS); } $row = $this->db->fetch_row($get_item); $this->db->free_result($get_item); $response = array_key_exists('verf', $_POST) && verify_csrf_code('send_item_' . $this->id, stripslashes($_POST['verf'])) ? $this->doItemSend($row) : null; if (!empty($response)) { $this->alert($response['type'], $response['content']); } $get_target_id = array_key_exists('user', $_GET) && is_numeric($_GET['user']) && (int)$_GET['user'] > 0 ? (int)$_GET['user'] : null; $this->renderItemSend($row, $get_target_id); } // ----------------------------------------- /** * @return void */ private function itemUse(): void { if (!$this->id) { $this->alert('error', self::INVALID_ITEM); } $get_item = $this->db->query( 'SELECT inv.inv_id, inv.inv_qty, i.itmid, i.itmname, i.effect1_on, i.effect2_on, i.effect3_on, i.effect1, i.effect2, i.effect3 FROM inventory AS inv INNER JOIN items AS i ON i.itmid = inv.inv_itemid WHERE inv.inv_id = ' . $this->id . ' AND inv.inv_userid = ' . $this->ir['userid'] . ' LIMIT 1' ); if (!$this->db->num_rows($get_item)) { $this->alert('error', self::ITEM_NOT_EXISTS); } $row = $this->db->fetch_row($get_item); $this->db->free_result($get_item); if (!$this->hasEffect($row)) { $this->alert('error', self::ITEM_NOT_USABLE); } for ($i = 1; $i <= 3; ++$i) { if (!$row['effect' . $i . '_on']) { continue; } $effect = unserialize($row['effect' . $i]); $max_stat = 'max' . $effect['stat']; $inc_amount = $effect['inc_type'] === 'percent' ? (in_array($effect['stat'], $this->bar_cols) ? round($this->ir[$max_stat] / 100 * $effect['inc_amount']) : round($this->ir[$effect['stat']] / 100 * $effect['inc_amount'])) : $effect['inc_amount']; if ($effect['dir'] === 'pos') { if (in_array($effect['stat'], $this->bar_cols)) { $this->ir[$effect['stat']] = min($this->ir[$effect['stat']] + $inc_amount, $this->ir[$max_stat]); } else { $this->ir[$effect['stat']] += $inc_amount; } } else { $this->ir[$effect['stat']] = max($this->ir[$effect['stat']] - $inc_amount, 0); } $table = in_array($effect['stat'], $this->stat_cols) ? $this->table_users_stats : $this->table_users; $this->db->query( 'UPDATE ' . $table . ' SET ' . $effect['stat'] . ' = ' . $this->ir[$effect['stat']] . ' WHERE userid = ' . $this->ir['userid'] ); } item_remove($this->ir['userid'], $row['itmid'], 1); $this->alert('success', sprintf(self::ITEM_USED, $this->aAn($row['itmname']))); } // ----------------------------------------- /** * @param array $row * @return array */ private function doItemMarketAdd(array $row): array { $price = array_key_exists('price', $_POST) && is_numeric($_POST['price']) && (int)$_POST['price'] > 0 ? (int)$_POST['price'] : null; $qty = array_key_exists('qty', $_POST) && is_numeric($_POST['qty']) && (int)$_POST['qty'] > 0 ? (int)$_POST['qty'] : null; $currency = array_key_exists('currency', $_POST) && in_array($_POST['currency'], $this->currencies) ? $_POST['currency'] : null; if (empty($qty)) { return [ 'type' => 'error', 'content' => self::INVALID_QUANTITY ]; } if (empty($price)) { return [ 'type' => 'error', 'content' => self::INVALID_PRICE ]; } if (empty($currency)) { return [ 'type' => 'error', 'content' => self::INVALID_CURRENCY ]; } if ($qty > $row['inv_qty']) { return [ 'type' => 'error', 'content' => self::NOT_ENOUGH_ITEMS ]; } $get_dupe = $this->db->query( sprintf( 'SELECT imID FROM itemmarket WHERE imITEM = %u AND imPRICE = %u AND imADDER = %u AND imCURRENCY = \'%s\'', $row['itmid'], $price, $this->ir['userid'], $currency ) ); $dupe = $this->db->num_rows($get_dupe) ? $this->db->fetch_single($get_dupe) : null; $this->db->free_result($get_dupe); if ($dupe) { $this->db->query( 'UPDATE itemmarket SET imQTY = imQTY + ' . $qty . ' WHERE imID = ' . $dupe ); } else { $this->db->query( sprintf( 'INSERT INTO itemmarket (imITEM, imADDER, imPRICE, imCURRENCY, imQTY) VALUES (%u, %u, %u, \'%s\', %u)', $row['itmid'], $this->ir['userid'], $price, $currency, $qty ) ); } item_remove($this->ir['userid'], $row['itmid'], $qty); $this->db->query( sprintf( 'INSERT INTO imarketaddlogs (imaITEM, imaPRICE, imaINVID, imaADDER, imaTIME, imaCONTENT) VALUES (%u, %u, %u, %u, %u, \'%s\')', $row['itmid'], $price, $row['inv_id'], $this->ir['userid'], time(), $this->db->escape($this->ir['username'] . ' added ' . $qty . ' ' . $this->pluralize($row['itmname'], $qty) . ' to the item market for ' . money_formatter($price)) ) ); return [ 'type' => 'success', 'content' => sprintf(self::ITEM_ADDED_TO_MARKET, $qty, $this->pluralize($row['itmname'], $qty), money_formatter($price)) ]; } /** * @param array $row * @return void */ private function renderItemMarketAdd(array $row): void { $code = request_csrf_code('market_item_' . $this->id); ?> <h3>Add Item to Market</h3> <p> Enter the amount of <?php echo $this->pluralize($row['itmname'], 0); ?> you want to list on the item market. You have <strong><?php echo $row['inv_qty']; ?></strong>.<br> Set the currency you want to receive and the price for <em>each</em> item. </p> <form action="inventory.php?action=market-add&id=<?php echo $this->id; ?>" method="post"> <input type="hidden" name="verf" value="<?php echo $code; ?>" /> <div class="form-group"> <label for="qty">Quantity</label> <input type="number" name="qty" id="qty" class="form-control" placeholder="0" required> </div> <div class="form-group"> <label for="price">Price</label> <input type="number" name="price" id="price" class="form-control" placeholder="0" required> </div> <div class="form-group"> <label for="currency">Currency</label> <select name="currency" id="currency" class="form-control"> <?php foreach ($this->currencies as $currency) { printf('<option value="%s">%s</option>', $currency, ucfirst($currency)); } ?> </select> </div> <button type="submit" class="btn btn-primary"> Add Item To Market </button> </form> <?php } /** * @return void */ private function itemMarketAdd(): void { if (!$this->id) { $this->alert('error', self::INVALID_ITEM); } $get_item = $this->db->query( 'SELECT inv.inv_id, inv.inv_qty, i.itmid, i.itmname FROM inventory AS inv INNER JOIN items AS i ON inv.inv_itemid = i.itmid WHERE inv.inv_id = ' . $this->id . ' AND inv.inv_userid = ' . $this->ir['userid'] . ' LIMIT 1' ); if (!$this->db->num_rows($get_item)) { $this->alert('error', self::ITEM_NOT_EXISTS); } $row = $this->db->fetch_row($get_item); $this->db->free_result($get_item); $response = array_key_exists('verf', $_POST) && verify_csrf_code('market_item_' . $this->id, stripslashes($_POST['verf'])) ? $this->doItemMarketAdd($row) : null; if (!empty($response)) { $this->alert($response['type'], $response['content']); } $this->renderItemMarketAdd($row); } // ----------------------------------------- /** * @return void */ private function doItemUnequip(): void { $type = array_key_exists('type', $_GET) && in_array($_GET['type'], $this->equipSlots) ? $_GET['type'] : null; if (empty($type)) { $this->alert('error', self::INVALID_EQUIP_SLOT); } if (empty($this->ir[$type])) { $this->alert('error', self::EQUIP_NOT_EXISTS); } item_add($this->ir['userid'], $this->ir[$type], 1); $this->db->query( 'UPDATE users SET ' . $type . ' = 0 WHERE userid = ' . $this->ir['userid'] ); $names = array_map(function ($slot) { $slot = str_replace('equip_', '', $slot); return ucfirst($slot) . ($slot !== 'armor' ? ' Weapon' : ''); }, $this->equipSlots); $this->alert('success', 'The item in your ' . $names[$type] . ' slot has been unequipped.'); } // ----------------------------------------- /** * @param array $row * @return array */ private function doEquipArmor(array $row): array { if (!array_key_exists('verf', $_GET) || !verify_csrf_code('verf', $_GET['verf'])) { return [ 'type' => 'error', 'content' => self::SECURITY_TIMEOUT, ]; } if ($this->ir['equip_armor'] > 0) { item_add($this->ir['userid'], $this->ir['equip_armor'], 1); } item_remove($this->ir['userid'], $row['itmid'], 1); $this->db->query( 'UPDATE users SET equip_armor = ' . $row['itmid'] . ' WHERE userid = ' . $this->ir['userid'] ); return [ 'type' => 'success', 'content' => 'Your armor has been equipped.' ]; } /** * @param array $row * @return string[] */ private function doEquipWeapon(array $row): array { $slots = [...array_slice($this->equipSlots, 0, 2)]; $type = array_key_exists('type', $_POST) && in_array($_POST['type'], $slots) ? $_POST['type'] : null; if (empty($type)) { return [ 'type' => 'error', 'content' => self::INVALID_EQUIP_SLOT ]; } if ($this->ir[$type] > 0) { item_add($this->ir['userid'], $this->ir[$type], 1); } item_remove($this->ir['userid'], $row['itmid'], 1); $this->db->query( 'UPDATE users SET ' . $type . ' = ' . $row['itmid'] . ' WHERE userid = ' . $this->ir['userid'] ); return [ 'type' => 'success', 'content' => 'Your weapon has been equipped.' ]; } /** * @param array $row * @param bool $multi * @return void */ private function renderEquipSelect(array $row, bool $multi = false): void { $response = array_key_exists('equip_' . $row['inv_id'], $_POST) && verify_csrf_code('equip_' . $row['inv_id'], $_POST['equip_' . $row['inv_id']]) ? (array_key_exists('type', $_POST) && $_POST['type'] === 'equip_armor' ? $this->doEquipArmor($row) : $this->doEquipWeapon($row) ) : null; if (!empty($response)) { $this->alert($response['type'], $response['content']); } $code = request_csrf_code('equip_' . $row['inv_id']); ?> <h3>Equip Item</h3> <p> Please choose the slot to equip your <?php echo $row['itmname']; ?>. If you already have an item equipped in that slot, it will be unequipped and returned to your inventory. </p> <form action="inventory.php?action=equip&id=<?php echo $this->id; ?>" method="post"> <input type="hidden" name="equip_<?php echo $row['inv_id']; ?>" value="<?php echo $code; ?>" /> <?php $cnt = 0; foreach ($this->equipSlots as $slot) { if ($slot === 'equip_armor' && !$multi) { continue; } ?> <div class="form-group"> <label for="<?php echo $slot; ?>"> <input type="radio" name="type" id="<?php echo $slot; ?>" value="<?php echo $slot; ?>" <?php echo !$cnt ? 'checked' : ''; ?> /> <?php echo ucfirst(str_replace('equip_', '', $slot)) . ($slot !== 'equip_armor' ? ' Weapon' : ''); ?> </label> </div> <?php ++$cnt; } ?> <button type="submit" class="btn btn-primary"> Equip Item </button> </form> <?php } /** * @return void */ private function itemEquip(): void { if (!$this->id) { $this->alert('error', self::INVALID_ITEM); } $get_item = $this->db->query( 'SELECT inv.inv_id, inv.inv_qty, i.itmid, i.itmname, i.weapon, i.armor FROM inventory AS inv INNER JOIN items AS i ON inv.inv_itemid = i.itmid WHERE inv.inv_id = ' . $this->id . ' AND inv.inv_userid = ' . $this->ir['userid'] . ' LIMIT 1' ); if (!$this->db->num_rows($get_item)) { $this->alert('error', self::ITEM_NOT_EXISTS); } $row = $this->db->fetch_row($get_item); $this->db->free_result($get_item); if (!$row['weapon'] && !$row['armor']) { $this->alert('error', self::ITEM_NOT_EQUIPPABLE); } if ($row['armor'] && !$row['weapon']) { $response = $this->doEquipArmor($row); $this->alert($response['type'], $response['content']); } elseif ($row['weapon'] && !$row['armor']) { $this->renderEquipSelect($row); } else { $type = array_key_exists('type', $_GET) && in_array($_GET['type'], ['weapon', 'armor']) ? $_GET['type'] : null; if ($type === 'armor') { $response = $this->doEquipArmor($row); $this->alert($response['type'], $response['content']); } $this->renderEquipSelect($row, true); } } // ----------------------------------------- /** * @param string $type * @param string $content * @return void */ private function alert(string $type, string $content): void { $alert = $type === 'error' ? 'danger' : $type; ?> <div class="alert alert-<?php echo $alert; ?>" role="alert"> <span class="alert-title"><?php echo ucfirst($type); ?></span> <?php echo $content; ?> <div class="alert-links"> <a href="inventory.php">Back to Inventory</a> </div> </div> <?php $this->headers->endpage(); exit; } /** * @param string $word * @return string */ private function aAn(string $word): string { $first_letter = substr($word, 0, 1); return in_array($first_letter, ['a', 'e', 'i', 'o', 'u']) ? 'an ' . $word : 'a ' . $word; } /** * Note: This does not respect irregular plurals in the slightest. It's just a quick and dirty way to whack an s on the end if $amount <> 1 and the word doesn't already end in s. * @param string $word * @param int $amount * @return string */ private function pluralize(string $word, int $amount = 1): string { $last_letter = substr($word, -1); return strtolower($last_letter) === 's' || $amount === 1 ? $word : $word . 's'; } } // basic int validation $_GET['id'] = array_key_exists('id', $_GET) && is_numeric($_GET['id']) && (int)$_GET['id'] > 0 ? (int)$_GET['id'] : null; $_GET['action'] ??= null; (new ItemHandler($db, $ir, $h, $_GET['action'], $_GET['id']))->run();
×
×
  • Create New...