feat: 100기 NPC 동조와 후보 추첨 보완
This commit is contained in:
@@ -8,6 +8,11 @@ use sammo\Util;
|
||||
|
||||
abstract class AbsFromUserPool extends AbsGeneralPool{
|
||||
|
||||
protected static function getCandidateWeight(array $info, int $owner): int|float
|
||||
{
|
||||
return array_sum($info['dex'] ?? []);
|
||||
}
|
||||
|
||||
public function occupyGeneralName(): bool
|
||||
{
|
||||
$generalID = $this->getGeneralBuilder()->getGeneralID();
|
||||
@@ -36,8 +41,7 @@ abstract class AbsFromUserPool extends AbsGeneralPool{
|
||||
$pool = [];
|
||||
foreach($db->query('SELECT id, unique_name, info FROM select_pool WHERE reserved_until IS NULL AND general_id IS NULL', $pickCnt) as $cand){
|
||||
$cand['info'] = Json::decode($cand['info']);
|
||||
$dexTotal = array_sum($cand['info']['dex']);
|
||||
$pool[] = [$cand, $dexTotal];
|
||||
$pool[] = [$cand, static::getCandidateWeight($cand['info'], $owner)];
|
||||
}
|
||||
|
||||
if(count($pool) < $pickCnt){
|
||||
@@ -69,4 +73,4 @@ abstract class AbsFromUserPool extends AbsGeneralPool{
|
||||
|
||||
return array_values($result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ final class CentennialAllStarGrowthService
|
||||
public const POOL_CLASS = 'SPoolUnderU100';
|
||||
public const AUX_KEY = 'event100_allstar';
|
||||
public const TRAIT_UNLOCK_PROGRESS = 0.4;
|
||||
public const NPC_PROGRESS_MULTIPLIER = 0.9;
|
||||
|
||||
private const STAT_KEYS = ['leadership', 'strength', 'intel'];
|
||||
private const DEX_KEYS = ['dex1', 'dex2', 'dex3', 'dex4', 'dex5'];
|
||||
@@ -35,18 +36,47 @@ final class CentennialAllStarGrowthService
|
||||
$builder->setAuxVar(self::AUX_KEY, self::initialAux($targetInfo));
|
||||
}
|
||||
|
||||
public static function calculateProgress(
|
||||
int $startYear,
|
||||
int $year,
|
||||
int $month,
|
||||
float $progressMultiplier = 1.0
|
||||
): float {
|
||||
if ($progressMultiplier < 0 || $progressMultiplier > 1) {
|
||||
throw new \InvalidArgumentException('progress multiplier must be between 0 and 1');
|
||||
}
|
||||
return min(
|
||||
1,
|
||||
CentennialAllStarGrowth::progress($startYear, $year, $month)
|
||||
* $progressMultiplier
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mutates the General object but leaves persistence to the caller.
|
||||
*
|
||||
* @return array{progress:float,milestone:int,previousMilestone:int,targetChanged:bool,changed:bool}
|
||||
*/
|
||||
public static function applyTarget(General $general, array $targetInfo, array $env): array
|
||||
public static function applyTarget(
|
||||
General $general,
|
||||
array $targetInfo,
|
||||
array $env,
|
||||
float $progressMultiplier = 1.0
|
||||
): array
|
||||
{
|
||||
$startYear = (int) $env['startyear'];
|
||||
$year = (int) $env['year'];
|
||||
$month = (int) $env['month'];
|
||||
$progress = CentennialAllStarGrowth::progress($startYear, $year, $month);
|
||||
$progressMonth = max(0, ($year - $startYear) * 12 + $month - 1);
|
||||
$progress = self::calculateProgress(
|
||||
$startYear,
|
||||
$year,
|
||||
$month,
|
||||
$progressMultiplier
|
||||
);
|
||||
$progressMonth = (int) floor(
|
||||
max(0, ($year - $startYear) * 12 + $month - 1)
|
||||
* $progressMultiplier
|
||||
);
|
||||
$targetId = (string) ($targetInfo['uniqueName'] ?? '');
|
||||
|
||||
$aux = $general->getAuxVar(self::AUX_KEY);
|
||||
@@ -159,6 +189,42 @@ final class CentennialAllStarGrowthService
|
||||
];
|
||||
}
|
||||
|
||||
public static function progressMultiplierFor(General $general): float
|
||||
{
|
||||
return self::progressMultiplierForNPCType($general->getNPCType());
|
||||
}
|
||||
|
||||
public static function progressMultiplierForNPCType(int $npcType): float
|
||||
{
|
||||
return in_array($npcType, [3, 4], true)
|
||||
? self::NPC_PROGRESS_MULTIPLIER
|
||||
: 1.0;
|
||||
}
|
||||
|
||||
public static function applyCurrentTargetToBuiltNPC(
|
||||
\MeekroDB $db,
|
||||
GeneralBuilder $builder,
|
||||
array $targetInfo,
|
||||
array $env
|
||||
): ?array {
|
||||
if (!self::isActive()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$general = General::createObjFromDB($builder->getGeneralID());
|
||||
if (!in_array($general->getNPCType(), [3, 4], true)) {
|
||||
return null;
|
||||
}
|
||||
$result = self::applyTarget(
|
||||
$general,
|
||||
$targetInfo,
|
||||
$env,
|
||||
self::NPC_PROGRESS_MULTIPLIER
|
||||
);
|
||||
$general->applyDB($db);
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function recordableValue(General $general, string $key): int
|
||||
{
|
||||
$aux = $general->getAuxVar(self::AUX_KEY);
|
||||
|
||||
@@ -7,6 +7,7 @@ use \sammo\Util;
|
||||
use \sammo\JosaUtil;
|
||||
use \sammo\General;
|
||||
use \sammo\ActionLogger;
|
||||
use \sammo\CentennialAllStarGrowthService;
|
||||
use \sammo\GameConst;
|
||||
use \sammo\LastTurn;
|
||||
use \sammo\GameUnitConst;
|
||||
@@ -190,6 +191,12 @@ class che_인재탐색 extends Command\GeneralCommand
|
||||
$newNPC->fillRemainSpecAsRandom($pickTypeList, $avgGen, $env);
|
||||
|
||||
$newNPC->build($this->env);
|
||||
CentennialAllStarGrowthService::applyCurrentTargetToBuiltNPC(
|
||||
$db,
|
||||
$newNPC,
|
||||
$pickedNPC->getInfo(),
|
||||
$this->env
|
||||
);
|
||||
$pickedNPC->occupyGeneralName();
|
||||
$npcName = $newNPC->getGeneralName();
|
||||
$josaRa = JosaUtil::pick($npcName, '라');
|
||||
|
||||
@@ -7,6 +7,7 @@ use \sammo\Util;
|
||||
use \sammo\JosaUtil;
|
||||
use \sammo\General;
|
||||
use \sammo\ActionLogger;
|
||||
use \sammo\CentennialAllStarGrowthService;
|
||||
use \sammo\GameConst;
|
||||
use \sammo\LastTurn;
|
||||
use \sammo\GameUnitConst;
|
||||
@@ -160,6 +161,12 @@ class che_의병모집 extends Command\NationCommand
|
||||
$newNPC->fillRemainSpecAsRandom($pickTypeList, $avgGen, $env);
|
||||
|
||||
$newNPC->build($this->env);
|
||||
CentennialAllStarGrowthService::applyCurrentTargetToBuiltNPC(
|
||||
$db,
|
||||
$newNPC,
|
||||
$pickedNPC->getInfo(),
|
||||
$this->env
|
||||
);
|
||||
$pickedNPC->occupyGeneralName();
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,12 @@ class AdvanceCentennialAllStar extends \sammo\Event\Action
|
||||
) as $row) {
|
||||
$general = General::createObjFromDB((int) $row['no']);
|
||||
$targetInfo = Json::decode($row['info']);
|
||||
$result = CentennialAllStarGrowthService::applyTarget($general, $targetInfo, $env);
|
||||
$result = CentennialAllStarGrowthService::applyTarget(
|
||||
$general,
|
||||
$targetInfo,
|
||||
$env,
|
||||
CentennialAllStarGrowthService::progressMultiplierFor($general)
|
||||
);
|
||||
|
||||
if ($result['milestone'] > $result['previousMilestone']) {
|
||||
$percent = $result['milestone'] * 20;
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace sammo\Event\Action;
|
||||
use \sammo\GameConst;
|
||||
use \sammo\Util;
|
||||
use \sammo\DB;
|
||||
use sammo\CentennialAllStarGrowthService;
|
||||
use sammo\LiteHashDRBG;
|
||||
use sammo\RandUtil;
|
||||
use sammo\UniqueConst;
|
||||
@@ -35,7 +36,8 @@ class CreateManyNPC extends \sammo\Event\Action
|
||||
)));
|
||||
|
||||
$result = [];
|
||||
foreach (pickGeneralFromPool(DB::db(), $rng, 0, $cnt) as $pickedNPC) {
|
||||
$db = DB::db();
|
||||
foreach (pickGeneralFromPool($db, $rng, 0, $cnt) as $pickedNPC) {
|
||||
$age = $rng->nextRangeInt(20, 25);
|
||||
$birthYear = $env['year'] - $age;
|
||||
$deathYear = $env['year'] + $rng->nextRangeInt(10, 50);
|
||||
@@ -50,6 +52,12 @@ class CreateManyNPC extends \sammo\Event\Action
|
||||
}
|
||||
$newNPC->fillRemainSpecAsZero($env);
|
||||
$newNPC->build($env);
|
||||
CentennialAllStarGrowthService::applyCurrentTargetToBuiltNPC(
|
||||
$db,
|
||||
$newNPC,
|
||||
$pickedNPC->getInfo(),
|
||||
$env
|
||||
);
|
||||
$pickedNPC->occupyGeneralName();
|
||||
$result[] = [
|
||||
$newNPC->getGeneralName(), $newNPC->getGeneralID()
|
||||
|
||||
+4682
-4682
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,11 @@ use sammo\RandUtil;
|
||||
|
||||
class SPoolUnderU100 extends AbsFromUserPool
|
||||
{
|
||||
private const MIN_DEX_WEIGHT = 100000;
|
||||
private const STAT_BONUS_MIN_TOTAL = 160;
|
||||
private const STAT_BONUS_MAX_TOTAL = 190;
|
||||
private const STAT_BONUS_MAX_MULTIPLIER = 1.5;
|
||||
|
||||
public function __construct(\MeekroDB $db, RandUtil $rng, array $info, string $validUntil)
|
||||
{
|
||||
$targetInfo = $info;
|
||||
@@ -36,6 +41,32 @@ class SPoolUnderU100 extends AbsFromUserPool
|
||||
return '100기 올스타 클래식';
|
||||
}
|
||||
|
||||
protected static function getCandidateWeight(array $info, int $owner): int|float
|
||||
{
|
||||
$dexWeight = max(
|
||||
self::MIN_DEX_WEIGHT,
|
||||
array_sum($info['dex'] ?? [])
|
||||
);
|
||||
if ($owner <= 0) {
|
||||
return $dexWeight;
|
||||
}
|
||||
|
||||
$statTotal = array_sum([
|
||||
(int) ($info['leadership'] ?? 0),
|
||||
(int) ($info['strength'] ?? 0),
|
||||
(int) ($info['intel'] ?? 0),
|
||||
]);
|
||||
$normalizedStat = min(1, max(
|
||||
0,
|
||||
($statTotal - self::STAT_BONUS_MIN_TOTAL)
|
||||
/ (self::STAT_BONUS_MAX_TOTAL - self::STAT_BONUS_MIN_TOTAL)
|
||||
));
|
||||
$statMultiplier = 1
|
||||
+ (self::STAT_BONUS_MAX_MULTIPLIER - 1) * $normalizedStat;
|
||||
|
||||
return $dexWeight * $statMultiplier;
|
||||
}
|
||||
|
||||
public static function initPool(\MeekroDB $db)
|
||||
{
|
||||
$jsonData = Json::decode(file_get_contents(__DIR__ . '/Pool/UnderS100.json'));
|
||||
|
||||
Reference in New Issue
Block a user