mirror of
https://github.com/Shadowss/TravianZ.git
synced 2026-07-17 18:16:09 +00:00
c7ba50036f
Add (Admin Panel) : Multi-account detection, Push protection, Heatmap, Gold Shop (redem codes), Blacklist user names and Quest Editor + fix some php notice Fix quest_core for mission 33,36, train 2 units, etc (NEW TRIBE) There are some temporary bugs that will be fixed in next commit (pagination for new heatmap, quest reward)
555 lines
23 KiB
PHP
555 lines
23 KiB
PHP
<?php
|
|
|
|
#################################################################################
|
|
## -= YOU MAY NOT REMOVE OR CHANGE THIS NOTICE =- ##
|
|
## --------------------------------------------------------------------------- ##
|
|
## Filename : HeroAuction.php ##
|
|
## Type : T4 Hero port - auction house backend (Phase 4) ##
|
|
## --------------------------------------------------------------------------- ##
|
|
## Developed by : Shadow ##
|
|
## Project : TravianZ ##
|
|
## GitHub : https://github.com/Shadowss/TravianZ ##
|
|
## --------------------------------------------------------------------------- ##
|
|
## License : TravianZ Project ##
|
|
## Copyright : TravianZ (c) 2010-2026. All rights reserved. ##
|
|
## --------------------------------------------------------------------------- ##
|
|
#################################################################################
|
|
## ##
|
|
## T4-style auction house with PROXY BIDDING (eBay model): ##
|
|
## - a bid is a secret MAXIMUM (bid_max); the visible price ##
|
|
## (silver_current) only rises to (loser's max + 1), capped at the ##
|
|
## winner's max. ##
|
|
## ##
|
|
## SILVER ESCROW MODEL (documented invariant): ##
|
|
## - when a bidder becomes top bidder, their FULL bid_max is deducted; ##
|
|
## - when outbid, their full bid_max is refunded immediately; ##
|
|
## - at finalization the winner is refunded (bid_max - silver_current), ##
|
|
## so they only ever pay the visible price. ##
|
|
## => at any moment: sum of all player silver + escrowed bid_max of top ##
|
|
## bidders is constant (minus the seller fee at finalization). ##
|
|
## ##
|
|
## SELLER FEE: player sellers receive silver_current minus AUCTION_FEE_PCT ##
|
|
## (10%, T4 convention). NPC-seller (seller = 0) proceeds vanish (silver ##
|
|
## sink). Unsold player items are returned to the seller's inventory. ##
|
|
## ##
|
|
## No cancellation once listed (T4 convention) - flagged, not implemented. ##
|
|
## ##
|
|
#################################################################################
|
|
|
|
class HeroAuction
|
|
{
|
|
/** Seller fee percent taken from the hammer price (player sellers only). */
|
|
const AUCTION_FEE_PCT = 10;
|
|
|
|
/** Minimum bid increment over the visible price. */
|
|
const MIN_INCREMENT = 1;
|
|
|
|
/** Allowed listing durations, seconds (4h / 8h / 24h like T4). */
|
|
public static $allowedDurations = array(14400, 28800, 86400);
|
|
|
|
/** How many NPC auctions to keep open at once. */
|
|
const NPC_OPEN_TARGET = 10;
|
|
|
|
/** NPC listing duration: 8 hours. */
|
|
const NPC_DURATION = 28800;
|
|
|
|
/** status values (mirror the auction table docs from Phase 1) */
|
|
const ST_OPEN = 0;
|
|
const ST_SOLD = 1; // reserved intermediate state; finalization goes straight to 2
|
|
const ST_PROCESSED = 2;
|
|
const ST_EXPIRED = 3;
|
|
|
|
/** result codes for placeBid() / createAuction() */
|
|
const BID_OK = 1;
|
|
const BID_OUTBID = 2; // accepted, but instantly below the holder's secret max
|
|
const BID_INVALID = 0; // bad auction/own auction/too low
|
|
const BID_NO_SILVER = -1;
|
|
|
|
/** @var mysqli */
|
|
private $db;
|
|
|
|
public function __construct()
|
|
{
|
|
global $database;
|
|
$this->db = $database->dblink;
|
|
|
|
global $heroItemCatalog;
|
|
if (!isset($heroItemCatalog)) {
|
|
include_once __DIR__ . '/Data/hero_items.php';
|
|
}
|
|
}
|
|
|
|
/* =========================================================================
|
|
* READS
|
|
* ===================================================================== */
|
|
|
|
/**
|
|
* All open, unexpired auctions, ending soonest first.
|
|
* When $viewerUid is given, right-hand WEAPONS of other tribes are
|
|
* hidden (T4 behavior: a Roman only sees Roman weapon lots). Universal
|
|
* gear and consumables are always shown. The SQL over-fetches slightly
|
|
* so the filter still fills the page.
|
|
*/
|
|
public function getOpenAuctions($limit = 50, $viewerUid = 0)
|
|
{
|
|
global $database;
|
|
$now = time(); $limit = max(1, (int) $limit);
|
|
|
|
$viewerTribe = ((int) $viewerUid > 0)
|
|
? (int) $database->getUserField((int) $viewerUid, 'tribe', 0) : 0;
|
|
$fetch = $viewerTribe > 0 ? $limit * 2 : $limit;
|
|
|
|
$stmt = $this->db->prepare(
|
|
"SELECT id, seller, itemid, slot, stat_value, quantity,
|
|
silver_start, silver_current, bidder, created, time_end, status
|
|
FROM " . TB_PREFIX . "auction
|
|
WHERE status = " . self::ST_OPEN . " AND time_end > ?
|
|
ORDER BY time_end ASC LIMIT ?"
|
|
);
|
|
$stmt->bind_param('ii', $now, $fetch);
|
|
$stmt->execute();
|
|
$res = $stmt->get_result();
|
|
$out = array();
|
|
while ($row = $res->fetch_assoc()) {
|
|
if ($viewerTribe > 0) {
|
|
$itemTribe = heroItemTribe($row['itemid']);
|
|
if ($itemTribe !== 0 && $itemTribe !== $viewerTribe) {
|
|
continue; // foreign-tribe weapon lot
|
|
}
|
|
}
|
|
$row['name'] = heroItemName($row['itemid']);
|
|
// bid_max intentionally NOT selected - it's the bidder's secret.
|
|
$out[] = $row;
|
|
if (count($out) >= $limit) {
|
|
break;
|
|
}
|
|
}
|
|
$stmt->close();
|
|
return $out;
|
|
}
|
|
|
|
/** Auctions where $uid is the current top bidder. */
|
|
public function getMyBids($uid)
|
|
{
|
|
$uid = (int) $uid; $now = time();
|
|
$stmt = $this->db->prepare(
|
|
"SELECT id, seller, itemid, quantity, silver_current, bid_max, time_end
|
|
FROM " . TB_PREFIX . "auction
|
|
WHERE bidder = ? AND status = " . self::ST_OPEN . " AND time_end > ?
|
|
ORDER BY time_end ASC"
|
|
);
|
|
$stmt->bind_param('ii', $uid, $now);
|
|
$stmt->execute();
|
|
$res = $stmt->get_result();
|
|
$out = array();
|
|
while ($row = $res->fetch_assoc()) {
|
|
$row['name'] = heroItemName($row['itemid']);
|
|
$out[] = $row;
|
|
}
|
|
$stmt->close();
|
|
return $out;
|
|
}
|
|
|
|
/** Open auctions listed by $uid. */
|
|
public function getMySales($uid)
|
|
{
|
|
$uid = (int) $uid;
|
|
$stmt = $this->db->prepare(
|
|
"SELECT id, itemid, quantity, silver_start, silver_current, bidder, time_end, status
|
|
FROM " . TB_PREFIX . "auction
|
|
WHERE seller = ? AND status = " . self::ST_OPEN . "
|
|
ORDER BY time_end ASC"
|
|
);
|
|
$stmt->bind_param('i', $uid);
|
|
$stmt->execute();
|
|
$res = $stmt->get_result();
|
|
$out = array();
|
|
while ($row = $res->fetch_assoc()) {
|
|
$row['name'] = heroItemName($row['itemid']);
|
|
$out[] = $row;
|
|
}
|
|
$stmt->close();
|
|
return $out;
|
|
}
|
|
|
|
/* =========================================================================
|
|
* LISTING
|
|
* ===================================================================== */
|
|
|
|
/**
|
|
* List an owned, UNEQUIPPED item (or part of a consumable stack).
|
|
* The item leaves the inventory immediately (escrowed in the auction row).
|
|
* Returns the new auction id, or 0 on failure.
|
|
*/
|
|
public function createAuction($uid, $inventoryRowId, $quantity, $startPrice, $duration)
|
|
{
|
|
$uid = (int) $uid; $inventoryRowId = (int) $inventoryRowId;
|
|
$quantity = max(1, (int) $quantity);
|
|
$startPrice = max(1, (int) $startPrice);
|
|
$duration = (int) $duration;
|
|
|
|
if (!in_array($duration, self::$allowedDurations, true)) {
|
|
return 0;
|
|
}
|
|
|
|
$heroItems = new HeroItems();
|
|
$row = null;
|
|
foreach ($heroItems->getInventory($uid) as $r) {
|
|
if ((int) $r['id'] === $inventoryRowId) { $row = $r; break; }
|
|
}
|
|
// Must exist, be catalog-known, unequipped, and cover the quantity.
|
|
if (!$row || $row['orphan'] || (int) $row['equipped'] === 1 || (int) $row['quantity'] < $quantity) {
|
|
return 0;
|
|
}
|
|
// Gear always lists as a single piece.
|
|
if ((int) $row['def']['slot'] !== HSLOT_BAG) {
|
|
$quantity = 1;
|
|
}
|
|
|
|
// Take the item out of the inventory first (race-safe decrement inside).
|
|
if (!$heroItems->removeItem($uid, $inventoryRowId, $quantity)) {
|
|
return 0;
|
|
}
|
|
|
|
$now = time(); $end = $now + $duration;
|
|
$itemid = (int) $row['itemid']; $slot = (int) $row['def']['slot'];
|
|
$statValue = (int) $row['stat_value'];
|
|
|
|
$stmt = $this->db->prepare(
|
|
"INSERT INTO " . TB_PREFIX . "auction
|
|
(seller, itemid, slot, stat_value, quantity,
|
|
silver_start, silver_current, bidder, bid_max, created, time_end, status)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, 0, 0, ?, ?, " . self::ST_OPEN . ")"
|
|
);
|
|
$stmt->bind_param('iiiiiiiii', $uid, $itemid, $slot, $statValue, $quantity,
|
|
$startPrice, $startPrice, $now, $end);
|
|
$stmt->execute();
|
|
$auctionId = (int) $stmt->insert_id;
|
|
$stmt->close();
|
|
|
|
return $auctionId;
|
|
}
|
|
|
|
/* =========================================================================
|
|
* BIDDING (proxy model)
|
|
* ===================================================================== */
|
|
|
|
/**
|
|
* Place a maximum bid. Returns a BID_* code.
|
|
*
|
|
* Escrow choreography (all conditional statements, race-safe):
|
|
* 1. take $maxBid silver from the bidder (refused if balance is short);
|
|
* 2. try to become top bidder atomically
|
|
* (WHERE bid_max < $maxBid guards the proxy comparison);
|
|
* 3a. success -> refund the PREVIOUS top bidder's full escrow;
|
|
* 3b. failure -> we lost against the holder's secret max: bump the
|
|
* visible price to min(holderMax, ourMax + 1) and refund ourselves.
|
|
*/
|
|
public function placeBid($uid, $auctionId, $maxBid)
|
|
{
|
|
$uid = (int) $uid; $auctionId = (int) $auctionId; $maxBid = (int) $maxBid;
|
|
$now = time();
|
|
|
|
// Snapshot for validation (authoritative checks are in the UPDATEs).
|
|
// bid_max is only used for the self-raise delta below - it is the
|
|
// caller's OWN secret in that case, never exposed to rivals.
|
|
$stmt = $this->db->prepare(
|
|
"SELECT seller, silver_current, bidder, bid_max FROM " . TB_PREFIX . "auction
|
|
WHERE id = ? AND status = " . self::ST_OPEN . " AND time_end > ? LIMIT 1"
|
|
);
|
|
$stmt->bind_param('ii', $auctionId, $now);
|
|
$stmt->execute();
|
|
$res = $stmt->get_result();
|
|
$auction = $res->fetch_assoc();
|
|
$stmt->close();
|
|
|
|
if (!$auction || (int) $auction['seller'] === $uid) {
|
|
return self::BID_INVALID;
|
|
}
|
|
// A fresh auction (no bidder) can be won AT the start price;
|
|
// an active one needs current + increment.
|
|
$minBid = (int) $auction['silver_current']
|
|
+ ((int) $auction['bidder'] > 0 ? self::MIN_INCREMENT : 0);
|
|
if ($maxBid < $minBid) {
|
|
return self::BID_INVALID;
|
|
}
|
|
// Self-raise: the old escrow (old bid_max) is already held, so only
|
|
// the difference is taken; a raise below the current max is pointless.
|
|
$selfRaise = ((int) $auction['bidder'] === $uid);
|
|
if ($selfRaise && $maxBid <= (int) $auction['bid_max']) {
|
|
return self::BID_INVALID;
|
|
}
|
|
$escrow = $selfRaise ? $maxBid - (int) $auction['bid_max'] : $maxBid;
|
|
|
|
$heroItems = new HeroItems();
|
|
|
|
// 1. escrow (conditional - fails on insufficient silver).
|
|
if (!$heroItems->spendSilver($uid, $escrow)) {
|
|
return self::BID_NO_SILVER;
|
|
}
|
|
|
|
// 2. capture the current holder as the refund target, then claim.
|
|
// Correctness anchor is the conditional UPDATE below - this read
|
|
// only records who to refund if we displace them.
|
|
$stmt = $this->db->prepare(
|
|
"SELECT bidder, bid_max FROM " . TB_PREFIX . "auction WHERE id = ? LIMIT 1"
|
|
);
|
|
$stmt->bind_param('i', $auctionId);
|
|
$stmt->execute();
|
|
$res = $stmt->get_result();
|
|
$prev = $res->fetch_assoc();
|
|
$stmt->close();
|
|
|
|
if ($selfRaise) {
|
|
// Deepening our own secret max never moves the visible price and
|
|
// never refunds anyone - the old escrow stays held, only the
|
|
// delta was added above.
|
|
$stmt = $this->db->prepare(
|
|
"UPDATE " . TB_PREFIX . "auction
|
|
SET bid_max = ?
|
|
WHERE id = ? AND status = " . self::ST_OPEN . " AND time_end > ?
|
|
AND bidder = ? AND bid_max < ?"
|
|
);
|
|
$stmt->bind_param('iiiii', $maxBid, $auctionId, $now, $uid, $maxBid);
|
|
$stmt->execute();
|
|
$won = $stmt->affected_rows > 0;
|
|
$stmt->close();
|
|
|
|
if ($won) {
|
|
return self::BID_OK;
|
|
}
|
|
// Race lost: a rival displaced us between snapshot and update, so
|
|
// their winning path already refunded our OLD escrow - the delta
|
|
// is all we still hold. Return it.
|
|
$heroItems->addSilver($uid, $escrow);
|
|
return self::BID_OUTBID;
|
|
}
|
|
|
|
$stmt = $this->db->prepare(
|
|
"UPDATE " . TB_PREFIX . "auction
|
|
SET silver_current = LEAST(?, GREATEST(silver_current, bid_max + " . self::MIN_INCREMENT . ")),
|
|
bidder = ?, bid_max = ?
|
|
WHERE id = ? AND status = " . self::ST_OPEN . " AND time_end > ?
|
|
AND bid_max < ? AND seller <> ? AND bidder <> ?"
|
|
);
|
|
$stmt->bind_param('iiiiiiii', $maxBid, $uid, $maxBid, $auctionId, $now, $maxBid, $uid, $uid);
|
|
$stmt->execute();
|
|
$won = $stmt->affected_rows > 0;
|
|
$stmt->close();
|
|
|
|
if ($won) {
|
|
// 3a. refund the displaced bidder's full escrow.
|
|
if ($prev && (int) $prev['bidder'] > 0 && (int) $prev['bidder'] !== $uid && (int) $prev['bid_max'] > 0) {
|
|
$heroItems->addSilver((int) $prev['bidder'], (int) $prev['bid_max']);
|
|
}
|
|
return self::BID_OK;
|
|
}
|
|
|
|
// 3b. lost against the holder's secret max (or the auction closed in
|
|
// the meantime): bump the visible price if still open, refund us.
|
|
$stmt = $this->db->prepare(
|
|
"UPDATE " . TB_PREFIX . "auction
|
|
SET silver_current = LEAST(bid_max, ? + " . self::MIN_INCREMENT . ")
|
|
WHERE id = ? AND status = " . self::ST_OPEN . " AND time_end > ? AND bid_max >= ?"
|
|
);
|
|
$stmt->bind_param('iiii', $maxBid, $auctionId, $now, $maxBid);
|
|
$stmt->execute();
|
|
$stmt->close();
|
|
|
|
$heroItems->addSilver($uid, $escrow);
|
|
return self::BID_OUTBID;
|
|
}
|
|
|
|
/* =========================================================================
|
|
* AUTOMATION PROCESSORS
|
|
* ===================================================================== */
|
|
|
|
/**
|
|
* Finalize ended auctions:
|
|
* - with a bidder: award the item, refund (bid_max - silver_current) to
|
|
* the winner, pay the player seller (minus fee), file reports;
|
|
* - without: return the item to a player seller, mark expired.
|
|
* NPC (seller 0) items simply vanish when unsold; NPC proceeds vanish too.
|
|
*/
|
|
public function processFinished()
|
|
{
|
|
global $database;
|
|
$now = time();
|
|
|
|
$q = "SELECT id, seller, itemid, stat_value, quantity,
|
|
silver_current, bidder, bid_max
|
|
FROM " . TB_PREFIX . "auction
|
|
WHERE status = " . self::ST_OPEN . " AND time_end <= $now";
|
|
$rows = $database->query_return($q);
|
|
if (!$rows || !count($rows)) {
|
|
return 0;
|
|
}
|
|
|
|
$heroItems = new HeroItems();
|
|
$processed = 0;
|
|
|
|
foreach ($rows as $a) {
|
|
// Claim the row (same race-safe pattern as movement claiming).
|
|
$stmt = $this->db->prepare(
|
|
"UPDATE " . TB_PREFIX . "auction SET status = " . self::ST_PROCESSED . "
|
|
WHERE id = ? AND status = " . self::ST_OPEN . " LIMIT 1"
|
|
);
|
|
$aid = (int) $a['id'];
|
|
$stmt->bind_param('i', $aid);
|
|
$stmt->execute();
|
|
$claimed = $stmt->affected_rows > 0;
|
|
$stmt->close();
|
|
if (!$claimed) {
|
|
continue;
|
|
}
|
|
|
|
$bidder = (int) $a['bidder'];
|
|
$seller = (int) $a['seller'];
|
|
$price = (int) $a['silver_current'];
|
|
|
|
if ($bidder > 0) {
|
|
/* ---- SOLD ---- */
|
|
$heroItems->addItem($bidder, (int) $a['itemid'], (int) $a['quantity'], (int) $a['stat_value']);
|
|
|
|
// Winner escrowed bid_max; they only pay the hammer price.
|
|
$refund = (int) $a['bid_max'] - $price;
|
|
if ($refund > 0) {
|
|
$heroItems->addSilver($bidder, $refund);
|
|
}
|
|
|
|
// Player seller gets the price minus the fee; NPC proceeds sink.
|
|
if ($seller > 0) {
|
|
$fee = (int) floor($price * self::AUCTION_FEE_PCT / 100);
|
|
$heroItems->addSilver($seller, $price - $fee);
|
|
$database->addNotice($seller, 0, 0, NTYPE_AUCTION_REPORT,
|
|
'Auction sold',
|
|
'role=seller&itemid=' . (int) $a['itemid'] . '&qty=' . (int) $a['quantity']
|
|
. '&price=' . $price . '&fee=' . $fee, $now);
|
|
}
|
|
|
|
$database->addNotice($bidder, 0, 0, NTYPE_AUCTION_REPORT,
|
|
'Auction won',
|
|
'role=winner&itemid=' . (int) $a['itemid'] . '&qty=' . (int) $a['quantity']
|
|
. '&price=' . $price . '&refund=' . max(0, $refund), $now);
|
|
} else {
|
|
/* ---- UNSOLD ---- */
|
|
if ($seller > 0) {
|
|
$heroItems->addItem($seller, (int) $a['itemid'], (int) $a['quantity'], (int) $a['stat_value']);
|
|
$database->addNotice($seller, 0, 0, NTYPE_AUCTION_REPORT,
|
|
'Auction expired',
|
|
'role=seller&expired=1&itemid=' . (int) $a['itemid'] . '&qty=' . (int) $a['quantity'], $now);
|
|
}
|
|
// Correct the claimed status for the unsold case.
|
|
$stmt = $this->db->prepare(
|
|
"UPDATE " . TB_PREFIX . "auction SET status = " . self::ST_EXPIRED . " WHERE id = ? LIMIT 1"
|
|
);
|
|
$stmt->bind_param('i', $aid);
|
|
$stmt->execute();
|
|
$stmt->close();
|
|
}
|
|
$processed++;
|
|
}
|
|
return $processed;
|
|
}
|
|
|
|
/**
|
|
* ADMIN-ONLY: cancel an open auction outright (Phase 7 admin panel).
|
|
* Player-facing cancellation intentionally does not exist (T4 convention);
|
|
* this is the moderation escape hatch. Refunds the top bidder's full
|
|
* escrow and returns the item to a player seller (NPC items vanish).
|
|
* Returns true when an open auction was cancelled.
|
|
*/
|
|
public function adminCancel($auctionId)
|
|
{
|
|
$auctionId = (int) $auctionId;
|
|
|
|
// Snapshot before claiming so we know who to refund.
|
|
$stmt = $this->db->prepare(
|
|
"SELECT seller, itemid, stat_value, quantity, bidder, bid_max
|
|
FROM " . TB_PREFIX . "auction
|
|
WHERE id = ? AND status = " . self::ST_OPEN . " LIMIT 1"
|
|
);
|
|
$stmt->bind_param('i', $auctionId);
|
|
$stmt->execute();
|
|
$res = $stmt->get_result();
|
|
$a = $res->fetch_assoc();
|
|
$stmt->close();
|
|
if (!$a) {
|
|
return false;
|
|
}
|
|
|
|
// Race-safe claim straight to EXPIRED so processFinished can't touch it.
|
|
$stmt = $this->db->prepare(
|
|
"UPDATE " . TB_PREFIX . "auction SET status = " . self::ST_EXPIRED . "
|
|
WHERE id = ? AND status = " . self::ST_OPEN . " LIMIT 1"
|
|
);
|
|
$stmt->bind_param('i', $auctionId);
|
|
$stmt->execute();
|
|
$claimed = $stmt->affected_rows > 0;
|
|
$stmt->close();
|
|
if (!$claimed) {
|
|
return false;
|
|
}
|
|
|
|
$heroItems = new HeroItems();
|
|
if ((int) $a['bidder'] > 0 && (int) $a['bid_max'] > 0) {
|
|
$heroItems->addSilver((int) $a['bidder'], (int) $a['bid_max']);
|
|
}
|
|
if ((int) $a['seller'] > 0) {
|
|
$heroItems->addItem((int) $a['seller'], (int) $a['itemid'], (int) $a['quantity'], (int) $a['stat_value']);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Keep ~NPC_OPEN_TARGET NPC-seller auctions open so the market always has
|
|
* stock. Random catalog items; consumables list in small stacks.
|
|
* Start price scales with tier so tier-3 gear stays expensive.
|
|
*/
|
|
public function seedNpcAuctions()
|
|
{
|
|
global $heroItemCatalog;
|
|
$now = time();
|
|
|
|
$stmt = $this->db->prepare(
|
|
"SELECT COUNT(*) FROM " . TB_PREFIX . "auction
|
|
WHERE seller = 0 AND status = " . self::ST_OPEN . " AND time_end > ?"
|
|
);
|
|
$stmt->bind_param('i', $now);
|
|
$stmt->execute();
|
|
$stmt->bind_result($open);
|
|
$stmt->fetch();
|
|
$stmt->close();
|
|
|
|
$need = self::NPC_OPEN_TARGET - (int) $open;
|
|
if ($need <= 0) {
|
|
return 0;
|
|
}
|
|
|
|
$ids = array_keys($heroItemCatalog);
|
|
$created = 0;
|
|
for ($i = 0; $i < $need; $i++) {
|
|
$itemid = (int) $ids[array_rand($ids)];
|
|
$def = $heroItemCatalog[$itemid];
|
|
$isBag = ((int) $def['slot'] === HSLOT_BAG);
|
|
$qty = $isBag ? mt_rand(3, 15) : 1;
|
|
// Start price: tier-scaled for gear, per-piece for consumables.
|
|
$start = $isBag ? $qty * mt_rand(2, 5) : (int) $def['tier'] * mt_rand(40, 80);
|
|
$end = $now + self::NPC_DURATION;
|
|
$slot = (int) $def['slot'];
|
|
|
|
$stmt = $this->db->prepare(
|
|
"INSERT INTO " . TB_PREFIX . "auction
|
|
(seller, itemid, slot, stat_value, quantity,
|
|
silver_start, silver_current, bidder, bid_max, created, time_end, status)
|
|
VALUES (0, ?, ?, 0, ?, ?, ?, 0, 0, ?, ?, " . self::ST_OPEN . ")"
|
|
);
|
|
$stmt->bind_param('iiiiiii', $itemid, $slot, $qty, $start, $start, $now, $end);
|
|
$stmt->execute();
|
|
$stmt->close();
|
|
$created++;
|
|
}
|
|
return $created;
|
|
}
|
|
}
|