Files
core/hwe/sammo/AuctionUniqueItem.php
T

270 lines
8.4 KiB
PHP

<?php
namespace sammo;
use DateTimeImmutable;
use sammo\DTO\AuctionBidItem;
use sammo\DTO\AuctionInfo;
use sammo\DTO\AuctionInfoDetail;
use sammo\Enums\AuctionType;
use sammo\Enums\InheritanceKey;
use sammo\Enums\ResourceType;
use sammo\RandUtil;
class AuctionUniqueItem extends Auction
{
static public function genObfuscatedName(int $id): string
{
$db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env');
$namePool = $gameStor->getValue('obfuscatedNamePool');
if ($namePool === null) {
$rng = new RandUtil(new LiteHashDRBG(Util::simpleSerialize(
UniqueConst::$hiddenSeed,
)));
$namePool = [];
foreach (GameConst::$randGenFirstName as $ch0) {
foreach (GameConst::$randGenMiddleName as $ch1) {
foreach (GameConst::$randGenLastName as $ch2) {
$namePool[] = "{$ch0}{$ch1}{$ch2}";
}
}
}
$namePool = $rng->shuffle($namePool);
$gameStor->setValue('obfuscatedNamePool', $namePool);
}
$dupIdx = intdiv($id, count($namePool));
$subIdx = $id % count($namePool);
if ($dupIdx == 0) {
return $namePool[$subIdx];
}
return "{$namePool[$subIdx]}{$dupIdx}";
}
static public function openItemAuction(BaseItem $item, General $general, int $startAmount): self|string
{
if ($startAmount < GameConst::$inheritItemUniqueMinPoint) {
return '최소 경매 금액은 ' . GameConst::$inheritItemUniqueMinPoint . '입니다.';
}
if ($general->getInheritancePoint(InheritanceKey::previous) < $startAmount) {
return '경매를 시작할 포인트가 부족합니다.';
}
if ($item->isBuyable()) {
return '구매할 수 있는 아이템입니다.';
}
$now = new DateTimeImmutable();
$db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env');
$turnTerm = $gameStor->getValue('turnterm');
$closeDate = $now->add(TimeUtil::secondsToDateInterval(
max(static::MIN_AUCTION_CLOSE_MINUTES, $turnTerm * static::COEFF_AUCTION_CLOSE_MINUTES) * 60
));
$availableLatestBidCloseDate = $closeDate->add(TimeUtil::secondsToDateInterval(
max(static::MIN_EXTENSION_MINUTES_LIMIT_BY_BID, $turnTerm * static::COEFF_EXTENSION_MINUTES_LIMIT_BY_BID) * 60
));
$info = new AuctionInfo(
null,
AuctionType::UniqueItem,
false,
$item->getRawClassName(),
$general->getID(),
ResourceType::inheritancePoint,
$now,
$closeDate,
new AuctionInfoDetail(
"{$item->getName()} 경매",
static::genObfuscatedName($general->getID()),
$startAmount,
null,
1,
$availableLatestBidCloseDate,
)
);
$auctionID = static::openAuction($info, $general);
if (!is_int($auctionID)) {
return $auctionID;
}
$auction = new static($auctionID, $general);
try {
$auction->bid($startAmount);
} catch (\Exception $e) {
//실패해선 안된다.
$msg = $e->getMessage();
$auction->closeAuction();
return "경매를 시작했지만, 첫 입찰에 실패했습니다: {$msg}";
}
return $auction;
}
private function giveUniqueItem(AuctionBidItem $highestBid, General $bidder): ?string
{
$itemKey = $this->info->target;
$itemObj = buildItemClass($itemKey);
$general = $bidder;
$availableItemTypes = [];
$reasons = [];
$db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env');
[$startYear, $year] = $gameStor->getValuesAsArray(['startyear', 'year']);
$relYear = $year - $startYear;
$availableEquipUniqueCnt = 1;
foreach (GameConst::$maxUniqueItemLimit as $tmpVals) {
[$targetYear, $targetTrialCnt] = $tmpVals;
if ($relYear < $targetYear) {
break;
}
$availableEquipUniqueCnt = $targetTrialCnt;
}
$availableEquipUniqueCnt = Util::valueFit($availableEquipUniqueCnt, null, count(GameConst::$allItems));
foreach ($general->getItems() as $item) {
if (!$item->isBuyable()) {
$availableEquipUniqueCnt -= 1;
}
}
if ($availableEquipUniqueCnt <= 0) {
return '유니크 아이템 소유 제한 상태입니다. 종료 시간이 연장됩니다.';
}
foreach (GameConst::$allItems as $itemType => $itemList) {
//아직은 그런 경우는 없지만 동일 유니크를 여러 부위에 장착할 수 있을지도 모름
if (!key_exists($itemKey, $itemList)) {
continue;
}
$ownItem = $general->getItem($itemType);
if ($ownItem->getRawClassName() == $itemKey) {
//FIXME: 이 경우에는 환불이 되던가 해야함.
$reasons[] = '이미 그 유니크를 가지고 있습니다.';
continue;
}
if (!$ownItem->isBuyable()) {
$reasons[] = '이미 다른 유니크를 가지고 있습니다.';
continue;
}
$availableCnt = $itemList[$itemKey];
$occupiedCnt = $db->queryFirstField('SELECT count(*) FROM general WHERE %b = %s', $itemType, $itemKey);
if ($occupiedCnt >= $availableCnt) {
//FIXME: 이 경우에는 환불이 되던가 해야함.
$reasons[] = '그 유니크는 모두 점유되었습니다.';
continue;
}
$availableItemTypes[] = $itemType;
}
if (!$availableItemTypes) {
return join(' ', $reasons);
}
$itemType = $availableItemTypes[0];
$general->setVar($itemType, $itemKey);
$logger = $general->getLogger();
$nationName = $general->getStaticNation()['name'];
$generalName = $general->getName();
$josaYi = JosaUtil::pick($generalName, '이');
$itemName = $itemObj->getName();
$itemRawName = $itemObj->getRawName();
$josaUl = JosaUtil::pick($itemRawName, '을');
$logger->pushGeneralActionLog("<C>{$itemName}</>{$josaUl} 습득했습니다!");
$logger->pushGeneralHistoryLog("<C>{$itemName}</>{$josaUl} 습득");
$logger->pushGlobalActionLog("<Y>{$generalName}</>{$josaYi} <C>{$itemName}</>{$josaUl} 습득했습니다!");
$logger->pushGlobalHistoryLog("<C><b>【보물발견】</b></><D><b>{$nationName}</b></>의 <Y>{$generalName}</>{$josaYi} <C>{$itemName}</>{$josaUl} 습득했습니다!");
$general->applyDB($db);
return null;
}
public function checkCloseDate(): ?bool
{
$now = new DateTimeImmutable();
if ($now < $this->info->closeDate) {
return null;
}
//경매를 닫아야한다.
$highestBid = $this->getHighestBid();
if ($highestBid === null) {
$this->closeAuction();
return true;
}
if ($highestBid->data->tryExtendCloseDate) {
//연장 요청이 있었다.
$extendedCloseDate = $this->info->closeDate->add(TimeUtil::secondsToDateInterval(
max(static::MIN_EXTENSION_MINUTES_BY_EXTENSION_QUERY, $this->info->turnTerm * static::COEFF_EXTENSION_MINUTES_BY_EXTENSION_QUERY) * 60
));
if ($this->extendCloseDate($extendedCloseDate) === null) {
return false;
}
}
$bidder = General::createGeneralObjFromDB($highestBid->generalID);
$failReason = $this->giveUniqueItem($highestBid, $bidder);
if ($failReason === null) {
$this->closeAuction();
return true;
}
$staticNation = $bidder->getStaticNation();
$src = new MessageTarget(0, '', 0, 'System', '#000000');
$dest = new MessageTarget(
$bidder->getID(),
$bidder->getName(),
$bidder->getNationID(),
$staticNation['name'],
$staticNation['color'],
GetImageURL($bidder->getVar('imgsvr'), $bidder->getVar('picture'))
);
//TODO: 전역 알림이 나타나야한다. 일반 메시지보다는 중요하고, 메시지보단 약하게..
//TODO: 바로가기를 제공하는 편이 좋을 것 같다.
$msg = new Message(
Message::MSGTYPE_PRIVATE,
$src,
$dest,
$failReason,
new \DateTime(),
new \DateTime('9999-12-31'),
[]
);
$msg->send(true);
//아이템 제한에 걸렸다면 자동 연장
$extendedCloseDate = $this->info->closeDate->add(TimeUtil::secondsToDateInterval(
max(static::MIN_EXTENSION_MINUTES_BY_EXTENSION_QUERY, $this->info->turnTerm * static::COEFF_EXTENSION_MINUTES_BY_EXTENSION_QUERY) * 60
));
$this->extendCloseDate($extendedCloseDate, true);
return false;
}
public function bid(int $amount, bool $tryExtendCloseDate = false): ?string
{
//TODO: 입찰 가능 제한확인(다른 경매 진행 등)
return parent::bid($amount, $tryExtendCloseDate);
}
}