Merge branch devel into NPC possession comparison

This commit is contained in:
2026-07-31 03:53:22 +00:00
10 changed files with 543 additions and 69 deletions
+22 -8
View File
@@ -22,8 +22,6 @@ $db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env');
$gameStor->cacheValues(['turntime', 'opentime', 'autorun_user', 'npcmode']);
increaseRefresh("내정보", 1);
$me = General::createObjFromDB($generalID, null, GeneralQueryMode::FullWithAccessLog);
$myset = $me->getVar('myset');
@@ -41,10 +39,26 @@ $lastRefresh = $db->queryFirstField(
$generalID
);
$targetTime = addTurn($lastRefresh, $gameStor->turnterm, GameConst::$minTurnDieOnPrestart);
if ($gameStor->turntime <= $gameStor->opentime) {
//서버 가오픈시 할 수 있는 행동
if ($me->getNPCType() == 0 && $me->getNationID() == 0) {
$nextChange = $me->getAuxVar('next_change');
if (!is_string($nextChange) || $nextChange === '') {
$nextChange = null;
}
increaseRefresh("내정보", 1);
if ($gameStor->turntime <= $gameStor->opentime) {
$targetTime = $me->getAuxVar('prestart_delete_after');
if (!is_string($targetTime) || $targetTime === '') {
$targetTime = addTurn(
$lastRefresh ?: TimeUtil::now(),
$gameStor->turnterm,
GameConst::$minTurnDieOnPrestart
);
$me->setAuxVar('prestart_delete_after', $targetTime);
$me->applyDB($db);
}
//서버 가오픈시 할 수 있는 행동
if ($me->getNPCType() == 0 && $me->getNationID() == 0) {
$showDieOnPrestartBtn = true;
if ($targetTime <= TimeUtil::now()) {
$availableDieOnPrestart = true;
@@ -174,7 +188,7 @@ $changeDefence999Atmos = $me->onCalcDomestic('changeDefenceTrain', "atmos999", $
<?php endif; ?>
<?php if ($gameStor->npcmode == 2 && $me->getNPCType() == 0) : ?>
다른 장수 선택 (<?= substr($me->getAuxVar('next_change') ?? TimeUtil::now(), 0, 19) ?> 부터)<br>
다른 장수 선택 (<?= $nextChange ? substr($nextChange, 0, 19) : '지금' ?>부터)<br>
<a href="select_general_from_pool.php" id='select_general_from_pool'><button type="button" style=background-color:<?= GameConst::$basecolor2 ?>;color:white;width:160px;height:30px;font-size:14px;>다른 장수 선택</button></a><br><br>
<?php endif; ?>
@@ -272,4 +286,4 @@ $changeDefence999Atmos = $me->onCalcDomestic('changeDefenceTrain', "atmos999", $
</div>
</body>
</html>
</html>
+65 -37
View File
@@ -42,15 +42,21 @@ function pushGeneralCommand(int $generalID, int $turnCnt=1){
$db = DB::db();
$db->update('general_turn', [
'turn_idx'=>$db->sqleval('turn_idx + %i', $turnCnt)
], 'general_id=%i ORDER BY turn_idx DESC', $generalID);
$db->update('general_turn', [
'turn_idx'=>$db->sqleval('turn_idx - %i', GameConst::$maxTurn),
'action'=>'휴식',
'arg'=>'{}',
'brief'=>'휴식'
], 'general_id=%i AND turn_idx >= %i', $generalID, GameConst::$maxTurn);
$db->query(
'UPDATE general_turn AS dst
LEFT JOIN general_turn AS src
ON src.general_id = dst.general_id
AND src.turn_idx = dst.turn_idx - %i
SET dst.action = IF(src.id IS NULL, %s, src.action),
dst.arg = IF(src.id IS NULL, %s, src.arg),
dst.brief = IF(src.id IS NULL, %s, src.brief)
WHERE dst.general_id = %i',
$turnCnt,
'휴식',
'{}',
'휴식',
$generalID
);
}
function pullGeneralCommand(int $generalID, int $turnCnt=1){
@@ -67,15 +73,21 @@ function pullGeneralCommand(int $generalID, int $turnCnt=1){
$db = DB::db();
$db->update('general_turn', [
'turn_idx'=>$db->sqleval('turn_idx + %i', GameConst::$maxTurn),
'action'=>'휴식',
'arg'=>'{}',
'brief'=>'휴식'
], 'general_id=%i AND turn_idx < %i', $generalID, $turnCnt);
$db->update('general_turn', [
'turn_idx'=>$db->sqleval('turn_idx - %i', $turnCnt)
], 'general_id=%i ORDER BY turn_idx ASC', $generalID);
$db->query(
'UPDATE general_turn AS dst
LEFT JOIN general_turn AS src
ON src.general_id = dst.general_id
AND src.turn_idx = dst.turn_idx + %i
SET dst.action = IF(src.id IS NULL, %s, src.action),
dst.arg = IF(src.id IS NULL, %s, src.arg),
dst.brief = IF(src.id IS NULL, %s, src.brief)
WHERE dst.general_id = %i',
$turnCnt,
'휴식',
'{}',
'휴식',
$generalID
);
}
function repeatGeneralCommand(int $generalId, int $turnCnt){
@@ -126,15 +138,23 @@ function pushNationCommand(int $nationID, int $officerLevel, int $turnCnt=1){
$db = DB::db();
$db->update('nation_turn', [
'turn_idx'=>$db->sqleval('turn_idx + %i', $turnCnt)
], 'nation_id=%i AND officer_level=%i ORDER BY turn_idx DESC', $nationID, $officerLevel);
$db->update('nation_turn', [
'turn_idx'=>$db->sqleval('turn_idx - %i', GameConst::$maxChiefTurn),
'action'=>'휴식',
'arg'=>'{}',
'brief'=>'휴식'
], 'nation_id=%i AND officer_level=%i AND turn_idx >= %i ORDER BY turn_idx ASC', $nationID, $officerLevel, GameConst::$maxChiefTurn);
$db->query(
'UPDATE nation_turn AS dst
LEFT JOIN nation_turn AS src
ON src.nation_id = dst.nation_id
AND src.officer_level = dst.officer_level
AND src.turn_idx = dst.turn_idx - %i
SET dst.action = IF(src.id IS NULL, %s, src.action),
dst.arg = IF(src.id IS NULL, %s, src.arg),
dst.brief = IF(src.id IS NULL, %s, src.brief)
WHERE dst.nation_id = %i AND dst.officer_level = %i',
$turnCnt,
'휴식',
'{}',
'휴식',
$nationID,
$officerLevel
);
}
function pullNationCommand(int $nationID, int $officerLevel, int $turnCnt=1){
@@ -157,15 +177,23 @@ function pullNationCommand(int $nationID, int $officerLevel, int $turnCnt=1){
$db = DB::db();
$db->update('nation_turn', [
'turn_idx'=>$db->sqleval('turn_idx + %i', GameConst::$maxChiefTurn),
'action'=>'휴식',
'arg'=>'{}',
'brief'=>'휴식',
], 'nation_id=%i AND officer_level=%i AND turn_idx < %i', $nationID, $officerLevel, $turnCnt);
$db->update('nation_turn', [
'turn_idx'=>$db->sqleval('turn_idx - %i', $turnCnt)
], 'nation_id=%i AND officer_level=%i ORDER BY turn_idx ASC', $nationID, $officerLevel);
$db->query(
'UPDATE nation_turn AS dst
LEFT JOIN nation_turn AS src
ON src.nation_id = dst.nation_id
AND src.officer_level = dst.officer_level
AND src.turn_idx = dst.turn_idx + %i
SET dst.action = IF(src.id IS NULL, %s, src.action),
dst.arg = IF(src.id IS NULL, %s, src.arg),
dst.brief = IF(src.id IS NULL, %s, src.brief)
WHERE dst.nation_id = %i AND dst.officer_level = %i',
$turnCnt,
'휴식',
'{}',
'휴식',
$nationID,
$officerLevel
);
}
function repeatNationCommand(int $nationID, int $officerLevel, int $turnCnt){
@@ -494,4 +522,4 @@ function setNationCommand(int $generalID, array $turnList, string $command, ?arr
'arg_test'=>true,
'reason'=>'success'
];
}
}
+4
View File
@@ -177,6 +177,10 @@ $builder->setOwnerName($userNick);
$builder->setKillturn(5);
$builder->setNPCType(0);
$builder->setAuxVar('next_change', TimeUtil::nowAddMinutes(12 * $env['turnterm']));
$builder->setAuxVar(
'prestart_delete_after',
addTurn($now, $env['turnterm'], GameConst::$minTurnDieOnPrestart)
);
$builder->fillRemainSpecAsZero($env);
if ($isCentennialAllStar) {
$candidateCities = $db->queryFirstColumn(
+18 -8
View File
@@ -39,6 +39,10 @@ class DieOnPrestart extends \sammo\BaseAPI
$gameStor->cacheValues(['turnterm', 'opentime', 'turntime', 'year', 'month']);
$general = $db->queryFirstRow('SELECT no,name,nation,owner_name,npc FROM general WHERE owner=%i AND npc = 0', $userID);
if (!$general) {
return '장수가 없습니다';
}
$lastRefresh = $db->queryFirstField(
'SELECT %b FROM general_access_log WHERE %b = %i',
GeneralAccessLogColumn::lastRefresh->value,
@@ -46,8 +50,9 @@ class DieOnPrestart extends \sammo\BaseAPI
$general['no']
);
if (!$general) {
return '장수가 없습니다';
$generalObj = General::createObjFromDB($general['no']);
if ($generalObj instanceof DummyGeneral) {
trigger_error("올바르지 않은 삭제 프로세스 $userID", E_USER_WARNING);
}
increaseRefresh("장수 삭제", 1);
@@ -61,18 +66,23 @@ class DieOnPrestart extends \sammo\BaseAPI
return '이미 국가에 소속되어있습니다.';
}
$targetTime = $generalObj->getAuxVar('prestart_delete_after');
if (!is_string($targetTime) || $targetTime === '') {
$targetTime = addTurn(
$lastRefresh ?: TimeUtil::now(),
$gameStor->turnterm,
GameConst::$minTurnDieOnPrestart
);
$generalObj->setAuxVar('prestart_delete_after', $targetTime);
$generalObj->applyDB($db);
}
//서버 가오픈시 할 수 있는 행동
$targetTime = addTurn($lastRefresh, $gameStor->turnterm, GameConst::$minTurnDieOnPrestart);
if ($targetTime > TimeUtil::now()) {
$targetTimeShort = substr($targetTime, 0, 19);
return "아직 삭제할 수 없습니다. {$targetTimeShort} 부터 가능합니다.";
}
$generalObj = General::createObjFromDB($general['no']);
if ($generalObj instanceof DummyGeneral) {
trigger_error("올바르지 않은 삭제 프로세스 $userID", E_USER_WARNING);
}
$generalName = $generalObj->getName();
$josaYi = JosaUtil::pick($generalName, '이');
$generalObj->kill($db, true, "<Y>{$generalName}</>{$josaYi} 홀연히 모습을 <R>감추었습니다</>");
+7
View File
@@ -435,6 +435,13 @@ class Join extends \sammo\BaseAPI
'specage2' => $specage2,
'special2' => $special2,
'penalty' => Json::encode($penalty),
'aux' => Json::encode([
'prestart_delete_after' => addTurn(
$now,
$admin['turnterm'],
GameConst::$minTurnDieOnPrestart
),
]),
]);
$generalID = $db->insertId();
$db->insert('general_access_log', [
@@ -451,6 +451,7 @@ final class CentennialAllStarGrowthService
if (!in_array($general->getNPCType(), [3, 4], true)) {
return null;
}
self::initializeGeneratedNPC($general, $targetInfo);
$result = self::applyTarget(
$general,
$targetInfo,
@@ -462,6 +463,36 @@ final class CentennialAllStarGrowthService
return $result;
}
/**
* Discards the builder's generic random stat/dex values before applying
* the selected all-star target. A newly generated NPC has no organic
* growth yet, so those temporary values must not mask the creation-date
* event baseline.
*/
public static function initializeGeneratedNPC(
General $general,
array $targetInfo
): void {
foreach (self::STAT_KEYS as $key) {
$target = min(
GameConst::$maxLevel,
max(0, (int) ($targetInfo[$key] ?? 0))
);
$general->updateVar(
$key,
CentennialAllStarGrowth::statFloor(
$target,
GameConst::$defaultStatMin,
0
)
);
}
foreach (self::DEX_KEYS as $key) {
$general->updateVar($key, 0);
}
$general->setAuxVar(self::AUX_KEY, self::initialAux($targetInfo));
}
/**
* Moves the event-backed part of a dex conversion with the converted
* value and consumes any guaranteed floor crossed by the source value.
+63 -16
View File
@@ -1045,18 +1045,64 @@ class General extends GeneralBase implements iAction
/** @var Map<GeneralAccessLog,int>|null */
$rawAccessLog = null;
$rankColumnValues = array_map(fn (\BackedEnum $e) => $e->value, $rankColumn);
if (!$accessLogColumn) {
$rawGeneral = $db->queryFirstRow('SELECT %l FROM general WHERE no = %i', Util::formatListOfBackticks($column), $generalID);
if ($rankColumn) {
$rawGeneral = $db->queryFirstRow(
'SELECT %l, (
SELECT GROUP_CONCAT(
CONCAT(`type`, CHAR(61), `value`)
ORDER BY `type`
)
FROM rank_data
WHERE rank_data.general_id = general.no
AND `type` IN %ls
) AS `_rank_values`
FROM general WHERE no = %i',
Util::formatListOfBackticks($column),
$rankColumnValues,
$generalID
);
} else {
$rawGeneral = $db->queryFirstRow(
'SELECT %l FROM general WHERE no = %i',
Util::formatListOfBackticks($column),
$generalID
);
}
} else {
$rawGeneral = $db->queryFirstRow(
'SELECT %l, %l FROM `general` LEFT JOIN general_access_log
ON general.no = general_access_log.general_id WHERE no = %i',
Util::formatListOfBackticks($column),
Util::formatListOfBackticks($accessLogColumn),
$generalID
);
if ($rankColumn) {
$rawGeneral = $db->queryFirstRow(
'SELECT %l, %l, (
SELECT GROUP_CONCAT(
CONCAT(`type`, CHAR(61), `value`)
ORDER BY `type`
)
FROM rank_data
WHERE rank_data.general_id = general.no
AND `type` IN %ls
) AS `_rank_values`
FROM `general` LEFT JOIN general_access_log
ON general.no = general_access_log.general_id
WHERE no = %i',
Util::formatListOfBackticks($column),
Util::formatListOfBackticks($accessLogColumn),
$rankColumnValues,
$generalID
);
} else {
$rawGeneral = $db->queryFirstRow(
'SELECT %l, %l FROM `general` LEFT JOIN general_access_log
ON general.no = general_access_log.general_id WHERE no = %i',
Util::formatListOfBackticks($column),
Util::formatListOfBackticks($accessLogColumn),
$generalID
);
}
}
if ($accessLogColumn) {
$rawAccessLog = new Map();
foreach ($accessLogColumn as $accessLogKey) {
if (!key_exists($accessLogKey->value, $rawGeneral)) {
@@ -1076,15 +1122,16 @@ class General extends GeneralBase implements iAction
$rawRankValues = new Map();
if ($rankColumn) {
$rawValue = $db->queryAllLists(
'SELECT `type`, `value` FROM rank_data WHERE general_id = %i AND `type` IN %ls',
$generalID,
array_map(fn (\BackedEnum $e) => $e->value, $rankColumn)
);
foreach ($rawValue as [$rawRankType, $rankValue]) {
$rankType = RankColumn::tryFrom($rawRankType);
$rawRankValues->put($rankType, $rankValue);
$rawRankPairs = $rawGeneral['_rank_values'];
foreach ($rawRankPairs === null || $rawRankPairs === ''
? []
: explode(',', $rawRankPairs) as $rawRankPair
) {
[$rawRankType, $rankValue] = explode('=', $rawRankPair, 2);
$rankType = RankColumn::from($rawRankType);
$rawRankValues->put($rankType, (int) $rankValue);
}
unset($rawGeneral['_rank_values']);
}