feat: 100기 NPC 동조 및 후보 추첨 기능 개선과 초기 능력치 계산 로직 추가
This commit is contained in:
@@ -51,6 +51,22 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.picture_choice {
|
||||||
|
margin: 8px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.picture_choice label,
|
||||||
|
.event_picture label {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
margin: 4px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.picture_choice img {
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
.custom_picture {
|
.custom_picture {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@@ -61,4 +77,4 @@
|
|||||||
|
|
||||||
.custom_stat {
|
.custom_stat {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,13 @@ function sortTokens(&$tokens){
|
|||||||
}
|
}
|
||||||
|
|
||||||
function putInfoText(&$info){
|
function putInfoText(&$info){
|
||||||
|
if (($info['event100Growth'] ?? false) === true) {
|
||||||
|
$initialStats = CentennialAllStarGrowthService::calculateUserInitialStats($info);
|
||||||
|
$info['initialLeadership'] = $initialStats['leadership'];
|
||||||
|
$info['initialStrength'] = $initialStats['strength'];
|
||||||
|
$info['initialIntel'] = $initialStats['intel'];
|
||||||
|
}
|
||||||
|
|
||||||
if(key_exists('specialDomestic', $info)){
|
if(key_exists('specialDomestic', $info)){
|
||||||
$class = buildGeneralSpecialDomesticClass($info['specialDomestic']);
|
$class = buildGeneralSpecialDomesticClass($info['specialDomestic']);
|
||||||
$info['specialDomesticName'] = $class->getName();
|
$info['specialDomesticName'] = $class->getName();
|
||||||
@@ -91,4 +98,4 @@ Json::die([
|
|||||||
'result'=>true,
|
'result'=>true,
|
||||||
'pick'=>$pick,
|
'pick'=>$pick,
|
||||||
'validUntil'=>$valid_until
|
'validUntil'=>$valid_until
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ $intel = Util::getPost(
|
|||||||
);
|
);
|
||||||
$personal = Util::getPost('personal', 'string', null);
|
$personal = Util::getPost('personal', 'string', null);
|
||||||
$use_own_picture = Util::getPost('use_own_picture', 'bool', false);
|
$use_own_picture = Util::getPost('use_own_picture', 'bool', false);
|
||||||
|
$pictureSource = Util::getPost('picture_source', 'string', 'selected');
|
||||||
|
|
||||||
|
|
||||||
if(!$pick){
|
if(!$pick){
|
||||||
@@ -69,16 +70,39 @@ if(!$selectInfo){
|
|||||||
}
|
}
|
||||||
$selectInfo = Json::decode($selectInfo);
|
$selectInfo = Json::decode($selectInfo);
|
||||||
|
|
||||||
$ownerInfo = RootDB::db()->queryFirstRow('SELECT `name`,`picture`,`imgsvr` FROM member WHERE `NO`=%i',$userID);
|
$ownerInfo = RootDB::db()->queryFirstRow(
|
||||||
if(!$ownerInfo){
|
'SELECT `name`,`picture`,`imgsvr`,`grade` FROM member WHERE `NO`=%i',
|
||||||
|
$userID
|
||||||
|
);
|
||||||
|
if(!$ownerInfo){
|
||||||
Json::die([
|
Json::die([
|
||||||
'result'=>false,
|
'result'=>false,
|
||||||
'reason'=>'멤버 정보를 가져오지 못했습니다.'
|
'reason'=>'멤버 정보를 가져오지 못했습니다.'
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
if ($isCentennialAllStar) {
|
||||||
|
if (!in_array($pictureSource, ['selected', 'own'], true)) {
|
||||||
$gencount = $db->queryFirstField('SELECT count(`no`) FROM general WHERE npc<2');
|
Json::die([
|
||||||
|
'result' => false,
|
||||||
|
'reason' => '올바르지 않은 전콘 선택입니다.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if ($pictureSource === 'own') {
|
||||||
|
$canUseOwnPicture = in_array('picture', GameConst::$generalPoolAllowOption, true)
|
||||||
|
&& $env['show_img_level'] >= 1
|
||||||
|
&& $ownerInfo['grade'] >= 1
|
||||||
|
&& $ownerInfo['picture'] !== '';
|
||||||
|
if (!$canUseOwnPicture) {
|
||||||
|
Json::die([
|
||||||
|
'result' => false,
|
||||||
|
'reason' => '사용할 수 있는 내 전콘이 없습니다.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$gencount = $db->queryFirstField('SELECT count(`no`) FROM general WHERE npc<2');
|
||||||
|
|
||||||
if ($gencount >= $maxgeneral) {
|
if ($gencount >= $maxgeneral) {
|
||||||
Json::die([
|
Json::die([
|
||||||
@@ -102,9 +126,12 @@ if ($isCentennialAllStar) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$builder = $pickedGeneral->getGeneralBuilder();
|
$builder = $pickedGeneral->getGeneralBuilder();
|
||||||
|
if ($isCentennialAllStar) {
|
||||||
|
CentennialAllStarGrowthService::prepareInitialUser($builder, $selectInfo);
|
||||||
|
}
|
||||||
|
|
||||||
foreach(GameConst::$generalPoolAllowOption as $allowOption){
|
foreach(GameConst::$generalPoolAllowOption as $allowOption){
|
||||||
if($allowOption == 'stat'){
|
if($allowOption == 'stat' && !$isCentennialAllStar){
|
||||||
$leadership = Util::valueFit($leadership, GameConst::$defaultStatMin, GameConst::$defaultStatMax);
|
$leadership = Util::valueFit($leadership, GameConst::$defaultStatMin, GameConst::$defaultStatMax);
|
||||||
$strength = Util::valueFit($strength, GameConst::$defaultStatMin, GameConst::$defaultStatMax);
|
$strength = Util::valueFit($strength, GameConst::$defaultStatMin, GameConst::$defaultStatMax);
|
||||||
$intel = Util::valueFit($intel, GameConst::$defaultStatMin, GameConst::$defaultStatMax);
|
$intel = Util::valueFit($intel, GameConst::$defaultStatMin, GameConst::$defaultStatMax);
|
||||||
@@ -117,8 +144,14 @@ foreach(GameConst::$generalPoolAllowOption as $allowOption){
|
|||||||
}
|
}
|
||||||
$builder->setStat($leadership, $strength, $intel);
|
$builder->setStat($leadership, $strength, $intel);
|
||||||
}
|
}
|
||||||
else if($allowOption == 'picture' && $use_own_picture){
|
else if(
|
||||||
$builder->setPicture($ownerInfo['imgsvr'], $ownerInfo['picture']);
|
$allowOption == 'picture'
|
||||||
|
&& (
|
||||||
|
(!$isCentennialAllStar && $use_own_picture)
|
||||||
|
|| ($isCentennialAllStar && $pictureSource === 'own')
|
||||||
|
)
|
||||||
|
){
|
||||||
|
$builder->setPicture($ownerInfo['imgsvr'], $ownerInfo['picture']);
|
||||||
}
|
}
|
||||||
else if($allowOption == 'ego'){
|
else if($allowOption == 'ego'){
|
||||||
if(!$personal || $personal == 'Random'){
|
if(!$personal || $personal == 'Random'){
|
||||||
@@ -135,7 +168,7 @@ foreach(GameConst::$generalPoolAllowOption as $allowOption){
|
|||||||
}
|
}
|
||||||
$builder->setEgo($personal);
|
$builder->setEgo($personal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$userNick = $ownerInfo['name'];
|
$userNick = $ownerInfo['name'];
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ include "func.php";
|
|||||||
WebUtil::requireAJAX();
|
WebUtil::requireAJAX();
|
||||||
|
|
||||||
$pick = Util::getPost('pick');
|
$pick = Util::getPost('pick');
|
||||||
|
$pictureSource = Util::getPost('picture_source', 'string', 'selected');
|
||||||
|
|
||||||
if(!$pick){
|
if(!$pick){
|
||||||
Json::die([
|
Json::die([
|
||||||
@@ -33,13 +34,22 @@ if(!$generalID){
|
|||||||
}
|
}
|
||||||
|
|
||||||
list(
|
list(
|
||||||
$year,
|
$year,
|
||||||
$month,
|
$month,
|
||||||
$startYear,
|
$startYear,
|
||||||
$maxgeneral,
|
$maxgeneral,
|
||||||
$npcmode,
|
$npcmode,
|
||||||
$turnterm
|
$turnterm,
|
||||||
) = $gameStor->getValuesAsArray(['year', 'month', 'startyear', 'maxgeneral', 'npcmode', 'turnterm']);
|
$showImgLevel
|
||||||
|
) = $gameStor->getValuesAsArray([
|
||||||
|
'year',
|
||||||
|
'month',
|
||||||
|
'startyear',
|
||||||
|
'maxgeneral',
|
||||||
|
'npcmode',
|
||||||
|
'turnterm',
|
||||||
|
'show_img_level',
|
||||||
|
]);
|
||||||
|
|
||||||
if($npcmode!=2){
|
if($npcmode!=2){
|
||||||
Json::die([
|
Json::die([
|
||||||
@@ -56,7 +66,10 @@ if(!$info){
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$ownerInfo = RootDB::db()->queryFirstRow('SELECT `name`,`picture`,`imgsvr` FROM member WHERE `NO`=%i',$userID);
|
$ownerInfo = RootDB::db()->queryFirstRow(
|
||||||
|
'SELECT `name`,`picture`,`imgsvr`,`grade` FROM member WHERE `NO`=%i',
|
||||||
|
$userID
|
||||||
|
);
|
||||||
if(!$ownerInfo){
|
if(!$ownerInfo){
|
||||||
Json::die([
|
Json::die([
|
||||||
'result'=>false,
|
'result'=>false,
|
||||||
@@ -65,6 +78,27 @@ if(!$ownerInfo){
|
|||||||
}
|
}
|
||||||
|
|
||||||
$info = Json::decode($info);
|
$info = Json::decode($info);
|
||||||
|
$isCentennialAllStar = CentennialAllStarGrowthService::isActive();
|
||||||
|
if ($isCentennialAllStar) {
|
||||||
|
if (!in_array($pictureSource, ['current', 'own', 'selected'], true)) {
|
||||||
|
Json::die([
|
||||||
|
'result' => false,
|
||||||
|
'reason' => '올바르지 않은 전콘 선택입니다.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if ($pictureSource === 'own') {
|
||||||
|
$canUseOwnPicture = in_array('picture', GameConst::$generalPoolAllowOption, true)
|
||||||
|
&& $showImgLevel >= 1
|
||||||
|
&& $ownerInfo['grade'] >= 1
|
||||||
|
&& $ownerInfo['picture'] !== '';
|
||||||
|
if (!$canUseOwnPicture) {
|
||||||
|
Json::die([
|
||||||
|
'result' => false,
|
||||||
|
'reason' => '사용할 수 있는 내 전콘이 없습니다.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
$generalObj = General::createObjFromDB($generalID);
|
$generalObj = General::createObjFromDB($generalID);
|
||||||
@@ -102,37 +136,45 @@ $db->update('select_pool',[
|
|||||||
'reserved_until'=>null,
|
'reserved_until'=>null,
|
||||||
], '(owner=%i or reserved_until < %s) AND general_id is NULL', $userID, $now);
|
], '(owner=%i or reserved_until < %s) AND general_id is NULL', $userID, $now);
|
||||||
|
|
||||||
$isCentennialAllStar = CentennialAllStarGrowthService::isActive();
|
if ($isCentennialAllStar) {
|
||||||
if ($isCentennialAllStar) {
|
CentennialAllStarGrowthService::prepareLegacyUserReselection($generalObj);
|
||||||
CentennialAllStarGrowthService::applyTarget($generalObj, $info, [
|
CentennialAllStarGrowthService::applyTarget($generalObj, $info, [
|
||||||
'startyear' => $startYear,
|
'startyear' => $startYear,
|
||||||
'year' => $year,
|
'year' => $year,
|
||||||
'month' => $month,
|
'month' => $month,
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
if(key_exists('leadership', $info)){
|
if(key_exists('leadership', $info)){
|
||||||
$generalObj->updateVar('leadership', $info['leadership']);
|
$generalObj->updateVar('leadership', $info['leadership']);
|
||||||
$generalObj->updateVar('strength', $info['strength']);
|
$generalObj->updateVar('strength', $info['strength']);
|
||||||
$generalObj->updateVar('intel', $info['intel']);
|
$generalObj->updateVar('intel', $info['intel']);
|
||||||
}
|
}
|
||||||
if(key_exists('dex', $info)){
|
if(key_exists('dex', $info)){
|
||||||
$generalObj->updateVar('dex1', $info['dex'][0]);
|
$generalObj->updateVar('dex1', $info['dex'][0]);
|
||||||
$generalObj->updateVar('dex2', $info['dex'][1]);
|
$generalObj->updateVar('dex2', $info['dex'][1]);
|
||||||
$generalObj->updateVar('dex3', $info['dex'][2]);
|
$generalObj->updateVar('dex3', $info['dex'][2]);
|
||||||
$generalObj->updateVar('dex4', $info['dex'][3]);
|
$generalObj->updateVar('dex4', $info['dex'][3]);
|
||||||
$generalObj->updateVar('dex5', $info['dex'][4]);
|
$generalObj->updateVar('dex5', $info['dex'][4]);
|
||||||
}
|
}
|
||||||
if(key_exists('ego', $info)){
|
if(key_exists('ego', $info)){
|
||||||
$generalObj->updateVar('personal', $info['ego']);
|
$generalObj->updateVar('personal', $info['ego']);
|
||||||
}
|
}
|
||||||
if(key_exists('specialDomestic', $info)){
|
if(key_exists('specialDomestic', $info)){
|
||||||
$generalObj->updateVar('special', $info['specialDomestic']);
|
$generalObj->updateVar('special', $info['specialDomestic']);
|
||||||
}
|
}
|
||||||
if(key_exists('specialWar', $info)){
|
if(key_exists('specialWar', $info)){
|
||||||
$generalObj->updateVar('special2', $info['specialWar']);
|
$generalObj->updateVar('special2', $info['specialWar']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(key_exists('picture', $info)){
|
if ($isCentennialAllStar) {
|
||||||
|
if ($pictureSource === 'own') {
|
||||||
|
$generalObj->updateVar('imgsvr', $ownerInfo['imgsvr']);
|
||||||
|
$generalObj->updateVar('picture', $ownerInfo['picture']);
|
||||||
|
} elseif ($pictureSource === 'selected' && key_exists('picture', $info)) {
|
||||||
|
$generalObj->updateVar('imgsvr', $info['imgsvr']);
|
||||||
|
$generalObj->updateVar('picture', $info['picture']);
|
||||||
|
}
|
||||||
|
} elseif(key_exists('picture', $info)){
|
||||||
$generalObj->updateVar('imgsvr', $info['imgsvr']);
|
$generalObj->updateVar('imgsvr', $info['imgsvr']);
|
||||||
$generalObj->updateVar('picture', $info['picture']);
|
$generalObj->updateVar('picture', $info['picture']);
|
||||||
}
|
}
|
||||||
@@ -158,4 +200,4 @@ $generalObj->applyDB($db);
|
|||||||
Json::die([
|
Json::die([
|
||||||
'result'=>true,
|
'result'=>true,
|
||||||
'reason'=>'success'
|
'reason'=>'success'
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -19,15 +19,24 @@ final class CentennialAllStarGrowthService
|
|||||||
return GameConst::$targetGeneralPool === self::POOL_CLASS;
|
return GameConst::$targetGeneralPool === self::POOL_CLASS;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function initialAux(array $targetInfo): array
|
public static function initialAux(array $targetInfo, ?array $userInitialStats = null): array
|
||||||
{
|
{
|
||||||
|
$granted = array_fill_keys(array_merge(self::STAT_KEYS, self::DEX_KEYS), 0);
|
||||||
|
if ($userInitialStats !== null) {
|
||||||
|
foreach (self::STAT_KEYS as $key) {
|
||||||
|
$initial = (int) ($userInitialStats[$key] ?? GameConst::$defaultStatMin);
|
||||||
|
$granted[$key] = max(0, $initial - min($initial, GameConst::$defaultStatMin));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'targetId' => (string) ($targetInfo['uniqueName'] ?? ''),
|
'targetId' => (string) ($targetInfo['uniqueName'] ?? ''),
|
||||||
'granted' => array_fill_keys(array_merge(self::STAT_KEYS, self::DEX_KEYS), 0),
|
'granted' => $granted,
|
||||||
'progressMonth' => -1,
|
'progressMonth' => -1,
|
||||||
'milestone' => 0,
|
'milestone' => 0,
|
||||||
'naturalSpecialDomestic' => null,
|
'naturalSpecialDomestic' => null,
|
||||||
'eventSpecialDomestic' => null,
|
'eventSpecialDomestic' => null,
|
||||||
|
'userInitialStats' => $userInitialStats,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,6 +45,130 @@ final class CentennialAllStarGrowthService
|
|||||||
$builder->setAuxVar(self::AUX_KEY, self::initialAux($targetInfo));
|
$builder->setAuxVar(self::AUX_KEY, self::initialAux($targetInfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds an ordinary-user stat total while preserving the selected
|
||||||
|
* candidate's relative strengths as closely as integer stats allow.
|
||||||
|
*
|
||||||
|
* @return array{leadership:int,strength:int,intel:int}
|
||||||
|
*/
|
||||||
|
public static function calculateUserInitialStats(array $targetInfo): array
|
||||||
|
{
|
||||||
|
$targets = [];
|
||||||
|
$bases = [];
|
||||||
|
foreach (self::STAT_KEYS as $key) {
|
||||||
|
$target = min(
|
||||||
|
GameConst::$defaultStatMax,
|
||||||
|
max(0, (int) ($targetInfo[$key] ?? 0))
|
||||||
|
);
|
||||||
|
$targets[$key] = $target;
|
||||||
|
$bases[$key] = min($target, GameConst::$defaultStatMin);
|
||||||
|
}
|
||||||
|
|
||||||
|
$targetTotal = array_sum($targets);
|
||||||
|
$desiredTotal = min(GameConst::$defaultStatTotal, $targetTotal);
|
||||||
|
$baseTotal = array_sum($bases);
|
||||||
|
$capacityTotal = $targetTotal - $baseTotal;
|
||||||
|
if ($capacityTotal <= 0 || $desiredTotal <= $baseTotal) {
|
||||||
|
return $bases;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ratio = ($desiredTotal - $baseTotal) / $capacityTotal;
|
||||||
|
$result = [];
|
||||||
|
$fractions = [];
|
||||||
|
foreach (self::STAT_KEYS as $idx => $key) {
|
||||||
|
$raw = $bases[$key] + ($targets[$key] - $bases[$key]) * $ratio;
|
||||||
|
$result[$key] = (int) floor($raw);
|
||||||
|
$fractions[] = [
|
||||||
|
'key' => $key,
|
||||||
|
'fraction' => $raw - $result[$key],
|
||||||
|
'order' => $idx,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
usort($fractions, static function (array $lhs, array $rhs): int {
|
||||||
|
$fractionOrder = $rhs['fraction'] <=> $lhs['fraction'];
|
||||||
|
return $fractionOrder !== 0 ? $fractionOrder : $lhs['order'] <=> $rhs['order'];
|
||||||
|
});
|
||||||
|
$remainder = $desiredTotal - array_sum($result);
|
||||||
|
foreach ($fractions as $fraction) {
|
||||||
|
if ($remainder <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$key = $fraction['key'];
|
||||||
|
if ($result[$key] >= $targets[$key]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$result[$key]++;
|
||||||
|
$remainder--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function prepareInitialUser(
|
||||||
|
GeneralBuilder $builder,
|
||||||
|
array $targetInfo
|
||||||
|
): void {
|
||||||
|
$initialStats = self::calculateUserInitialStats($targetInfo);
|
||||||
|
$builder->setStat(
|
||||||
|
$initialStats['leadership'],
|
||||||
|
$initialStats['strength'],
|
||||||
|
$initialStats['intel']
|
||||||
|
);
|
||||||
|
$builder->setAuxVar(
|
||||||
|
self::AUX_KEY,
|
||||||
|
self::initialAux($targetInfo, $initialStats)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Old 100th-season characters did not distinguish their form-entered
|
||||||
|
* initial stats from organic growth. Before their first reselection, treat
|
||||||
|
* the ordinary creation range as the replaceable initial allocation.
|
||||||
|
*/
|
||||||
|
public static function prepareLegacyUserReselection(General $general): void
|
||||||
|
{
|
||||||
|
$aux = $general->getAuxVar(self::AUX_KEY);
|
||||||
|
if (!is_array($aux) || is_array($aux['userInitialStats'] ?? null)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$granted = is_array($aux['granted'] ?? null)
|
||||||
|
? $aux['granted']
|
||||||
|
: array_fill_keys(array_merge(self::STAT_KEYS, self::DEX_KEYS), 0);
|
||||||
|
$legacyInitialStats = [];
|
||||||
|
foreach (self::STAT_KEYS as $key) {
|
||||||
|
$current = (int) $general->getVar($key);
|
||||||
|
$granted[$key] = self::calculateLegacyUserGrant(
|
||||||
|
$current,
|
||||||
|
(int) ($granted[$key] ?? 0)
|
||||||
|
);
|
||||||
|
$beforeEventGrant = max(
|
||||||
|
0,
|
||||||
|
$current - max(0, (int) ($aux['granted'][$key] ?? 0))
|
||||||
|
);
|
||||||
|
$legacyInitialStats[$key] = min(
|
||||||
|
$beforeEventGrant,
|
||||||
|
GameConst::$defaultStatMax
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$aux['granted'] = $granted;
|
||||||
|
$aux['userInitialStats'] = $legacyInitialStats;
|
||||||
|
$general->setAuxVar(self::AUX_KEY, $aux);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function calculateLegacyUserGrant(int $current, int $eventGrant): int
|
||||||
|
{
|
||||||
|
$eventGrant = max(0, $eventGrant);
|
||||||
|
$beforeEventGrant = max(0, $current - $eventGrant);
|
||||||
|
$replaceableInitialGrant = max(
|
||||||
|
0,
|
||||||
|
min($beforeEventGrant, GameConst::$defaultStatMax)
|
||||||
|
- min($beforeEventGrant, GameConst::$defaultStatMin)
|
||||||
|
);
|
||||||
|
return $eventGrant + $replaceableInitialGrant;
|
||||||
|
}
|
||||||
|
|
||||||
public static function calculateProgress(
|
public static function calculateProgress(
|
||||||
int $startYear,
|
int $startYear,
|
||||||
int $year,
|
int $year,
|
||||||
@@ -87,6 +220,10 @@ final class CentennialAllStarGrowthService
|
|||||||
? $aux['granted']
|
? $aux['granted']
|
||||||
: array_fill_keys(array_merge(self::STAT_KEYS, self::DEX_KEYS), 0);
|
: array_fill_keys(array_merge(self::STAT_KEYS, self::DEX_KEYS), 0);
|
||||||
$targetChanged = ($aux['targetId'] ?? '') !== $targetId;
|
$targetChanged = ($aux['targetId'] ?? '') !== $targetId;
|
||||||
|
$isUserTarget = is_array($aux['userInitialStats'] ?? null);
|
||||||
|
$nextUserInitialStats = $targetChanged && $isUserTarget
|
||||||
|
? self::calculateUserInitialStats($targetInfo)
|
||||||
|
: ($aux['userInitialStats'] ?? null);
|
||||||
$changed = false;
|
$changed = false;
|
||||||
|
|
||||||
foreach (self::STAT_KEYS as $key) {
|
foreach (self::STAT_KEYS as $key) {
|
||||||
@@ -99,6 +236,9 @@ final class CentennialAllStarGrowthService
|
|||||||
GameConst::$defaultStatMin,
|
GameConst::$defaultStatMin,
|
||||||
$progress
|
$progress
|
||||||
);
|
);
|
||||||
|
if ($nextUserInitialStats !== null) {
|
||||||
|
$floor = max($floor, (int) ($nextUserInitialStats[$key] ?? 0));
|
||||||
|
}
|
||||||
$current = (int) $general->getVar($key);
|
$current = (int) $general->getVar($key);
|
||||||
if ($targetChanged) {
|
if ($targetChanged) {
|
||||||
$result = CentennialAllStarGrowth::replaceTarget(
|
$result = CentennialAllStarGrowth::replaceTarget(
|
||||||
@@ -178,6 +318,7 @@ final class CentennialAllStarGrowthService
|
|||||||
$aux['granted'] = $granted;
|
$aux['granted'] = $granted;
|
||||||
$aux['progressMonth'] = max((int) ($aux['progressMonth'] ?? -1), $progressMonth);
|
$aux['progressMonth'] = max((int) ($aux['progressMonth'] ?? -1), $progressMonth);
|
||||||
$aux['milestone'] = max($previousMilestone, $milestone);
|
$aux['milestone'] = max($previousMilestone, $milestone);
|
||||||
|
$aux['userInitialStats'] = $nextUserInitialStats;
|
||||||
$general->setAuxVar(self::AUX_KEY, $aux);
|
$general->setAuxVar(self::AUX_KEY, $aux);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|||||||
@@ -19,8 +19,16 @@ if ($admin['npcmode'] != 2) {
|
|||||||
|
|
||||||
$member = RootDB::db()->queryFirstRow("SELECT no,name,picture,imgsvr,grade from member where no= %i", $userID);
|
$member = RootDB::db()->queryFirstRow("SELECT no,name,picture,imgsvr,grade from member where no= %i", $userID);
|
||||||
|
|
||||||
$generalID = $db->queryFirstField('SELECT no FROM general WHERE owner = %i', $userID);
|
$currentGeneral = $db->queryFirstRow(
|
||||||
|
'SELECT no,picture,imgsvr FROM general WHERE owner = %i',
|
||||||
|
$userID
|
||||||
|
);
|
||||||
|
$generalID = $currentGeneral['no'] ?? null;
|
||||||
$gencount = $db->queryFirstField('SELECT count(`no`) FROM general WHERE npc<2');
|
$gencount = $db->queryFirstField('SELECT count(`no`) FROM general WHERE npc<2');
|
||||||
|
$isCentennialAllStar = CentennialAllStarGrowthService::isActive();
|
||||||
|
$canUseOwnPicture = $admin['show_img_level'] >= 1
|
||||||
|
&& $member['grade'] >= 1
|
||||||
|
&& $member['picture'] != "";
|
||||||
|
|
||||||
$nationList = $db->query('SELECT nation,`name`,color,scout FROM nation');
|
$nationList = $db->query('SELECT nation,`name`,color,scout FROM nation');
|
||||||
shuffle($nationList);
|
shuffle($nationList);
|
||||||
@@ -54,6 +62,7 @@ foreach (getCharacterList(false) as $id => [$name, $info]) {
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
var hasGeneralID = <?= $generalID === null ? 'false' : 'true' ?>;
|
var hasGeneralID = <?= $generalID === null ? 'false' : 'true' ?>;
|
||||||
|
var isCentennialAllStar = <?= $isCentennialAllStar ? 'true' : 'false' ?>;
|
||||||
var defaultStatTotal = <?= GameConst::$defaultStatTotal ?>;
|
var defaultStatTotal = <?= GameConst::$defaultStatTotal ?>;
|
||||||
var defaultStatMin = <?= GameConst::$defaultStatMin ?>;
|
var defaultStatMin = <?= GameConst::$defaultStatMin ?>;
|
||||||
var defaultStatMax = <?= GameConst::$defaultStatMax ?>;
|
var defaultStatMax = <?= GameConst::$defaultStatMax ?>;
|
||||||
@@ -104,6 +113,27 @@ if ($gencount >= $admin['maxgeneral']) {
|
|||||||
<small id="valid_until">(<span id="valid_until_text"></span>까지 유효)</small><small id="outdate_token">- 만료 -</small><br>
|
<small id="valid_until">(<span id="valid_until_text"></span>까지 유효)</small><small id="outdate_token">- 만료 -</small><br>
|
||||||
<form class="card_holder">
|
<form class="card_holder">
|
||||||
</form>
|
</form>
|
||||||
|
<?php if ($isCentennialAllStar && $generalID !== null) : ?>
|
||||||
|
<div id="reselect_picture_plate" class="picture_choice">
|
||||||
|
<strong>변경 후 전콘</strong>
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="reselect_picture_source" value="selected" checked>
|
||||||
|
새로 선택할 장수 전콘
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="reselect_picture_source" value="current">
|
||||||
|
<img width="32" height="32" src="<?= GetImageURL($currentGeneral['imgsvr']) ?>/<?= $currentGeneral['picture'] ?>" border="0">
|
||||||
|
현재 장수 전콘
|
||||||
|
</label>
|
||||||
|
<?php if ($canUseOwnPicture) : ?>
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="reselect_picture_source" value="own">
|
||||||
|
<img width="32" height="32" src="<?= GetImageURL($member['imgsvr']) ?>/<?= $member['picture'] ?>" border="0">
|
||||||
|
내 원래 전콘
|
||||||
|
</label>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -117,7 +147,28 @@ if ($gencount >= $admin['maxgeneral']) {
|
|||||||
<form id='custom_form'>
|
<form id='custom_form'>
|
||||||
<table class='tb_layout' style='width:100%;text-align:left;'>
|
<table class='tb_layout' style='width:100%;text-align:left;'>
|
||||||
<?php
|
<?php
|
||||||
if ($admin['show_img_level'] >= 1 && $member['grade'] >= 1 && $member['picture'] != "") {
|
if ($isCentennialAllStar) {
|
||||||
|
echo "
|
||||||
|
<tr class='event_picture'>
|
||||||
|
<td align=right class='bg1'>전콘 선택</td>
|
||||||
|
<td colspan=2>
|
||||||
|
<label><input type=radio name=picture_source value=selected checked> 선택한 장수 전콘</label>
|
||||||
|
";
|
||||||
|
if ($canUseOwnPicture) {
|
||||||
|
$imageTemp = GetImageURL($member['imgsvr']);
|
||||||
|
echo "
|
||||||
|
<label>
|
||||||
|
<input type=radio name=picture_source value=own>
|
||||||
|
<img width='64' height='64' src='{$imageTemp}/{$member['picture']}' border='0'>
|
||||||
|
내 전콘
|
||||||
|
</label>
|
||||||
|
";
|
||||||
|
}
|
||||||
|
echo "
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
";
|
||||||
|
} elseif ($canUseOwnPicture) {
|
||||||
$imageTemp = GetImageURL($member['imgsvr']);
|
$imageTemp = GetImageURL($member['imgsvr']);
|
||||||
echo "
|
echo "
|
||||||
<tr class='custom_picture'>
|
<tr class='custom_picture'>
|
||||||
@@ -144,36 +195,42 @@ if ($gencount >= $admin['maxgeneral']) {
|
|||||||
</select> <span id="charInfoText"></span>
|
</select> <span id="charInfoText"></span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class='custom_stat'>
|
<?php if (!$isCentennialAllStar) : ?>
|
||||||
<td align=right class='bg1'>통솔</td>
|
<tr class='custom_stat'>
|
||||||
<td colspan=2><input type="number" name="leadership" id="leadership" value="50"></td>
|
<td align=right class='bg1'>통솔</td>
|
||||||
</tr>
|
<td colspan=2><input type="number" name="leadership" id="leadership" value="50"></td>
|
||||||
<tr class='custom_stat'>
|
</tr>
|
||||||
<td align=right class='bg1'>무력</td>
|
<tr class='custom_stat'>
|
||||||
<td colspan=2><input type="number" name="strength" id="strength" value="50"></td>
|
<td align=right class='bg1'>무력</td>
|
||||||
</tr>
|
<td colspan=2><input type="number" name="strength" id="strength" value="50"></td>
|
||||||
<tr class='custom_stat'>
|
</tr>
|
||||||
<td align=right class='bg1'>지력</td>
|
<tr class='custom_stat'>
|
||||||
<td colspan=2><input type="number" name="intel" id="intel" value="50"></td>
|
<td align=right class='bg1'>지력</td>
|
||||||
</tr>
|
<td colspan=2><input type="number" name="intel" id="intel" value="50"></td>
|
||||||
<tr class='custom_stat'>
|
</tr>
|
||||||
<td align=right class='bg1'>능력치 조정</td>
|
<tr class='custom_stat'>
|
||||||
<td colspan=2>
|
<td align=right class='bg1'>능력치 조정</td>
|
||||||
<input type=button value=랜덤형 onclick=abilityRand()>
|
<td colspan=2>
|
||||||
<input type=button value=통솔무력형 onclick=abilityLeadpow()>
|
<input type=button value=랜덤형 onclick=abilityRand()>
|
||||||
<input type=button value=통솔지력형 onclick=abilityLeadint()>
|
<input type=button value=통솔무력형 onclick=abilityLeadpow()>
|
||||||
<input type=button value=무력지력형 onclick=abilityPowint()>
|
<input type=button value=통솔지력형 onclick=abilityLeadint()>
|
||||||
</td>
|
<input type=button value=무력지력형 onclick=abilityPowint()>
|
||||||
</tr>
|
</td>
|
||||||
<tr class='custom_stat'>
|
</tr>
|
||||||
<td align=center colspan=3>
|
<tr class='custom_stat'>
|
||||||
<font color=orange>모든 능력치는 ( <?= GameConst::$defaultStatMin ?> <= 능력치 <=<?= GameConst::$defaultStatMax ?> ) 사이로 잡으셔야 합니다.<br>
|
<td align=center colspan=3>
|
||||||
그 외의 능력치는 가입되지 않습니다.</font>
|
<font color=orange>모든 능력치는 ( <?= GameConst::$defaultStatMin ?> <= 능력치 <=<?= GameConst::$defaultStatMax ?> ) 사이로 잡으셔야 합니다.<br>
|
||||||
</td>
|
그 외의 능력치는 가입되지 않습니다.</font>
|
||||||
</tr>
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endif; ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td align=center colspan=3>
|
<td align=center colspan=3>
|
||||||
<span class='custom_stat'>능력치의 총합은 <?= GameConst::$defaultStatTotal ?>입니다. 가입후 0~10의 능력치 보너스를 받게 됩니다.<br></span>
|
<?php if ($isCentennialAllStar) : ?>
|
||||||
|
선택한 장수의 최종 능력치 비율을 반영한 약화 능력치로 시작합니다.<br>
|
||||||
|
<?php else : ?>
|
||||||
|
<span class='custom_stat'>능력치의 총합은 <?= GameConst::$defaultStatTotal ?>입니다. 가입후 0~10의 능력치 보너스를 받게 됩니다.<br></span>
|
||||||
|
<?php endif; ?>
|
||||||
임의의 도시에서 재야로 시작하며 건국과 임관은 게임 내에서 실행합니다.
|
임의의 도시에서 재야로 시작하며 건국과 임관은 게임 내에서 실행합니다.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -194,4 +251,4 @@ if ($gencount >= $admin['maxgeneral']) {
|
|||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ type CardItem = {
|
|||||||
leadership?: number,
|
leadership?: number,
|
||||||
strength?: number,
|
strength?: number,
|
||||||
intel?: number,
|
intel?: number,
|
||||||
|
initialLeadership?: number,
|
||||||
|
initialStrength?: number,
|
||||||
|
initialIntel?: number,
|
||||||
dex?: number[],
|
dex?: number[],
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,7 +45,8 @@ type GeneralPoolResponse = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
declare const characterInfo: Record<string, { name: string, info: string }>;
|
declare const characterInfo: Record<string, { name: string, info: string }>;
|
||||||
declare const hasGeneralID: number;
|
declare const hasGeneralID: number;
|
||||||
|
declare const isCentennialAllStar: boolean;
|
||||||
declare let currentGeneralInfo: CardItem | undefined;
|
declare let currentGeneralInfo: CardItem | undefined;
|
||||||
declare const cards: Record<string, CardItem>;
|
declare const cards: Record<string, CardItem>;
|
||||||
declare const validCustomOption: string[];
|
declare const validCustomOption: string[];
|
||||||
@@ -51,9 +55,13 @@ const templateGeneralCard = '<div class="general_card">\
|
|||||||
<h4 class="bg1 with_border"><%generalName%></h4>\
|
<h4 class="bg1 with_border"><%generalName%></h4>\
|
||||||
<h4><img src="<%iconPath%>" height=64 width=64></h4><p>\
|
<h4><img src="<%iconPath%>" height=64 width=64></h4><p>\
|
||||||
<%if(event100Growth){%><b>195년 최종 동조 목표</b><br><%}%>\
|
<%if(event100Growth){%><b>195년 최종 동조 목표</b><br><%}%>\
|
||||||
<%if(leadership){%>\
|
<%if(leadership){%>\
|
||||||
<%leadership%> / <%strength%> / <%intel%><br>\
|
<%leadership%> / <%strength%> / <%intel%><br>\
|
||||||
<%}%>\
|
<%}%>\
|
||||||
|
<%if(initialLeadership){%>\
|
||||||
|
<b>시작 능력치</b><br>\
|
||||||
|
<%initialLeadership%> / <%initialStrength%> / <%initialIntel%><br>\
|
||||||
|
<%}%>\
|
||||||
<%if(personalText){%><%personalText%><br><%}%>\
|
<%if(personalText){%><%personalText%><br><%}%>\
|
||||||
<%if(specialDomesticText||specialWarText){%>\
|
<%if(specialDomesticText||specialWarText){%>\
|
||||||
<%specialDomesticText%> / <%specialWarText%><br>\
|
<%specialDomesticText%> / <%specialWarText%><br>\
|
||||||
@@ -107,9 +115,12 @@ async function pickGeneral(this: HTMLElement, e: JQuery.Event) {
|
|||||||
url: 'j_update_picked_general.php',
|
url: 'j_update_picked_general.php',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
responseType: 'json',
|
responseType: 'json',
|
||||||
data: convertFormData({
|
data: convertFormData({
|
||||||
pick: unwrap_any<string>($btn.val())
|
pick: unwrap_any<string>($btn.val()),
|
||||||
})
|
picture_source: unwrap_any<string>(
|
||||||
|
$('input[name="reselect_picture_source"]:checked').val() ?? 'selected'
|
||||||
|
)
|
||||||
|
})
|
||||||
});
|
});
|
||||||
result = response.data;
|
result = response.data;
|
||||||
if (!result.result) {
|
if (!result.result) {
|
||||||
@@ -138,23 +149,25 @@ async function buildGeneral(e: JQuery.Event) {
|
|||||||
|
|
||||||
let result: InvalidResponse;
|
let result: InvalidResponse;
|
||||||
try {
|
try {
|
||||||
const response = await axios({
|
const formData: Record<string, string|number|boolean> = {
|
||||||
url: 'j_select_picked_general.php',
|
pick: unwrap(currentGeneralInfo).uniqueName,
|
||||||
method: 'post',
|
use_own_picture: $('#use_own_picture').is(':checked'),
|
||||||
responseType: 'json',
|
picture_source: unwrap_any<string>(
|
||||||
data: convertFormData({
|
$('input[name="picture_source"]:checked').val() ?? 'selected'
|
||||||
pick: unwrap(currentGeneralInfo).uniqueName,
|
),
|
||||||
use_own_picture: $('#use_own_picture').is(':checked'),
|
personal: unwrap_any<string>($('#selChar').val())
|
||||||
leadership: parseInt(unwrap_any<string>($('#leadership').val())),
|
};
|
||||||
strength: parseInt(unwrap_any<string>(
|
if (!isCentennialAllStar) {
|
||||||
$(unwrap(currentGeneralInfo).event100Growth ? '#strength' : '#leadership').val()
|
formData.leadership = parseInt(unwrap_any<string>($('#leadership').val()));
|
||||||
)),
|
formData.strength = parseInt(unwrap_any<string>($('#leadership').val()));
|
||||||
intel: parseInt(unwrap_any<string>(
|
formData.intel = parseInt(unwrap_any<string>($('#leadership').val()));
|
||||||
$(unwrap(currentGeneralInfo).event100Growth ? '#intel' : '#leadership').val()
|
}
|
||||||
)),
|
const response = await axios({
|
||||||
personal: unwrap_any<string>($('#selChar').val())
|
url: 'j_select_picked_general.php',
|
||||||
})
|
method: 'post',
|
||||||
})
|
responseType: 'json',
|
||||||
|
data: convertFormData(formData)
|
||||||
|
})
|
||||||
result = response.data;
|
result = response.data;
|
||||||
if (!result.result) {
|
if (!result.result) {
|
||||||
throw result.reason;
|
throw result.reason;
|
||||||
@@ -200,9 +213,12 @@ function printGenerals(value: GeneralPoolResponse) {
|
|||||||
|
|
||||||
const emptyCard = {
|
const emptyCard = {
|
||||||
'leadership': null,
|
'leadership': null,
|
||||||
'strength': null,
|
'strength': null,
|
||||||
'intel': null,
|
'intel': null,
|
||||||
'personalText': null,
|
'initialLeadership': null,
|
||||||
|
'initialStrength': null,
|
||||||
|
'initialIntel': null,
|
||||||
|
'personalText': null,
|
||||||
'specialDomesticText': null,
|
'specialDomesticText': null,
|
||||||
'specialWarText': null,
|
'specialWarText': null,
|
||||||
'dex': null
|
'dex': null
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ use PHPUnit\Framework\TestCase;
|
|||||||
use sammo\CentennialAllStarGrowth;
|
use sammo\CentennialAllStarGrowth;
|
||||||
use sammo\CentennialAllStarGrowthService;
|
use sammo\CentennialAllStarGrowthService;
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../hwe/sammo/ActionLogger.php';
|
||||||
|
require_once __DIR__ . '/../hwe/sammo/GameConstBase.php';
|
||||||
|
require_once __DIR__ . '/../hwe/d_setting/GameConst.php';
|
||||||
require_once __DIR__ . '/../hwe/sammo/CentennialAllStarGrowthService.php';
|
require_once __DIR__ . '/../hwe/sammo/CentennialAllStarGrowthService.php';
|
||||||
|
|
||||||
final class CentennialAllStarGrowthTest extends TestCase
|
final class CentennialAllStarGrowthTest extends TestCase
|
||||||
@@ -65,6 +68,77 @@ final class CentennialAllStarGrowthTest extends TestCase
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testUserInitialStatsPreserveCandidateShapeAtOrdinaryTotal(): void
|
||||||
|
{
|
||||||
|
$initial = CentennialAllStarGrowthService::calculateUserInitialStats([
|
||||||
|
'leadership' => 80,
|
||||||
|
'strength' => 70,
|
||||||
|
'intel' => 50,
|
||||||
|
]);
|
||||||
|
|
||||||
|
self::assertSame([
|
||||||
|
'leadership' => 65,
|
||||||
|
'strength' => 58,
|
||||||
|
'intel' => 42,
|
||||||
|
], $initial);
|
||||||
|
self::assertSame(165, array_sum($initial));
|
||||||
|
self::assertLessThanOrEqual(80, $initial['leadership']);
|
||||||
|
self::assertLessThanOrEqual(70, $initial['strength']);
|
||||||
|
self::assertLessThanOrEqual(50, $initial['intel']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUserInitialStatsDoNotRaiseCandidateBelowOrdinaryTotal(): void
|
||||||
|
{
|
||||||
|
self::assertSame([
|
||||||
|
'leadership' => 60,
|
||||||
|
'strength' => 45,
|
||||||
|
'intel' => 30,
|
||||||
|
], CentennialAllStarGrowthService::calculateUserInitialStats([
|
||||||
|
'leadership' => 60,
|
||||||
|
'strength' => 45,
|
||||||
|
'intel' => 30,
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testInitialUserGrantMakesInitialAllocationReplaceable(): void
|
||||||
|
{
|
||||||
|
$initial = CentennialAllStarGrowthService::calculateUserInitialStats([
|
||||||
|
'uniqueName' => 'A1000001',
|
||||||
|
'leadership' => 80,
|
||||||
|
'strength' => 70,
|
||||||
|
'intel' => 50,
|
||||||
|
]);
|
||||||
|
$aux = CentennialAllStarGrowthService::initialAux([
|
||||||
|
'uniqueName' => 'A1000001',
|
||||||
|
], $initial);
|
||||||
|
|
||||||
|
self::assertSame('A1000001', $aux['targetId']);
|
||||||
|
self::assertSame($initial, $aux['userInitialStats']);
|
||||||
|
self::assertSame(50, $aux['granted']['leadership']);
|
||||||
|
self::assertSame(43, $aux['granted']['strength']);
|
||||||
|
self::assertSame(27, $aux['granted']['intel']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLegacyInitialGrantKeepsOnlyGrowthBeyondCreationRange(): void
|
||||||
|
{
|
||||||
|
self::assertSame(
|
||||||
|
35,
|
||||||
|
CentennialAllStarGrowthService::calculateLegacyUserGrant(50, 0)
|
||||||
|
);
|
||||||
|
self::assertSame(
|
||||||
|
75,
|
||||||
|
CentennialAllStarGrowthService::calculateLegacyUserGrant(90, 40)
|
||||||
|
);
|
||||||
|
self::assertSame(
|
||||||
|
105,
|
||||||
|
CentennialAllStarGrowthService::calculateLegacyUserGrant(140, 40)
|
||||||
|
);
|
||||||
|
self::assertSame(
|
||||||
|
35,
|
||||||
|
140 - CentennialAllStarGrowthService::calculateLegacyUserGrant(140, 40)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public function testReselectDropsOldGrantAndKeepsOrganicGrowth(): void
|
public function testReselectDropsOldGrantAndKeepsOrganicGrowth(): void
|
||||||
{
|
{
|
||||||
self::assertSame(
|
self::assertSame(
|
||||||
|
|||||||
Reference in New Issue
Block a user