feat: 100기 NPC 동조 및 후보 추첨 기능 개선과 초기 능력치 계산 로직 추가
This commit is contained in:
@@ -51,6 +51,22 @@
|
||||
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 {
|
||||
display: none;
|
||||
}
|
||||
@@ -61,4 +77,4 @@
|
||||
|
||||
.custom_stat {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,13 @@ function sortTokens(&$tokens){
|
||||
}
|
||||
|
||||
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)){
|
||||
$class = buildGeneralSpecialDomesticClass($info['specialDomestic']);
|
||||
$info['specialDomesticName'] = $class->getName();
|
||||
@@ -91,4 +98,4 @@ Json::die([
|
||||
'result'=>true,
|
||||
'pick'=>$pick,
|
||||
'validUntil'=>$valid_until
|
||||
]);
|
||||
]);
|
||||
|
||||
@@ -21,6 +21,7 @@ $intel = Util::getPost(
|
||||
);
|
||||
$personal = Util::getPost('personal', 'string', null);
|
||||
$use_own_picture = Util::getPost('use_own_picture', 'bool', false);
|
||||
$pictureSource = Util::getPost('picture_source', 'string', 'selected');
|
||||
|
||||
|
||||
if(!$pick){
|
||||
@@ -69,16 +70,39 @@ if(!$selectInfo){
|
||||
}
|
||||
$selectInfo = Json::decode($selectInfo);
|
||||
|
||||
$ownerInfo = RootDB::db()->queryFirstRow('SELECT `name`,`picture`,`imgsvr` FROM member WHERE `NO`=%i',$userID);
|
||||
if(!$ownerInfo){
|
||||
$ownerInfo = RootDB::db()->queryFirstRow(
|
||||
'SELECT `name`,`picture`,`imgsvr`,`grade` FROM member WHERE `NO`=%i',
|
||||
$userID
|
||||
);
|
||||
if(!$ownerInfo){
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reason'=>'멤버 정보를 가져오지 못했습니다.'
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
$gencount = $db->queryFirstField('SELECT count(`no`) FROM general WHERE npc<2');
|
||||
]);
|
||||
}
|
||||
if ($isCentennialAllStar) {
|
||||
if (!in_array($pictureSource, ['selected', 'own'], true)) {
|
||||
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) {
|
||||
Json::die([
|
||||
@@ -102,9 +126,12 @@ if ($isCentennialAllStar) {
|
||||
}
|
||||
|
||||
$builder = $pickedGeneral->getGeneralBuilder();
|
||||
if ($isCentennialAllStar) {
|
||||
CentennialAllStarGrowthService::prepareInitialUser($builder, $selectInfo);
|
||||
}
|
||||
|
||||
foreach(GameConst::$generalPoolAllowOption as $allowOption){
|
||||
if($allowOption == 'stat'){
|
||||
if($allowOption == 'stat' && !$isCentennialAllStar){
|
||||
$leadership = Util::valueFit($leadership, GameConst::$defaultStatMin, GameConst::$defaultStatMax);
|
||||
$strength = Util::valueFit($strength, 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);
|
||||
}
|
||||
else if($allowOption == 'picture' && $use_own_picture){
|
||||
$builder->setPicture($ownerInfo['imgsvr'], $ownerInfo['picture']);
|
||||
else if(
|
||||
$allowOption == 'picture'
|
||||
&& (
|
||||
(!$isCentennialAllStar && $use_own_picture)
|
||||
|| ($isCentennialAllStar && $pictureSource === 'own')
|
||||
)
|
||||
){
|
||||
$builder->setPicture($ownerInfo['imgsvr'], $ownerInfo['picture']);
|
||||
}
|
||||
else if($allowOption == 'ego'){
|
||||
if(!$personal || $personal == 'Random'){
|
||||
@@ -135,7 +168,7 @@ foreach(GameConst::$generalPoolAllowOption as $allowOption){
|
||||
}
|
||||
$builder->setEgo($personal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$userNick = $ownerInfo['name'];
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ include "func.php";
|
||||
WebUtil::requireAJAX();
|
||||
|
||||
$pick = Util::getPost('pick');
|
||||
$pictureSource = Util::getPost('picture_source', 'string', 'selected');
|
||||
|
||||
if(!$pick){
|
||||
Json::die([
|
||||
@@ -33,13 +34,22 @@ if(!$generalID){
|
||||
}
|
||||
|
||||
list(
|
||||
$year,
|
||||
$month,
|
||||
$startYear,
|
||||
$maxgeneral,
|
||||
$npcmode,
|
||||
$turnterm
|
||||
) = $gameStor->getValuesAsArray(['year', 'month', 'startyear', 'maxgeneral', 'npcmode', 'turnterm']);
|
||||
$year,
|
||||
$month,
|
||||
$startYear,
|
||||
$maxgeneral,
|
||||
$npcmode,
|
||||
$turnterm,
|
||||
$showImgLevel
|
||||
) = $gameStor->getValuesAsArray([
|
||||
'year',
|
||||
'month',
|
||||
'startyear',
|
||||
'maxgeneral',
|
||||
'npcmode',
|
||||
'turnterm',
|
||||
'show_img_level',
|
||||
]);
|
||||
|
||||
if($npcmode!=2){
|
||||
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){
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
@@ -65,6 +78,27 @@ if(!$ownerInfo){
|
||||
}
|
||||
|
||||
$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);
|
||||
@@ -102,37 +136,45 @@ $db->update('select_pool',[
|
||||
'reserved_until'=>null,
|
||||
], '(owner=%i or reserved_until < %s) AND general_id is NULL', $userID, $now);
|
||||
|
||||
$isCentennialAllStar = CentennialAllStarGrowthService::isActive();
|
||||
if ($isCentennialAllStar) {
|
||||
CentennialAllStarGrowthService::applyTarget($generalObj, $info, [
|
||||
'startyear' => $startYear,
|
||||
'year' => $year,
|
||||
'month' => $month,
|
||||
]);
|
||||
} else {
|
||||
if(key_exists('leadership', $info)){
|
||||
$generalObj->updateVar('leadership', $info['leadership']);
|
||||
$generalObj->updateVar('strength', $info['strength']);
|
||||
$generalObj->updateVar('intel', $info['intel']);
|
||||
}
|
||||
if(key_exists('dex', $info)){
|
||||
$generalObj->updateVar('dex1', $info['dex'][0]);
|
||||
$generalObj->updateVar('dex2', $info['dex'][1]);
|
||||
$generalObj->updateVar('dex3', $info['dex'][2]);
|
||||
$generalObj->updateVar('dex4', $info['dex'][3]);
|
||||
$generalObj->updateVar('dex5', $info['dex'][4]);
|
||||
}
|
||||
if(key_exists('ego', $info)){
|
||||
$generalObj->updateVar('personal', $info['ego']);
|
||||
}
|
||||
if(key_exists('specialDomestic', $info)){
|
||||
$generalObj->updateVar('special', $info['specialDomestic']);
|
||||
}
|
||||
if(key_exists('specialWar', $info)){
|
||||
$generalObj->updateVar('special2', $info['specialWar']);
|
||||
}
|
||||
}
|
||||
if(key_exists('picture', $info)){
|
||||
if ($isCentennialAllStar) {
|
||||
CentennialAllStarGrowthService::prepareLegacyUserReselection($generalObj);
|
||||
CentennialAllStarGrowthService::applyTarget($generalObj, $info, [
|
||||
'startyear' => $startYear,
|
||||
'year' => $year,
|
||||
'month' => $month,
|
||||
]);
|
||||
} else {
|
||||
if(key_exists('leadership', $info)){
|
||||
$generalObj->updateVar('leadership', $info['leadership']);
|
||||
$generalObj->updateVar('strength', $info['strength']);
|
||||
$generalObj->updateVar('intel', $info['intel']);
|
||||
}
|
||||
if(key_exists('dex', $info)){
|
||||
$generalObj->updateVar('dex1', $info['dex'][0]);
|
||||
$generalObj->updateVar('dex2', $info['dex'][1]);
|
||||
$generalObj->updateVar('dex3', $info['dex'][2]);
|
||||
$generalObj->updateVar('dex4', $info['dex'][3]);
|
||||
$generalObj->updateVar('dex5', $info['dex'][4]);
|
||||
}
|
||||
if(key_exists('ego', $info)){
|
||||
$generalObj->updateVar('personal', $info['ego']);
|
||||
}
|
||||
if(key_exists('specialDomestic', $info)){
|
||||
$generalObj->updateVar('special', $info['specialDomestic']);
|
||||
}
|
||||
if(key_exists('specialWar', $info)){
|
||||
$generalObj->updateVar('special2', $info['specialWar']);
|
||||
}
|
||||
}
|
||||
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('picture', $info['picture']);
|
||||
}
|
||||
@@ -158,4 +200,4 @@ $generalObj->applyDB($db);
|
||||
Json::die([
|
||||
'result'=>true,
|
||||
'reason'=>'success'
|
||||
]);
|
||||
]);
|
||||
|
||||
@@ -19,15 +19,24 @@ final class CentennialAllStarGrowthService
|
||||
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 [
|
||||
'targetId' => (string) ($targetInfo['uniqueName'] ?? ''),
|
||||
'granted' => array_fill_keys(array_merge(self::STAT_KEYS, self::DEX_KEYS), 0),
|
||||
'granted' => $granted,
|
||||
'progressMonth' => -1,
|
||||
'milestone' => 0,
|
||||
'naturalSpecialDomestic' => null,
|
||||
'eventSpecialDomestic' => null,
|
||||
'userInitialStats' => $userInitialStats,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -36,6 +45,130 @@ final class CentennialAllStarGrowthService
|
||||
$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(
|
||||
int $startYear,
|
||||
int $year,
|
||||
@@ -87,6 +220,10 @@ final class CentennialAllStarGrowthService
|
||||
? $aux['granted']
|
||||
: array_fill_keys(array_merge(self::STAT_KEYS, self::DEX_KEYS), 0);
|
||||
$targetChanged = ($aux['targetId'] ?? '') !== $targetId;
|
||||
$isUserTarget = is_array($aux['userInitialStats'] ?? null);
|
||||
$nextUserInitialStats = $targetChanged && $isUserTarget
|
||||
? self::calculateUserInitialStats($targetInfo)
|
||||
: ($aux['userInitialStats'] ?? null);
|
||||
$changed = false;
|
||||
|
||||
foreach (self::STAT_KEYS as $key) {
|
||||
@@ -99,6 +236,9 @@ final class CentennialAllStarGrowthService
|
||||
GameConst::$defaultStatMin,
|
||||
$progress
|
||||
);
|
||||
if ($nextUserInitialStats !== null) {
|
||||
$floor = max($floor, (int) ($nextUserInitialStats[$key] ?? 0));
|
||||
}
|
||||
$current = (int) $general->getVar($key);
|
||||
if ($targetChanged) {
|
||||
$result = CentennialAllStarGrowth::replaceTarget(
|
||||
@@ -178,6 +318,7 @@ final class CentennialAllStarGrowthService
|
||||
$aux['granted'] = $granted;
|
||||
$aux['progressMonth'] = max((int) ($aux['progressMonth'] ?? -1), $progressMonth);
|
||||
$aux['milestone'] = max($previousMilestone, $milestone);
|
||||
$aux['userInitialStats'] = $nextUserInitialStats;
|
||||
$general->setAuxVar(self::AUX_KEY, $aux);
|
||||
|
||||
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);
|
||||
|
||||
$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');
|
||||
$isCentennialAllStar = CentennialAllStarGrowthService::isActive();
|
||||
$canUseOwnPicture = $admin['show_img_level'] >= 1
|
||||
&& $member['grade'] >= 1
|
||||
&& $member['picture'] != "";
|
||||
|
||||
$nationList = $db->query('SELECT nation,`name`,color,scout FROM nation');
|
||||
shuffle($nationList);
|
||||
@@ -54,6 +62,7 @@ foreach (getCharacterList(false) as $id => [$name, $info]) {
|
||||
|
||||
<script>
|
||||
var hasGeneralID = <?= $generalID === null ? 'false' : 'true' ?>;
|
||||
var isCentennialAllStar = <?= $isCentennialAllStar ? 'true' : 'false' ?>;
|
||||
var defaultStatTotal = <?= GameConst::$defaultStatTotal ?>;
|
||||
var defaultStatMin = <?= GameConst::$defaultStatMin ?>;
|
||||
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>
|
||||
<form class="card_holder">
|
||||
</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>
|
||||
|
||||
@@ -117,7 +147,28 @@ if ($gencount >= $admin['maxgeneral']) {
|
||||
<form id='custom_form'>
|
||||
<table class='tb_layout' style='width:100%;text-align:left;'>
|
||||
<?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']);
|
||||
echo "
|
||||
<tr class='custom_picture'>
|
||||
@@ -144,36 +195,42 @@ if ($gencount >= $admin['maxgeneral']) {
|
||||
</select> <span id="charInfoText"></span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class='custom_stat'>
|
||||
<td align=right class='bg1'>통솔</td>
|
||||
<td colspan=2><input type="number" name="leadership" id="leadership" value="50"></td>
|
||||
</tr>
|
||||
<tr class='custom_stat'>
|
||||
<td align=right class='bg1'>무력</td>
|
||||
<td colspan=2><input type="number" name="strength" id="strength" value="50"></td>
|
||||
</tr>
|
||||
<tr class='custom_stat'>
|
||||
<td align=right class='bg1'>지력</td>
|
||||
<td colspan=2><input type="number" name="intel" id="intel" value="50"></td>
|
||||
</tr>
|
||||
<tr class='custom_stat'>
|
||||
<td align=right class='bg1'>능력치 조정</td>
|
||||
<td colspan=2>
|
||||
<input type=button value=랜덤형 onclick=abilityRand()>
|
||||
<input type=button value=통솔무력형 onclick=abilityLeadpow()>
|
||||
<input type=button value=통솔지력형 onclick=abilityLeadint()>
|
||||
<input type=button value=무력지력형 onclick=abilityPowint()>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class='custom_stat'>
|
||||
<td align=center colspan=3>
|
||||
<font color=orange>모든 능력치는 ( <?= GameConst::$defaultStatMin ?> <= 능력치 <=<?= GameConst::$defaultStatMax ?> ) 사이로 잡으셔야 합니다.<br>
|
||||
그 외의 능력치는 가입되지 않습니다.</font>
|
||||
</td>
|
||||
</tr>
|
||||
<?php if (!$isCentennialAllStar) : ?>
|
||||
<tr class='custom_stat'>
|
||||
<td align=right class='bg1'>통솔</td>
|
||||
<td colspan=2><input type="number" name="leadership" id="leadership" value="50"></td>
|
||||
</tr>
|
||||
<tr class='custom_stat'>
|
||||
<td align=right class='bg1'>무력</td>
|
||||
<td colspan=2><input type="number" name="strength" id="strength" value="50"></td>
|
||||
</tr>
|
||||
<tr class='custom_stat'>
|
||||
<td align=right class='bg1'>지력</td>
|
||||
<td colspan=2><input type="number" name="intel" id="intel" value="50"></td>
|
||||
</tr>
|
||||
<tr class='custom_stat'>
|
||||
<td align=right class='bg1'>능력치 조정</td>
|
||||
<td colspan=2>
|
||||
<input type=button value=랜덤형 onclick=abilityRand()>
|
||||
<input type=button value=통솔무력형 onclick=abilityLeadpow()>
|
||||
<input type=button value=통솔지력형 onclick=abilityLeadint()>
|
||||
<input type=button value=무력지력형 onclick=abilityPowint()>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class='custom_stat'>
|
||||
<td align=center colspan=3>
|
||||
<font color=orange>모든 능력치는 ( <?= GameConst::$defaultStatMin ?> <= 능력치 <=<?= GameConst::$defaultStatMax ?> ) 사이로 잡으셔야 합니다.<br>
|
||||
그 외의 능력치는 가입되지 않습니다.</font>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
<tr>
|
||||
<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>
|
||||
</tr>
|
||||
@@ -194,4 +251,4 @@ if ($gencount >= $admin['maxgeneral']) {
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -32,6 +32,9 @@ type CardItem = {
|
||||
leadership?: number,
|
||||
strength?: number,
|
||||
intel?: number,
|
||||
initialLeadership?: number,
|
||||
initialStrength?: number,
|
||||
initialIntel?: number,
|
||||
dex?: number[],
|
||||
}
|
||||
|
||||
@@ -42,7 +45,8 @@ type GeneralPoolResponse = {
|
||||
}
|
||||
|
||||
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 const cards: Record<string, CardItem>;
|
||||
declare const validCustomOption: string[];
|
||||
@@ -51,9 +55,13 @@ const templateGeneralCard = '<div class="general_card">\
|
||||
<h4 class="bg1 with_border"><%generalName%></h4>\
|
||||
<h4><img src="<%iconPath%>" height=64 width=64></h4><p>\
|
||||
<%if(event100Growth){%><b>195년 최종 동조 목표</b><br><%}%>\
|
||||
<%if(leadership){%>\
|
||||
<%leadership%> / <%strength%> / <%intel%><br>\
|
||||
<%}%>\
|
||||
<%if(leadership){%>\
|
||||
<%leadership%> / <%strength%> / <%intel%><br>\
|
||||
<%}%>\
|
||||
<%if(initialLeadership){%>\
|
||||
<b>시작 능력치</b><br>\
|
||||
<%initialLeadership%> / <%initialStrength%> / <%initialIntel%><br>\
|
||||
<%}%>\
|
||||
<%if(personalText){%><%personalText%><br><%}%>\
|
||||
<%if(specialDomesticText||specialWarText){%>\
|
||||
<%specialDomesticText%> / <%specialWarText%><br>\
|
||||
@@ -107,9 +115,12 @@ async function pickGeneral(this: HTMLElement, e: JQuery.Event) {
|
||||
url: 'j_update_picked_general.php',
|
||||
method: 'post',
|
||||
responseType: 'json',
|
||||
data: convertFormData({
|
||||
pick: unwrap_any<string>($btn.val())
|
||||
})
|
||||
data: convertFormData({
|
||||
pick: unwrap_any<string>($btn.val()),
|
||||
picture_source: unwrap_any<string>(
|
||||
$('input[name="reselect_picture_source"]:checked').val() ?? 'selected'
|
||||
)
|
||||
})
|
||||
});
|
||||
result = response.data;
|
||||
if (!result.result) {
|
||||
@@ -138,23 +149,25 @@ async function buildGeneral(e: JQuery.Event) {
|
||||
|
||||
let result: InvalidResponse;
|
||||
try {
|
||||
const response = await axios({
|
||||
url: 'j_select_picked_general.php',
|
||||
method: 'post',
|
||||
responseType: 'json',
|
||||
data: convertFormData({
|
||||
pick: unwrap(currentGeneralInfo).uniqueName,
|
||||
use_own_picture: $('#use_own_picture').is(':checked'),
|
||||
leadership: parseInt(unwrap_any<string>($('#leadership').val())),
|
||||
strength: parseInt(unwrap_any<string>(
|
||||
$(unwrap(currentGeneralInfo).event100Growth ? '#strength' : '#leadership').val()
|
||||
)),
|
||||
intel: parseInt(unwrap_any<string>(
|
||||
$(unwrap(currentGeneralInfo).event100Growth ? '#intel' : '#leadership').val()
|
||||
)),
|
||||
personal: unwrap_any<string>($('#selChar').val())
|
||||
})
|
||||
})
|
||||
const formData: Record<string, string|number|boolean> = {
|
||||
pick: unwrap(currentGeneralInfo).uniqueName,
|
||||
use_own_picture: $('#use_own_picture').is(':checked'),
|
||||
picture_source: unwrap_any<string>(
|
||||
$('input[name="picture_source"]:checked').val() ?? 'selected'
|
||||
),
|
||||
personal: unwrap_any<string>($('#selChar').val())
|
||||
};
|
||||
if (!isCentennialAllStar) {
|
||||
formData.leadership = parseInt(unwrap_any<string>($('#leadership').val()));
|
||||
formData.strength = parseInt(unwrap_any<string>($('#leadership').val()));
|
||||
formData.intel = parseInt(unwrap_any<string>($('#leadership').val()));
|
||||
}
|
||||
const response = await axios({
|
||||
url: 'j_select_picked_general.php',
|
||||
method: 'post',
|
||||
responseType: 'json',
|
||||
data: convertFormData(formData)
|
||||
})
|
||||
result = response.data;
|
||||
if (!result.result) {
|
||||
throw result.reason;
|
||||
@@ -200,9 +213,12 @@ function printGenerals(value: GeneralPoolResponse) {
|
||||
|
||||
const emptyCard = {
|
||||
'leadership': null,
|
||||
'strength': null,
|
||||
'intel': null,
|
||||
'personalText': null,
|
||||
'strength': null,
|
||||
'intel': null,
|
||||
'initialLeadership': null,
|
||||
'initialStrength': null,
|
||||
'initialIntel': null,
|
||||
'personalText': null,
|
||||
'specialDomesticText': null,
|
||||
'specialWarText': null,
|
||||
'dex': null
|
||||
|
||||
@@ -4,6 +4,9 @@ use PHPUnit\Framework\TestCase;
|
||||
use sammo\CentennialAllStarGrowth;
|
||||
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';
|
||||
|
||||
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
|
||||
{
|
||||
self::assertSame(
|
||||
|
||||
Reference in New Issue
Block a user