feat(wip): 유니크 경매 세부 진행
This commit is contained in:
+80
-28
@@ -20,6 +20,16 @@ class Auction
|
|||||||
protected AuctionInfo $info;
|
protected AuctionInfo $info;
|
||||||
public const LAST_AUCTION_ID_KEY = 'last_auction_id';
|
public const LAST_AUCTION_ID_KEY = 'last_auction_id';
|
||||||
|
|
||||||
|
|
||||||
|
public const COEFF_AUCTION_CLOSE_MINUTES = 24;
|
||||||
|
public const COEFF_EXTENSION_MINUTES_PER_BID = 1 / 6;
|
||||||
|
public const COEFF_EXTENSION_MINUTES_LIMIT_BY_BID = 1;
|
||||||
|
public const COEFF_EXTENSION_MINUTES_BY_EXTENSION_QUERY = 1;
|
||||||
|
public const MIN_AUCTION_CLOSE_MINUTES = 30;
|
||||||
|
public const MIN_EXTENSION_MINUTES_PER_BID = 1;
|
||||||
|
public const MIN_EXTENSION_MINUTES_LIMIT_BY_BID = 5;
|
||||||
|
public const MIN_EXTENSION_MINUTES_BY_EXTENSION_QUERY = 5;
|
||||||
|
|
||||||
static public function genNextAuctionID(): int
|
static public function genNextAuctionID(): int
|
||||||
{
|
{
|
||||||
$db = DB::db();
|
$db = DB::db();
|
||||||
@@ -41,14 +51,14 @@ class Auction
|
|||||||
$auctionStor = KVStorage::getStorage($db, 'auction');
|
$auctionStor = KVStorage::getStorage($db, 'auction');
|
||||||
|
|
||||||
$openedAuction = $general->getAuxVar('openedAuction') ?? [];
|
$openedAuction = $general->getAuxVar('openedAuction') ?? [];
|
||||||
$prevAuctionID = $openedAuction[$info->reqResource->value] ?? null;
|
$prevAuctionID = $openedAuction[$info->type->value] ?? null;
|
||||||
if ($prevAuctionID !== null) {
|
if ($prevAuctionID !== null) {
|
||||||
//XXX: 이 구조보다는 차라리 DB에 insert로 넣는 게 나을 수도.
|
//XXX: 이 구조보다는 차라리 DB에 insert로 넣는 게 나을 수도.
|
||||||
$prevAuction = new Auction($prevAuctionID, $general);
|
$prevAuction = new Auction($prevAuctionID, $general);
|
||||||
if (!$prevAuction->info->finished) {
|
if (!$prevAuction->info->finished) {
|
||||||
return '아직 경매가 끝나지 않았습니다.';
|
return '아직 경매가 끝나지 않았습니다.';
|
||||||
}
|
}
|
||||||
unset($openedAuction[$info->reqResource->value]);
|
unset($openedAuction[$info->type->value]);
|
||||||
$general->setAuxVar('openedAuction', $openedAuction);
|
$general->setAuxVar('openedAuction', $openedAuction);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +72,7 @@ class Auction
|
|||||||
'SELECT * FROM ng_auction WHERE auction_id = %i ORDER BY `amount` DESC LIMIT 1',
|
'SELECT * FROM ng_auction WHERE auction_id = %i ORDER BY `amount` DESC LIMIT 1',
|
||||||
$this->info->id
|
$this->info->id
|
||||||
);
|
);
|
||||||
if(!$rawHighestBid){
|
if (!$rawHighestBid) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,31 +109,49 @@ class Auction
|
|||||||
return $this->info;
|
return $this->info;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function extendCloseDate(DateTimeInterface $date): void
|
public function extendCloseDate(DateTimeInterface $date, bool $force = false): ?string
|
||||||
{
|
{
|
||||||
|
if(!$force){
|
||||||
|
if ($this->info->remainCloseExtensionCnt === null) {
|
||||||
|
return '연장할 수 없는 경매입니다.';
|
||||||
|
}
|
||||||
|
if ($this->info->remainCloseExtensionCnt === 0) {
|
||||||
|
return '더 이상 연장할 수 없습니다';
|
||||||
|
}
|
||||||
|
if ($this->info->remainCloseExtensionCnt > 0) {
|
||||||
|
$this->info->remainCloseExtensionCnt--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$db = DB::db();
|
$db = DB::db();
|
||||||
$auctionStor = KVStorage::getStorage($db, 'auction');
|
$auctionStor = KVStorage::getStorage($db, 'auction');
|
||||||
$this->info->closeDate = DateTimeImmutable::createFromInterface($date);
|
$gameStor = KVStorage::getStorage($db, 'game_env');
|
||||||
|
$turnTerm = $gameStor->getValue('turnterm');
|
||||||
|
$closeDate = DateTimeImmutable::createFromInterface($date);
|
||||||
|
$this->info->closeDate = $closeDate;
|
||||||
|
$this->info->availableLatestBidCloseDate = $closeDate->add(TimeUtil::secondsToDateInterval(
|
||||||
|
max(static::MIN_EXTENSION_MINUTES_PER_BID, $turnTerm * static::COEFF_EXTENSION_MINUTES_PER_BID) * 60
|
||||||
|
));
|
||||||
$auctionStor->setValue("id_{$this->info->id}", $this->info->toArray());
|
$auctionStor->setValue("id_{$this->info->id}", $this->info->toArray());
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function refundBid(AuctionBidItem $bidItem, string $reason): void
|
public function refundBid(AuctionBidItem $bidItem, string $reason): void
|
||||||
{
|
{
|
||||||
if($bidItem->auctionID !== $this->info->id){
|
if ($bidItem->auctionID !== $this->info->id) {
|
||||||
throw new \RuntimeException('잘못된 경매입니다.');
|
throw new \RuntimeException('잘못된 경매입니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$db = DB::db();
|
$db = DB::db();
|
||||||
if($bidItem->generalID === $this->general->generalID) {
|
if ($bidItem->generalID === $this->general->generalID) {
|
||||||
$oldBidder = $this->general;
|
$oldBidder = $this->general;
|
||||||
} else {
|
} else {
|
||||||
$oldBidder = General::createGeneralObjFromDB($bidItem->general_id);
|
$oldBidder = General::createGeneralObjFromDB($bidItem->generalID);
|
||||||
}
|
}
|
||||||
|
|
||||||
if($this->info->reqResource === ResourceType::inheritancePoint){
|
if ($this->info->reqResource === ResourceType::inheritancePoint) {
|
||||||
$oldBidder->increaseInheritancePoint(InheritanceKey::previous, $bidItem->amount);
|
$oldBidder->increaseInheritancePoint(InheritanceKey::previous, $bidItem->amount);
|
||||||
}
|
} else {
|
||||||
else{
|
|
||||||
$oldBidder->increaseVar($this->info->reqResource->value, $bidItem->amount);
|
$oldBidder->increaseVar($this->info->reqResource->value, $bidItem->amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,22 +189,21 @@ class Auction
|
|||||||
$this->info->finished = true;
|
$this->info->finished = true;
|
||||||
|
|
||||||
$openedAuction = $this->general->getAuxVar('openedAuction') ?? [];
|
$openedAuction = $this->general->getAuxVar('openedAuction') ?? [];
|
||||||
if (key_exists($this->info->reqResource->value, $openedAuction)) {
|
if (key_exists($this->info->type->value, $openedAuction)) {
|
||||||
unset($openedAuction[$this->info->reqResource->value]);
|
unset($openedAuction[$this->info->type->value]);
|
||||||
$this->general->setAuxVar('openedAuction', $openedAuction);
|
$this->general->setAuxVar('openedAuction', $openedAuction);
|
||||||
}
|
}
|
||||||
if ($isRollback) {
|
if ($isRollback) {
|
||||||
$highestBid = $this->getHighestBid();
|
$highestBid = $this->getHighestBid();
|
||||||
if($highestBid !== null){
|
if ($highestBid !== null) {
|
||||||
$this->refundBid($highestBid, "{$this->info->title} 경매가 취소되었습니다.");
|
$this->refundBid($highestBid, "{$this->info->title} 경매가 취소되었습니다.");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
$auctionID = $this->info->id;
|
$auctionID = $this->info->id;
|
||||||
$auctionStor->setValue("id_{$auctionID}", $this->info->toArray());
|
$auctionStor->setValue("id_{$auctionID}", $this->info->toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
private function bidInheritPoint(int $amount, \DateTimeImmutable $now): void
|
private function bidInheritPoint(int $amount, \DateTimeImmutable $now, bool $tryExtendCloseDate): ?string
|
||||||
{
|
{
|
||||||
$db = DB::db();
|
$db = DB::db();
|
||||||
|
|
||||||
@@ -185,7 +212,7 @@ class Auction
|
|||||||
|
|
||||||
$highestBid = $this->getHighestBid();
|
$highestBid = $this->getHighestBid();
|
||||||
if ($highestBid !== null && $amount <= $highestBid->amount) {
|
if ($highestBid !== null && $amount <= $highestBid->amount) {
|
||||||
throw new \RuntimeException('현재입찰가보다 높게 입찰해야 합니다.');
|
return '현재입찰가보다 높게 입찰해야 합니다.';
|
||||||
}
|
}
|
||||||
|
|
||||||
$rawMyPrevBid = $db->queryFirstRow(
|
$rawMyPrevBid = $db->queryFirstRow(
|
||||||
@@ -202,7 +229,7 @@ class Auction
|
|||||||
$morePoint = $amount - ($myPrevBid ? $myPrevBid->amount : 0);
|
$morePoint = $amount - ($myPrevBid ? $myPrevBid->amount : 0);
|
||||||
$currPoint = $general->getInheritancePoint(InheritanceKey::previous);
|
$currPoint = $general->getInheritancePoint(InheritanceKey::previous);
|
||||||
if ($currPoint === null || $currPoint < $morePoint) {
|
if ($currPoint === null || $currPoint < $morePoint) {
|
||||||
throw new \RuntimeException('유산포인트가 부족합니다.');
|
return '유산포인트가 부족합니다.';
|
||||||
}
|
}
|
||||||
|
|
||||||
//여기서부터 입찰 성공
|
//여기서부터 입찰 성공
|
||||||
@@ -217,9 +244,26 @@ class Auction
|
|||||||
new AuctionBidItemData(
|
new AuctionBidItemData(
|
||||||
$general->getVar('owner_name'),
|
$general->getVar('owner_name'),
|
||||||
$general->getName(),
|
$general->getName(),
|
||||||
|
$tryExtendCloseDate,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
$db->insert('ng_auction', $newBid->toArray());
|
$db->insert('ng_auction', $newBid->toArray());
|
||||||
|
if ($db->affectedRows() == 0) {
|
||||||
|
return '입찰에 실패했습니다: DB 오류';
|
||||||
|
}
|
||||||
|
|
||||||
|
$gameStor = KVStorage::getStorage($db, 'game_env');
|
||||||
|
$turnTerm = $gameStor->getValue('turnterm');
|
||||||
|
|
||||||
|
if ($this->info->availableLatestBidCloseDate !== null) {
|
||||||
|
$extendedCloseDate = $this->info->closeDate->add(TimeUtil::secondsToDateInterval(
|
||||||
|
max(static::MIN_EXTENSION_MINUTES_PER_BID, $turnTerm * static::COEFF_EXTENSION_MINUTES_PER_BID) * 60
|
||||||
|
));
|
||||||
|
|
||||||
|
if ($extendedCloseDate > $this->info->closeDate && $this->info->closeDate < $this->info->availableLatestBidCloseDate) {
|
||||||
|
$this->extendCloseDate(min($extendedCloseDate, $this->info->availableLatestBidCloseDate));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$general->increaseInheritancePoint(InheritanceKey::previous, -$morePoint);
|
$general->increaseInheritancePoint(InheritanceKey::previous, -$morePoint);
|
||||||
$general->increaseRankVar(RankColumn::inherit_point_spent_dynamic, $morePoint);
|
$general->increaseRankVar(RankColumn::inherit_point_spent_dynamic, $morePoint);
|
||||||
@@ -228,33 +272,33 @@ class Auction
|
|||||||
$this->refundBid($highestBid, "{$auctionInfo->title} 경매에 상회입찰자가 나타났습니다.");
|
$this->refundBid($highestBid, "{$auctionInfo->title} 경매에 상회입찰자가 나타났습니다.");
|
||||||
}
|
}
|
||||||
$general->applyDB($db);
|
$general->applyDB($db);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function bid(int $amount): void
|
public function bid(int $amount, bool $tryExtendCloseDate = false): ?string
|
||||||
{
|
{
|
||||||
$auctionInfo = $this->info;
|
$auctionInfo = $this->info;
|
||||||
$general = $this->general;
|
$general = $this->general;
|
||||||
|
|
||||||
if ($auctionInfo->finished) {
|
if ($auctionInfo->finished) {
|
||||||
throw new \RuntimeException('경매가 이미 끝났습니다.');
|
return '경매가 이미 끝났습니다.';
|
||||||
}
|
}
|
||||||
|
|
||||||
$now = new \DateTimeImmutable();
|
$now = new \DateTimeImmutable();
|
||||||
|
|
||||||
if ($auctionInfo->closeDate < $now) {
|
if ($auctionInfo->closeDate < $now) {
|
||||||
throw new \RuntimeException('경매가 이미 끝났습니다.');
|
return '경매가 이미 끝났습니다.';
|
||||||
}
|
}
|
||||||
if ($auctionInfo->closeDate > $now) {
|
if ($auctionInfo->openDate > $now) {
|
||||||
throw new \RuntimeException('경매가 아직 시작되지 않았습니다.');
|
return '경매가 아직 시작되지 않았습니다.';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($auctionInfo->buyImmediatelyAmount !== null && $auctionInfo->buyImmediatelyAmount < $amount) {
|
if ($auctionInfo->buyImmediatelyAmount !== null && $auctionInfo->buyImmediatelyAmount < $amount) {
|
||||||
throw new \RuntimeException('즉시판매가보다 높을 수 없습니다.');
|
return '즉시판매가보다 높을 수 없습니다.';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($auctionInfo->reqResource === ResourceType::inheritancePoint) {
|
if ($auctionInfo->reqResource === ResourceType::inheritancePoint) {
|
||||||
$this->bidInheritPoint($amount, $now);
|
return $this->bidInheritPoint($amount, $now, $tryExtendCloseDate);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//reqResource는 말 그대로 '구매자가 내야하는 자원'이다.
|
//reqResource는 말 그대로 '구매자가 내야하는 자원'이다.
|
||||||
@@ -263,7 +307,7 @@ class Auction
|
|||||||
|
|
||||||
$highestBid = $this->getHighestBid();
|
$highestBid = $this->getHighestBid();
|
||||||
if ($highestBid !== null && $amount <= $highestBid->amount) {
|
if ($highestBid !== null && $amount <= $highestBid->amount) {
|
||||||
throw new \RuntimeException('현재입찰가보다 높게 입찰해야 합니다.');
|
return '현재입찰가보다 높게 입찰해야 합니다.';
|
||||||
}
|
}
|
||||||
|
|
||||||
$myPrevBid = $this->getMyPrevBid();
|
$myPrevBid = $this->getMyPrevBid();
|
||||||
@@ -276,7 +320,7 @@ class Auction
|
|||||||
};
|
};
|
||||||
|
|
||||||
if ($general->getVar($resType->value) < $morePoint + $minReqRes) {
|
if ($general->getVar($resType->value) < $morePoint + $minReqRes) {
|
||||||
throw new \RuntimeException($resType->getName() . '이 부족합니다.');
|
return $resType->getName() . '이 부족합니다.';
|
||||||
}
|
}
|
||||||
|
|
||||||
//여기서부터 입찰 성공
|
//여기서부터 입찰 성공
|
||||||
@@ -291,9 +335,16 @@ class Auction
|
|||||||
new AuctionBidItemData(
|
new AuctionBidItemData(
|
||||||
$general->getVar('owner_name'),
|
$general->getVar('owner_name'),
|
||||||
$general->getName(),
|
$general->getName(),
|
||||||
|
$tryExtendCloseDate,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
$db->insert('ng_auction', $newBid->toArray());
|
$db->insert('ng_auction', $newBid->toArray());
|
||||||
|
if ($db->affectedRows() == 0) {
|
||||||
|
return '입찰에 실패했습니다: DB 오류';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$general->setVar($resType->value, $general->getVar($resType->value) - $morePoint);
|
$general->setVar($resType->value, $general->getVar($resType->value) - $morePoint);
|
||||||
|
|
||||||
@@ -301,5 +352,6 @@ class Auction
|
|||||||
$this->refundBid($highestBid, "{$auctionInfo->title} 경매에 상회입찰자가 나타났습니다.");
|
$this->refundBid($highestBid, "{$auctionInfo->title} 경매에 상회입찰자가 나타났습니다.");
|
||||||
}
|
}
|
||||||
$general->applyDB($db);
|
$general->applyDB($db);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+185
-12
@@ -3,7 +3,9 @@
|
|||||||
namespace sammo;
|
namespace sammo;
|
||||||
|
|
||||||
use DateTimeImmutable;
|
use DateTimeImmutable;
|
||||||
|
use sammo\DTO\AuctionBidItem;
|
||||||
use sammo\DTO\AuctionInfo;
|
use sammo\DTO\AuctionInfo;
|
||||||
|
use sammo\Enums\AuctionType;
|
||||||
use sammo\Enums\InheritanceKey;
|
use sammo\Enums\InheritanceKey;
|
||||||
use sammo\Enums\ResourceType;
|
use sammo\Enums\ResourceType;
|
||||||
use sammo\RandUtil;
|
use sammo\RandUtil;
|
||||||
@@ -17,14 +19,14 @@ class AuctionUniqueItem extends Auction
|
|||||||
$gameStor = KVStorage::getStorage($db, 'game_env');
|
$gameStor = KVStorage::getStorage($db, 'game_env');
|
||||||
|
|
||||||
$namePool = $gameStor->getValue('obfuscatedNamePool');
|
$namePool = $gameStor->getValue('obfuscatedNamePool');
|
||||||
if($namePool === null){
|
if ($namePool === null) {
|
||||||
$rng = new RandUtil(new LiteHashDRBG(Util::simpleSerialize(
|
$rng = new RandUtil(new LiteHashDRBG(Util::simpleSerialize(
|
||||||
UniqueConst::$hiddenSeed,
|
UniqueConst::$hiddenSeed,
|
||||||
)));
|
)));
|
||||||
$namePool = [];
|
$namePool = [];
|
||||||
foreach(GameConst::$randGenFirstName as $ch0){
|
foreach (GameConst::$randGenFirstName as $ch0) {
|
||||||
foreach(GameConst::$randGenMiddleName as $ch1){
|
foreach (GameConst::$randGenMiddleName as $ch1) {
|
||||||
foreach(GameConst::$randGenLastName as $ch2){
|
foreach (GameConst::$randGenLastName as $ch2) {
|
||||||
$namePool[] = "{$ch0}{$ch1}{$ch2}";
|
$namePool[] = "{$ch0}{$ch1}{$ch2}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,42 +50,55 @@ class AuctionUniqueItem extends Auction
|
|||||||
return '최소 경매 금액은 ' . GameConst::$inheritItemUniqueMinPoint . '입니다.';
|
return '최소 경매 금액은 ' . GameConst::$inheritItemUniqueMinPoint . '입니다.';
|
||||||
}
|
}
|
||||||
|
|
||||||
if($general->getInheritancePoint(InheritanceKey::previous) < $startAmount){
|
if ($general->getInheritancePoint(InheritanceKey::previous) < $startAmount) {
|
||||||
return '경매를 시작할 포인트가 부족합니다.';
|
return '경매를 시작할 포인트가 부족합니다.';
|
||||||
}
|
}
|
||||||
|
|
||||||
$auctionID = static::genNextAuctionID();
|
if ($item->isBuyable()) {
|
||||||
|
return '구매할 수 있는 아이템입니다.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GameConst::$allItems)
|
||||||
|
|
||||||
|
$auctionID = static::genNextAuctionID();
|
||||||
$now = new DateTimeImmutable();
|
$now = new DateTimeImmutable();
|
||||||
|
|
||||||
$db = DB::db();
|
$db = DB::db();
|
||||||
$gameStor = KVStorage::getStorage($db, 'game_env');
|
$gameStor = KVStorage::getStorage($db, 'game_env');
|
||||||
$turnTerm = $gameStor->getValue('turnterm');
|
$turnTerm = $gameStor->getValue('turnterm');
|
||||||
|
|
||||||
$closeDate = $now->add(TimeUtil::secondsToDateInterval(24 * 60 * $turnTerm * 60));
|
$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(
|
$info = new AuctionInfo(
|
||||||
$auctionID,
|
$auctionID,
|
||||||
static::genObfuscatedName($general->getID()),
|
static::genObfuscatedName($general->getID()),
|
||||||
$general->getID(),
|
$general->getID(),
|
||||||
|
AuctionType::UniqueItem,
|
||||||
$item->getRawClassName(),
|
$item->getRawClassName(),
|
||||||
"{$item->getName()} 경매",
|
"{$item->getName()} 경매",
|
||||||
false,
|
false,
|
||||||
ResourceType::inheritancePoint,
|
ResourceType::inheritancePoint,
|
||||||
$startAmount,
|
$startAmount,
|
||||||
null,
|
null,
|
||||||
|
1,
|
||||||
$now,
|
$now,
|
||||||
$closeDate
|
$closeDate,
|
||||||
|
$availableLatestBidCloseDate,
|
||||||
);
|
);
|
||||||
|
|
||||||
$result = static::openAuction($info, $general);
|
$result = static::openAuction($info, $general);
|
||||||
if($result !== null){
|
if ($result !== null) {
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
$auction = new static($auctionID, $general);
|
$auction = new static($auctionID, $general);
|
||||||
try{
|
try {
|
||||||
$auction->bid($startAmount);
|
$auction->bid($startAmount);
|
||||||
}
|
} catch (\Exception $e) {
|
||||||
catch(\Exception $e){
|
|
||||||
//실패해선 안된다.
|
//실패해선 안된다.
|
||||||
$msg = $e->getMessage();
|
$msg = $e->getMessage();
|
||||||
$auction->closeAuction();
|
$auction->closeAuction();
|
||||||
@@ -92,4 +107,162 @@ class AuctionUniqueItem extends Auction
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ class AuctionBidItemData extends DTO
|
|||||||
#[NullIsUndefined]
|
#[NullIsUndefined]
|
||||||
public ?string $ownerName,
|
public ?string $ownerName,
|
||||||
public string $generalName,
|
public string $generalName,
|
||||||
|
#[NullIsUndefined]
|
||||||
|
public ?bool $tryExtendCloseDate,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ namespace sammo\DTO;
|
|||||||
|
|
||||||
use sammo\DTO\Attr\Convert;
|
use sammo\DTO\Attr\Convert;
|
||||||
use sammo\DTO\Converter\DateTimeConverter;
|
use sammo\DTO\Converter\DateTimeConverter;
|
||||||
|
use sammo\Enums\AuctionType;
|
||||||
use sammo\Enums\ResourceType;
|
use sammo\Enums\ResourceType;
|
||||||
|
|
||||||
class AuctionInfo extends DTO
|
class AuctionInfo extends DTO
|
||||||
@@ -12,17 +13,21 @@ class AuctionInfo extends DTO
|
|||||||
public int $id,
|
public int $id,
|
||||||
public string $openerName,
|
public string $openerName,
|
||||||
public int $openerGeneralID,
|
public int $openerGeneralID,
|
||||||
public string $type,
|
public AuctionType $type,
|
||||||
|
public ?string $target,
|
||||||
public string $title,
|
public string $title,
|
||||||
public bool $finished,
|
public bool $finished,
|
||||||
public ResourceType $reqResource,
|
public ResourceType $reqResource,
|
||||||
public int $minAmount,
|
public int $minAmount,
|
||||||
public ?int $buyImmediatelyAmount,
|
public ?int $buyImmediatelyAmount,
|
||||||
|
public ?int $remainCloseDateExtensionCnt,
|
||||||
|
|
||||||
#[Convert(DateTimeConverter::class)]
|
#[Convert(DateTimeConverter::class)]
|
||||||
public \DateTimeImmutable $openDate,
|
public \DateTimeImmutable $openDate,
|
||||||
#[Convert(DateTimeConverter::class)]
|
#[Convert(DateTimeConverter::class)]
|
||||||
public \DateTimeImmutable $closeDate,
|
public \DateTimeImmutable $closeDate,
|
||||||
|
#[Convert(DateTimeConverter::class)]
|
||||||
|
public ?\DateTimeImmutable $availableLatestBidCloseDate,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
namespace sammo\Enums;
|
||||||
|
|
||||||
|
enum AuctionType: string{
|
||||||
|
case Gold = 'gold';
|
||||||
|
case Rice = 'rice';
|
||||||
|
case UniqueItem = 'uniqueItem';
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user