diff --git a/hwe/_admin2.php b/hwe/_admin2.php index 4281e2eb..913f7fac 100644 --- a/hwe/_admin2.php +++ b/hwe/_admin2.php @@ -48,54 +48,25 @@ $db = DB::db(); 접속제한
블럭회원 - + query('SELECT `no`, `name`, npc, `block` FROM general ORDER BY npc, binary(`name`)'); - echo " - - - 아이템 지급 - - - - - - diff --git a/hwe/_admin2_submit.php b/hwe/_admin2_submit.php index fb0b717f..660c204b 100644 --- a/hwe/_admin2_submit.php +++ b/hwe/_admin2_submit.php @@ -200,95 +200,6 @@ switch ($btn) { $msg->send(true); } break; - case "무기지급": - - if ($item == 'None') { - $text = "무기 회수!"; - } else { - $text = getItemName($item) . " 지급!"; - } - - foreach ($genlist as $generalID) { - $msg = new Message(Message::MSGTYPE_PRIVATE, $src, MessageTarget::buildQuick($generalID), $text, new \DateTime(), new \DateTime('9999-12-31'), []); - $msg->send(true); - } - - if ($item === 'None') { - $db->update('general', [ - 'weapon' => 'None' - ], '`no` IN %li', $genlist); - } else { - $db->update('general', [ - 'weapon' => $item - ], '`no` IN %li', $genlist, $item); - } - break; - case "책지급": - if ($item == 'None') { - $text = "책 회수!"; - } else { - $text = getItemName($item) . " 지급!"; - } - - foreach ($genlist as $generalID) { - $msg = new Message(Message::MSGTYPE_PRIVATE, $src, MessageTarget::buildQuick($generalID), $text, new \DateTime(), new \DateTime('9999-12-31'), []); - $msg->send(true); - } - - if ($item == 'None') { - $db->update('general', [ - 'book' => 'None' - ], '`no` IN %li', $genlist); - } else { - $db->update('general', [ - 'book' => $item - ], '`no` IN %li', $genlist); - } - break; - case "말지급": - if ($item == 'None') { - $text = "말 회수!"; - } else { - $text = getItemName($item) . " 지급!"; - } - - foreach ($genlist as $generalID) { - $msg = new Message(Message::MSGTYPE_PRIVATE, $src, MessageTarget::buildQuick($generalID), $text, new \DateTime(), new \DateTime('9999-12-31'), []); - $msg->send(true); - } - - if ($item == 'None') { - $db->update('general', [ - 'horse' => 'None' - ], '`no` IN %li', $genlist); - } else { - $db->update('general', [ - 'horse' => $item - ], '`no` IN %li', $genlist); - } - break; - case "도구지급": - if ($item == 'None') { - $text = "특수도구 회수!"; - } else { - $text = getItemName($item) . " 지급!"; - } - - foreach ($genlist as $generalID) { - $msg = new Message(Message::MSGTYPE_PRIVATE, $src, MessageTarget::buildQuick($generalID), $text, new \DateTime(), new \DateTime('9999-12-31'), []); - $msg->send(true); - } - - if ($item == 'None') { - $db->update('general', [ - 'item' => 'None' - ], '`no` IN %li', $genlist); - } else { - $db->update('general', [ - 'item' => $item - ], '`no` IN %li AND item < %i', $genlist, $item); - } - break; case "하야입력": $db->update('general_turn', [ 'action' => 'che_하야', diff --git a/hwe/func.php b/hwe/func.php index 66774df1..0e7b550b 100644 --- a/hwe/func.php +++ b/hwe/func.php @@ -3,6 +3,7 @@ namespace sammo; use DateTime; +use Ds\Set; use sammo\Enums\InheritanceKey; use sammo\Enums\RankColumn; use sammo\Event\Action; @@ -1665,6 +1666,10 @@ function giveRandomUniqueItem(RandUtil $rng, General $general, string $acquireTy $logger->pushGlobalActionLog("{$generalName}{$josaYi} {$itemName}{$josaUl} 습득했습니다!"); $logger->pushGlobalHistoryLog("【{$acquireType}】{$nationName}{$generalName}{$josaYi} {$itemName}{$josaUl} 습득했습니다!"); + $givenUnique =$gameStor->getValue('givenUnique') ?? []; + $givenUnique[$itemCode] = ($givenUnique[$itemCode] ?? 0) + 1; + $gameStor->setValue('givenUnique', $givenUnique); + return true; } @@ -1866,6 +1871,11 @@ function tryInheritUniqueItem(RandUtil $rng, General $general, string $acquireTy $general->applyDB($db); + $gameStor = KVStorage::getStorage($db, 'game_env'); + $givenUnique =$gameStor->getValue('givenUnique') ?? []; + $givenUnique[$ownTarget] = ($givenUnique[$ownTarget] ?? 0) + 1; + $gameStor->setValue('givenUnique', $givenUnique); + //같은 종류의 유니크를 입찰했을 수 있으니 한번 더 검사한다. tryRollbackInheritUniqueItem($rng, $general); diff --git a/hwe/sammo/API/Auction/BidUniqueAuction.php b/hwe/sammo/API/Auction/BidUniqueAuction.php new file mode 100644 index 00000000..9c5c7948 --- /dev/null +++ b/hwe/sammo/API/Auction/BidUniqueAuction.php @@ -0,0 +1,65 @@ + $amount) { + if ($amount == 0) { + continue; + } + $availableItems[$itemKey] = $amount; + } + } + + $v = new Validator($this->args); + $v->rule('required', [ + 'auctionID', + 'amount', + ]) + ->rule('int', 'amount') + ->rule('int', 'auctionID') + ->rule('boolean', 'extendCloseDate'); + + if (!$v->validate()) { + return $v->errorStr(); + } + return null; + } + + public function getRequiredSessionMode(): int + { + return static::REQ_GAME_LOGIN; + } + + public function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag) + { + $auctionID = $this->args['auctionID']; + $amount = $this->args['amount']; + $tryExtendCloseDate = $this->arg['extendCloseDate'] ?? false; + + $generalID = $session->generalID; + $general = General::createGeneralObjFromDB($generalID); + $auction = new AuctionUniqueItem($auctionID, $general); + $result = $auction->bid($amount, $tryExtendCloseDate); + + if (is_string($result)) { + return $result; + } + + return null; + } +} diff --git a/hwe/sammo/API/Auction/OpenUniqueAuction.php b/hwe/sammo/API/Auction/OpenUniqueAuction.php index 21c6e2ee..ef3928ec 100644 --- a/hwe/sammo/API/Auction/OpenUniqueAuction.php +++ b/hwe/sammo/API/Auction/OpenUniqueAuction.php @@ -4,14 +4,12 @@ namespace sammo\API\Auction; use sammo\Session; use DateTimeInterface; -use sammo\Betting; -use sammo\DB; -use sammo\DTO\BettingItem; +use sammo\AuctionUniqueItem; use sammo\Validator; use sammo\GameConst; -use sammo\KVStorage; -use sammo\UserLogger; -use sammo\Util; +use sammo\General; + +use function sammo\buildItemClass; class OpenUniqueAuction extends \sammo\BaseAPI { @@ -34,7 +32,7 @@ class OpenUniqueAuction extends \sammo\BaseAPI ]) ->rule('int', 'amount') ->rule('min', 'amount', GameConst::$inheritItemUniqueMinPoint) - ->rule('keyExists', 'item', $availableItems); + ->rule('keyExists', 'itemID', $availableItems); if (!$v->validate()) { @@ -50,22 +48,22 @@ class OpenUniqueAuction extends \sammo\BaseAPI public function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag) { - /** @var int */ - $bettingID = $this->args['bettingID']; - /** @var int[] */ - $bettingType = $this->args['bettingType']; - /** @var int */ + $itemID = $this->args['itemID']; $amount = $this->args['amount']; + $generalID = $session->generalID; - $bettingHelper = new Betting($bettingID); - try { - $bettingHelper->bet($session->generalID, $session->userID, $bettingType, $amount); - } catch (\Throwable $e) { - return $e->getMessage(); + $itemObj = buildItemClass($itemID); + $general = General::createGeneralObjFromDB($generalID); + + $auctionResult = AuctionUniqueItem::openItemAuction($itemObj, $general, $amount); + + if(is_string($auctionResult)) { + return $auctionResult; } return [ - 'result' => true + 'result' => true, + 'auctionID' => $auctionResult->id, ]; } } diff --git a/hwe/sammo/API/General/DropItem.php b/hwe/sammo/API/General/DropItem.php index 61c82db9..317aea01 100644 --- a/hwe/sammo/API/General/DropItem.php +++ b/hwe/sammo/API/General/DropItem.php @@ -2,6 +2,7 @@ namespace sammo\API\General; +use Ds\Set; use sammo\DB; use sammo\Validator; @@ -9,6 +10,7 @@ use sammo\Session; use sammo\GameConst; use sammo\General; use sammo\JosaUtil; +use sammo\KVStorage; class DropItem extends \sammo\BaseAPI { @@ -56,12 +58,30 @@ class DropItem extends \sammo\BaseAPI $logger->pushGeneralActionLog("{$itemName}{$josaUl} 버렸습니다."); $nationName = $me->getStaticNation()['name']; + $db = DB::db(); if (!$item->isBuyable()) { $logger->pushGlobalActionLog("{$generalName}{$josaYi} {$itemName}{$josaUl} 잃었습니다!"); $logger->pushGlobalHistoryLog("【망실】{$nationName}{$generalName}{$josaYi} {$itemName}{$josaUl} 잃었습니다!"); + + $gameStor = KVStorage::getStorage($db, 'game_env'); + $givenUnique = $gameStor->getValue('givenUnique') ?? []; + $itemCode = $item->getRawClassName(); + if (key_exists($itemCode, $givenUnique)) { + $givenUniqueCnt = $givenUnique[$itemCode]; + $givenUniqueCnt -= 1; + if ($givenUniqueCnt <= 0) { + unset($givenUnique[$itemCode]); + } else { + $givenUnique[$itemCode] = $givenUniqueCnt; + } + } else { + //XXX: 처리하지 못한 코드가 있는가? + //FIXME: 여기에서 무언가 경고를 내야함 + } + $gameStor->setValue('givenUnique', $givenUnique); } - $me->applyDB(DB::db()); + $me->applyDB($db); return null; } diff --git a/hwe/sammo/Auction.php b/hwe/sammo/Auction.php index 7284b779..1d609c80 100644 --- a/hwe/sammo/Auction.php +++ b/hwe/sammo/Auction.php @@ -317,8 +317,6 @@ class Auction return '입찰에 실패했습니다: DB 오류'; } - - $general->setVar($resType->value, $general->getVar($resType->value) - $morePoint); if ($myPrevBid === null || ($highestBid !== null && $myPrevBid->id !== $highestBid->id)) { diff --git a/hwe/sammo/AuctionUniqueItem.php b/hwe/sammo/AuctionUniqueItem.php index 21af7aae..c717d08b 100644 --- a/hwe/sammo/AuctionUniqueItem.php +++ b/hwe/sammo/AuctionUniqueItem.php @@ -3,6 +3,7 @@ namespace sammo; use DateTimeImmutable; +use Ds\Set; use sammo\DTO\AuctionBidItem; use sammo\DTO\AuctionInfo; use sammo\DTO\AuctionInfoDetail; @@ -59,10 +60,33 @@ class AuctionUniqueItem extends Auction return '구매할 수 있는 아이템입니다.'; } + $itemKey = $item->getRawClassName(); + $db = DB::db(); + $auctionIDonProgress = $db->queryFirstField( + 'SELECT `id` FROM ng_auction WHERE `finished` = 0 AND `target` = %s AND `target` = %s', + AuctionType::UniqueItem->value, + $itemKey + ); + if ($auctionIDonProgress !== null) { + return '이미 경매가 진행중입니다.'; + } + + $gameStor = KVStorage::getStorage($db, 'game_env'); + $givenUnique = $gameStor->getValue('givenUnique') ?? []; + $givenUniqueCnt = $givenUnique[$itemKey] ?? 0; + $remainUniqueCnt = 0; + foreach (GameConst::$allItems as $itemList) { + if (!key_exists($itemKey, $itemList)) { + continue; + } + $remainUniqueCnt += $itemList[$itemKey]; + } + if ($remainUniqueCnt > 1 && $givenUniqueCnt >= $remainUniqueCnt - 1) { + return '더이상 이 아이템을 경매로 가져갈 수 없습니다.'; + } + $now = new DateTimeImmutable(); - $db = DB::db(); - $gameStor = KVStorage::getStorage($db, 'game_env'); $turnTerm = $gameStor->getValue('turnterm'); $closeDate = $now->add(TimeUtil::secondsToDateInterval( @@ -76,7 +100,7 @@ class AuctionUniqueItem extends Auction null, AuctionType::UniqueItem, false, - $item->getRawClassName(), + $itemKey, $general->getID(), ResourceType::inheritancePoint, $now, @@ -113,6 +137,9 @@ class AuctionUniqueItem extends Auction { $itemKey = $this->info->target; + if ($itemKey === null) { + throw new \Exception('아이템 키가 없습니다.'); + } $itemObj = buildItemClass($itemKey); $general = $bidder; $availableItemTypes = []; @@ -192,6 +219,11 @@ class AuctionUniqueItem extends Auction $logger->pushGlobalHistoryLog("【보물발견】{$nationName}{$generalName}{$josaYi} {$itemName}{$josaUl} 습득했습니다!"); $general->applyDB($db); + + $givenUnique = $gameStor->getValue('givenUnique') ?? []; + $givenUnique[$itemKey] = ($givenUnique[$itemKey] ?? 0) + 1; + $gameStor->setValue('givenUnique', $givenUnique); + return null; } @@ -263,7 +295,60 @@ class AuctionUniqueItem extends Auction public function bid(int $amount, bool $tryExtendCloseDate = false): ?string { - //TODO: 입찰 가능 제한확인(다른 경매 진행 등) + + $db = DB::db(); + /** @var AuctionInfo[] */ + $openUniqueAuctions = array_map(fn ($raw) => AuctionInfo::fromArray($raw), $db->query( + 'SELECT * FROM `ng_auction` WHERE `finished` = 0 AND `type`= %s', + AuctionType::UniqueItem->value + ) ?? []); + + $db = DB::db(); + $rawHighestBids = Util::convertArrayToDict($db->queryFirstRow( + 'SELECT * FROM ng_auction_bid WHERE auction_id IN %i ORDER BY `amount` DESC LIMIT 1', + $this->info->id + ) ?? [], 'auction_id'); + /** @var array */ + $highestBids = Util::mapWithKey( + fn ($auctionID, $bid) => AuctionBidItem::fromArray($bid), + $rawHighestBids + ); + + $itemCode = $this->info->target; + $bidItemTypes = new Set(); + foreach (GameConst::$allItems as $itemType => $itemList) { + if (($itemList[$itemCode] ?? 0) <= 0) { + continue; + } + $bidItemTypes->add($itemType); + } + + foreach ($openUniqueAuctions as $auction) { + $auctionID = $auction->id; + if (!isset($highestBids[$auctionID])) { + continue; + } + if ($auctionID === $this->auctionID) { + continue; + } + + $bid = $highestBids[$auctionID]; + if ($bid->generalID !== $this->general->getID()) { + continue; + } + + $itemCodeComp = $auction->target; + + foreach (GameConst::$allItems as $itemType => $itemList) { + if (($itemList[$itemCodeComp] ?? 0) <= 0) { + continue; + } + if ($bidItemTypes->contains($itemType)) { + return '1순위 입찰자인 경매중에 같은 부위가 있습니다.'; + } + } + } + return parent::bid($amount, $tryExtendCloseDate); } } diff --git a/hwe/sammo/Command/General/che_장비매매.php b/hwe/sammo/Command/General/che_장비매매.php index 2204dae9..a7faaf89 100644 --- a/hwe/sammo/Command/General/che_장비매매.php +++ b/hwe/sammo/Command/General/che_장비매매.php @@ -13,7 +13,8 @@ use \sammo\{ GameConst, GameUnitConst, LastTurn, - Command + Command, + KVStorage }; use function \sammo\buildItemClass; @@ -181,12 +182,28 @@ class che_장비매매 extends Command\GeneralCommand $general->onArbitraryAction($general, $rng, '장비매매', '판매', ['itemCode' => $itemCode]); $general->setItem($itemType, null); - if(!$itemObj->isBuyable()){ + if (!$itemObj->isBuyable()) { $generalName = $general->getName(); $josaYi = JosaUtil::pick($generalName, '이'); $nationName = $general->getStaticNation()['name']; $logger->pushGlobalActionLog("{$generalName}{$josaYi} {$itemName}{$josaUl} 판매했습니다!"); $logger->pushGlobalHistoryLog("【판매】{$nationName}{$generalName}{$josaYi} {$itemName}{$josaUl} 판매했습니다!"); + + $gameStor = KVStorage::getStorage($db, 'game_env'); + $givenUnique = $gameStor->getValue('givenUnique') ?? []; + if (key_exists($itemCode, $givenUnique)) { + $givenUniqueCnt = $givenUnique[$itemCode]; + $givenUniqueCnt -= 1; + if ($givenUniqueCnt <= 0) { + unset($givenUnique[$itemCode]); + } else { + $givenUnique[$itemCode] = $givenUniqueCnt; + } + } else { + //XXX: 처리하지 못한 코드가 있는가? + //FIXME: 여기에서 무언가 경고를 내야함 + } + $gameStor->setValue('givenUnique', $givenUnique); } }