forked from devsam/core
Compare commits
67
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32d2f7b0d1 | ||
|
|
4ffd7b5bc0 | ||
|
|
1e30659bdd | ||
|
|
162f198f08 | ||
|
|
191abf83c0 | ||
|
|
edfeae5f0f | ||
|
|
424184a51a | ||
|
|
03a321f95d | ||
|
|
e3615fa6bb | ||
|
|
32991f736d | ||
|
|
8b2748d3d4 | ||
|
|
883080e275 | ||
|
|
ce807e2564 | ||
|
|
9a2ac82373 | ||
|
|
3c35539c16 | ||
|
|
5ab08403c6 | ||
|
|
c1ce478d22 | ||
|
|
d8bf8ba922 | ||
|
|
95f66e4c39 | ||
|
|
4de7ebec17 | ||
|
|
d43dff35fb | ||
|
|
5b13f8bd30 | ||
|
|
e51bc6f711 | ||
|
|
1b08aa0c58 | ||
|
|
f2547fe207 | ||
|
|
cec7726731 | ||
|
|
d679a6fed8 | ||
|
|
6fe156eb7c | ||
|
|
663ddbe76a | ||
|
|
30297454f7 | ||
|
|
bc602722bf | ||
|
|
be8f147216 | ||
|
|
b9495fb64a | ||
|
|
07b217b290 | ||
|
|
42fac46062 | ||
|
|
24bc8985d0 | ||
|
|
76a2d5ff63 | ||
|
|
148f32f78b | ||
|
|
62f5b6597e | ||
|
|
7e810b0279 | ||
|
|
2336b29b65 | ||
|
|
185c0e16a0 | ||
|
|
656baeb9ee | ||
|
|
3e06304dc7 | ||
|
|
4bb940c8c5 | ||
|
|
2b376b6aab | ||
|
|
04df26ff83 | ||
|
|
aca4240b92 | ||
|
|
faf26fa8a2 | ||
|
|
030ab1e7ea | ||
|
|
7f2c1d4782 | ||
|
|
9132138a99 | ||
|
|
84875d60bd | ||
|
|
3cbaf76626 | ||
|
|
f4846d0d66 | ||
|
|
f183dcc72b | ||
|
|
63cc5d8e88 | ||
|
|
f858c32fdf | ||
|
|
60eb127d71 | ||
|
|
ede33f3a37 | ||
|
|
ffd53a2390 | ||
|
|
2591fbde67 | ||
|
|
922d3f1938 | ||
|
|
64522ba8a6 | ||
|
|
7b1eedb574 | ||
|
|
7ec6ca605f | ||
|
|
5a5c613814 |
@@ -12,7 +12,6 @@
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"brandonwamboldt/utilphp": "^1.1",
|
||||
"sergeytsalkov/meekrodb": "^2.5",
|
||||
"league/plates": "^3.5",
|
||||
"vlucas/valitron": "^1.4",
|
||||
|
||||
Generated
+2
-52
@@ -4,58 +4,8 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "deed9afdc95bc42f84784df67d295223",
|
||||
"content-hash": "7edae9b6be35e66ffd0d180f5fac6679",
|
||||
"packages": [
|
||||
{
|
||||
"name": "brandonwamboldt/utilphp",
|
||||
"version": "1.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/brandonwamboldt/utilphp.git",
|
||||
"reference": "36c32efc4f0679c05163464a550f45c8d83fe683"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/brandonwamboldt/utilphp/zipball/36c32efc4f0679c05163464a550f45c8d83fe683",
|
||||
"reference": "36c32efc4f0679c05163464a550f45c8d83fe683",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"satooshi/php-coveralls": "dev-master"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"utilphp\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Brandon Wamboldt",
|
||||
"email": "brandon.wamboldt@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "util.php is a collection of useful functions and snippets that you need or could use every day, designed to avoid conflicts with existing projects",
|
||||
"homepage": "https://github.com/brandonwamboldt/utilphp",
|
||||
"keywords": [
|
||||
"collection",
|
||||
"helpers",
|
||||
"php",
|
||||
"utility"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/brandonwamboldt/utilphp/issues",
|
||||
"source": "https://github.com/brandonwamboldt/utilphp/tree/master"
|
||||
},
|
||||
"time": "2015-02-02T17:56:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "brick/math",
|
||||
"version": "0.12.1",
|
||||
@@ -5721,5 +5671,5 @@
|
||||
"platform-overrides": {
|
||||
"php": "8.3"
|
||||
},
|
||||
"plugin-api-version": "2.3.0"
|
||||
"plugin-api-version": "2.6.0"
|
||||
}
|
||||
|
||||
+4
-1
@@ -99,7 +99,10 @@ function logError(string $err, string $errstr, string $errpath, array $trace)
|
||||
$err = str_replace(ROOT, '{ROOT}', $err);
|
||||
$errstr = str_replace(ROOT, '{ROOT}', $errstr);
|
||||
$errpath = str_replace(ROOT, '{ROOT}', $errpath);
|
||||
$trace = array_map(function (string $text) {
|
||||
$trace = array_map(function (string|array $text) {
|
||||
if (is_array($text)) {
|
||||
$text = Json::encode($text);
|
||||
}
|
||||
return str_replace(ROOT, '{ROOT}', $text);
|
||||
}, $trace);
|
||||
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ $queryMap = [
|
||||
];
|
||||
|
||||
if ($reqQueryType === null || !key_exists($reqQueryType, $queryMap)) {
|
||||
$reqQueryType = Util::array_first_key($queryMap);
|
||||
$reqQueryType = array_key_first($queryMap);
|
||||
}
|
||||
|
||||
//로그인 검사
|
||||
|
||||
@@ -243,11 +243,14 @@ $templates = new \League\Plates\Engine(__DIR__ . '/templates');
|
||||
"SELECT nation,no,name,owner_name as ownerName, owner, picture, imgsvr,
|
||||
experience, dedication,
|
||||
dex1, dex2, dex3, dex4, dex5,
|
||||
horse, weapon, book, item
|
||||
horse, weapon, book, item, aux
|
||||
FROM general WHERE %l",
|
||||
$btn == "NPC 보기" ? "npc>=2" : "npc<2"
|
||||
) as $general) {
|
||||
$generalID = $general['no'];
|
||||
foreach (['dex1', 'dex2', 'dex3', 'dex4', 'dex5'] as $dexKey) {
|
||||
$general[$dexKey] = CentennialAllStarGrowthService::recordableRawValue($general, $dexKey);
|
||||
}
|
||||
$general['bgColor'] = $nationColor[$general['nation']] ?? GameConst::$basecolor4;
|
||||
$general['fgColor'] = newColor($general['bgColor']);
|
||||
$general['nationName'] = $nationName[$general['nation']];
|
||||
@@ -427,4 +430,4 @@ $templates = new \League\Plates\Engine(__DIR__ . '/templates');
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
||||
+22
-8
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+6
-3
@@ -1047,7 +1047,7 @@ function increaseRefresh($type = "", $cnt = 1)
|
||||
|
||||
$logDB = FileDB::db($logPath, $serverPath . '/../f_install/sql/api_log.sql');
|
||||
|
||||
$ip = $_SERVER['REMOTE_ADDR'] ?? 'local';
|
||||
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? 'local';
|
||||
$date = date('Y-m-d H:i:s');
|
||||
|
||||
$logDB->insert('api_log', [
|
||||
@@ -1401,8 +1401,11 @@ function CheckHall($no)
|
||||
|
||||
foreach ($types as [$typeName, $valueType]) {
|
||||
|
||||
if ($valueType === 'natural') {
|
||||
$value = $generalObj->getVar($typeName);
|
||||
if ($valueType === 'natural') {
|
||||
$value = $generalObj->getVar($typeName);
|
||||
if (in_array($typeName, ['dex1', 'dex2', 'dex3', 'dex4', 'dex5'], true)) {
|
||||
$value = CentennialAllStarGrowthService::recordableValue($generalObj, $typeName);
|
||||
}
|
||||
} else if ($valueType === 'rank') {
|
||||
$value = $generalObj->getRankVar(RankColumn::from($typeName));
|
||||
} else if ($valueType === 'calc') {
|
||||
|
||||
@@ -686,7 +686,7 @@ function TechLimit($startYear, $year, $tech) : bool {
|
||||
$relYear = $year - $startYear;
|
||||
|
||||
$relMaxTech = Util::valueFit(
|
||||
floor($relYear / 5) + 1,
|
||||
floor($relYear / GameConst::$techLevelIncYear) + GameConst::$initialAllowedTechLevel,
|
||||
1,
|
||||
GameConst::$maxTechLevel
|
||||
);
|
||||
|
||||
@@ -559,7 +559,7 @@ function checkStatistic()
|
||||
|
||||
$nationHist = '';
|
||||
foreach (GameConst::$availableNationType as $nationType) {
|
||||
if (!Util::array_get($nationHists[$nationType])) {
|
||||
if (!($nationHists[$nationType] ?? null)) {
|
||||
$nationHists[$nationType] = '-';
|
||||
}
|
||||
$nationHist .= getNationType($nationType) . "({$nationHists[$nationType]}), ";
|
||||
|
||||
+2
-2
@@ -138,7 +138,7 @@ function JSCitiesBasedOnDistance(int $cityNo, int $maxDistance = 1): array{
|
||||
$distanceList = searchDistance($cityNo, $maxDistance, true);
|
||||
$result = [];
|
||||
for ($dist = 1; $dist <= $maxDistance; $dist++) {
|
||||
$result[$dist] = Util::array_get($distanceList[$dist], []);
|
||||
$result[$dist] = $distanceList[$dist] ?? [];
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
@@ -152,7 +152,7 @@ function printCitiesBasedOnDistance(int $cityNo, int $maxDistance = 1): string
|
||||
for ($dist = 1; $dist <= $maxDistance; $dist++) {
|
||||
$cityList = array_map(function ($cityID) {
|
||||
return CityConst::byID($cityID)->name;
|
||||
}, Util::array_get($distanceList[$dist], []));
|
||||
}, $distanceList[$dist] ?? []);
|
||||
|
||||
$cityStr = join(', ', $cityList);
|
||||
|
||||
|
||||
@@ -1153,21 +1153,25 @@ function fight($tnmt_type, $tnmt, $phs, $group, $g1, $g2, $type)
|
||||
];
|
||||
|
||||
//막판 분노
|
||||
$factor1 = 1.0;
|
||||
$factor2 = 1.0;
|
||||
$ratio = rand() % 300;
|
||||
if ($e1 / 5 > $energy1 && $damage1 > $damage2 && $gen1[$tp] >= $ratio) {
|
||||
$damage2 *= Util::round((rand() % 301 + 200) / 100); // 200 ~ 500%
|
||||
$factor2 = Util::round((rand() % 301 + 200) / 100); // 200 ~ 500%
|
||||
$critical1 = 1;
|
||||
$str = Util::choiceRandom($crticialSkillMap[$tnmt_type]);
|
||||
$log[] = "<S>●</> <Y>{$gen1['name']}</>의 분노의 <M>{$str}</> 공격!";
|
||||
}
|
||||
$ratio = rand() % 300;
|
||||
if ($e2 / 5 > $energy2 && $damage2 > $damage1 && $gen2[$tp] >= $ratio) {
|
||||
$damage1 *= Util::round((rand() % 301 + 200) / 100); // 200 ~ 500%
|
||||
$factor1 = Util::round((rand() % 301 + 200) / 100); // 200 ~ 500%
|
||||
$critical2 = 1;
|
||||
|
||||
$str = Util::choiceRandom($crticialSkillMap[$tnmt_type]);
|
||||
$log[] = "<S>●</> <Y>{$gen2['name']}</>의 분노의 <M>{$str}</> 공격!";
|
||||
}
|
||||
$damage1 *= $factor1;
|
||||
$damage2 *= $factor2;
|
||||
|
||||
//1합 승부
|
||||
if ($phase == 1) {
|
||||
|
||||
|
||||
@@ -10,7 +10,23 @@ function sortTokens(&$tokens){
|
||||
});
|
||||
}
|
||||
|
||||
function putInfoText(&$info){
|
||||
function putInfoText(&$info, ?array $currentTargetEnv){
|
||||
if (($info['event100Growth'] ?? false) === true) {
|
||||
if ($currentTargetEnv === null) {
|
||||
$displayStats = CentennialAllStarGrowthService::calculateUserInitialStats($info);
|
||||
$info['selectionStatLabel'] = '시작 능력치';
|
||||
} else {
|
||||
$displayStats = CentennialAllStarGrowthService::calculateUserCurrentTargetStats(
|
||||
$info,
|
||||
$currentTargetEnv
|
||||
);
|
||||
$info['selectionStatLabel'] = '현재 변경 기준 능력치';
|
||||
}
|
||||
$info['selectionLeadership'] = $displayStats['leadership'];
|
||||
$info['selectionStrength'] = $displayStats['strength'];
|
||||
$info['selectionIntel'] = $displayStats['intel'];
|
||||
}
|
||||
|
||||
if(key_exists('specialDomestic', $info)){
|
||||
$class = buildGeneralSpecialDomesticClass($info['specialDomestic']);
|
||||
$info['specialDomesticName'] = $class->getName();
|
||||
@@ -34,7 +50,8 @@ $now = $oNow->format('Y-m-d H:i:s');
|
||||
$db = DB::db();
|
||||
$gameStor = KVStorage::getStorage($db, 'game_env');
|
||||
|
||||
$npcmode = $gameStor->getValue('npcmode');
|
||||
$eventEnv = $gameStor->getValues(['npcmode', 'startyear', 'year', 'month']);
|
||||
$npcmode = $eventEnv['npcmode'];
|
||||
if($npcmode!=2){
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
@@ -42,7 +59,8 @@ if($npcmode!=2){
|
||||
]);
|
||||
}
|
||||
|
||||
$rawGeneral = $db->queryFirstRow('SELECT no, aux FROM general WHERE `owner` = %i', $userID);
|
||||
$rawGeneral = $db->queryFirstRow('SELECT no, aux FROM general WHERE `owner` = %i', $userID);
|
||||
$currentTargetEnv = $rawGeneral ? $eventEnv : null;
|
||||
if($rawGeneral){
|
||||
$generalAux = Json::decode($rawGeneral['aux']);
|
||||
if(key_exists('next_change', $generalAux)&& $generalAux['next_change'] > $now){
|
||||
@@ -62,7 +80,7 @@ if($tokens){
|
||||
foreach($tokens as $token){
|
||||
$valid_until = $token['reserved_until'];
|
||||
$info = Json::decode($token['info']);
|
||||
putInfoText($info);
|
||||
putInfoText($info, $currentTargetEnv);
|
||||
$info['uniqueName'] = $token['unique_name'];
|
||||
$pick[] = $info;
|
||||
}
|
||||
@@ -83,7 +101,7 @@ $valid_until = null;
|
||||
foreach(pickGeneralFromPool($db, $rng, $userID, 14) as $pickObj){
|
||||
$valid_until = $pickObj->getValidUntil();
|
||||
$info = $pickObj->getInfo();
|
||||
putInfoText($info);
|
||||
putInfoText($info, $currentTargetEnv);
|
||||
$pick[] = $info;
|
||||
}
|
||||
sortTokens($pick);//좀 무식하지만..
|
||||
@@ -91,4 +109,4 @@ Json::die([
|
||||
'result'=>true,
|
||||
'pick'=>$pick,
|
||||
'validUntil'=>$valid_until
|
||||
]);
|
||||
]);
|
||||
|
||||
@@ -144,7 +144,7 @@ function do도시임명(General $general, int $cityID, int $targetOfficerLevel):
|
||||
return '올바르지 않은 도시입니다';
|
||||
}
|
||||
|
||||
if ($general->getVar(GeneralColumn::officer_level) >= 4 && $myPenalty[PenaltyKey::NoChiefChange->value] ?? false) {
|
||||
if ($general->getVar(GeneralColumn::officer_level) >= 4 && ($myPenalty[PenaltyKey::NoChiefChange->value] ?? false)) {
|
||||
return '수뇌인 장수를 변경할 수 없는 상태입니다.';
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ $penaltyInfo = array_merge($memberPenalty['any'] ?? [], $memberPenalty[DB::prefi
|
||||
|
||||
$penalty = [];
|
||||
foreach($penaltyInfo as $penaltyKey => $penaltyValue){
|
||||
if($penaltyValue['expire'] ?? 0 > TimeUtil::now()){
|
||||
if(($penaltyValue['expire'] ?? 0) > TimeUtil::now()){
|
||||
$penalty[$penaltyKey] = $penaltyValue['value'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,10 +8,20 @@ WebUtil::requireAJAX();
|
||||
|
||||
$pick = Util::getPost('pick');
|
||||
$leadership = Util::getPost('leadership', 'int', GameConst::$defaultStatMin);
|
||||
$strength = Util::getPost('leadership', 'int', GameConst::$defaultStatMin);
|
||||
$intel = Util::getPost('leadership', 'int', GameConst::$defaultStatMin);
|
||||
$isCentennialAllStar = CentennialAllStarGrowthService::isActive();
|
||||
$strength = Util::getPost(
|
||||
$isCentennialAllStar ? 'strength' : 'leadership',
|
||||
'int',
|
||||
GameConst::$defaultStatMin
|
||||
);
|
||||
$intel = Util::getPost(
|
||||
$isCentennialAllStar ? 'intel' : 'leadership',
|
||||
'int',
|
||||
GameConst::$defaultStatMin
|
||||
);
|
||||
$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){
|
||||
@@ -60,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([
|
||||
@@ -80,12 +113,25 @@ if ($gencount >= $maxgeneral) {
|
||||
|
||||
$poolClass = getGeneralPoolClass(GameConst::$targetGeneralPool);
|
||||
/** @var AbsGeneralPool */
|
||||
$pickedGeneral = new $poolClass($db, $selectInfo, $now);
|
||||
if ($isCentennialAllStar) {
|
||||
$rng = new RandUtil(new LiteHashDRBG(Util::simpleSerialize(
|
||||
UniqueConst::$hiddenSeed,
|
||||
'selectPickedGeneral',
|
||||
$userID,
|
||||
$pick
|
||||
)));
|
||||
$pickedGeneral = new $poolClass($db, $rng, $selectInfo, $now);
|
||||
} else {
|
||||
$pickedGeneral = new $poolClass($db, $selectInfo, $now);
|
||||
}
|
||||
|
||||
$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);
|
||||
@@ -98,14 +144,23 @@ 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'){
|
||||
$personal = Util::choiceRandom(GameConst::$availablePersonality);
|
||||
}
|
||||
if(!array_search($personal, GameConst::$availablePersonality)){
|
||||
$invalidPersonal = $isCentennialAllStar
|
||||
? !in_array($personal, GameConst::$availablePersonality, true)
|
||||
: !array_search($personal, GameConst::$availablePersonality);
|
||||
if($invalidPersonal){
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reason'=>'올바르지 않은 성격입니다.'
|
||||
@@ -113,7 +168,7 @@ foreach(GameConst::$generalPoolAllowOption as $allowOption){
|
||||
}
|
||||
$builder->setEgo($personal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$userNick = $ownerInfo['name'];
|
||||
|
||||
@@ -122,7 +177,28 @@ $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(
|
||||
'SELECT city FROM city WHERE level >= 5 AND level <= 6 AND nation = 0'
|
||||
);
|
||||
if (!$candidateCities) {
|
||||
$candidateCities = $db->queryFirstColumn(
|
||||
'SELECT city FROM city WHERE level >= 5 AND level <= 6'
|
||||
);
|
||||
}
|
||||
if (!$candidateCities) {
|
||||
Json::die([
|
||||
'result' => false,
|
||||
'reason' => '장수를 생성할 소·중성이 없습니다.',
|
||||
]);
|
||||
}
|
||||
$builder->setCityID($rng->choice($candidateCities));
|
||||
}
|
||||
$builder->build($env);
|
||||
$generalID = $builder->getGeneralID();
|
||||
if(!$generalID){
|
||||
@@ -167,4 +243,4 @@ $rootDB->insert('member_log', [
|
||||
Json::die([
|
||||
'result'=>true,
|
||||
'reason'=>'success'
|
||||
]);
|
||||
]);
|
||||
|
||||
@@ -88,7 +88,7 @@ $penaltyInfo = array_merge($memberPenalty['any'] ?? [], $memberPenalty[DB::prefi
|
||||
|
||||
$penalty = [];
|
||||
foreach ($penaltyInfo as $penaltyKey => $penaltyValue) {
|
||||
if ($penaltyValue['expire'] ?? 0 > TimeUtil::now()) {
|
||||
if (($penaltyValue['expire'] ?? 0) > TimeUtil::now()) {
|
||||
$penalty[$penaltyKey] = $penaltyValue['value'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ include "func.php";
|
||||
WebUtil::requireAJAX();
|
||||
|
||||
$pick = Util::getPost('pick');
|
||||
$pictureSource = Util::getPost('picture_source', 'string', 'selected');
|
||||
|
||||
if(!$pick){
|
||||
Json::die([
|
||||
@@ -35,10 +36,20 @@ if(!$generalID){
|
||||
list(
|
||||
$year,
|
||||
$month,
|
||||
$startYear,
|
||||
$maxgeneral,
|
||||
$npcmode,
|
||||
$turnterm
|
||||
) = $gameStor->getValuesAsArray(['year', 'month', 'maxgeneral', 'npcmode', 'turnterm']);
|
||||
$turnterm,
|
||||
$showImgLevel
|
||||
) = $gameStor->getValuesAsArray([
|
||||
'year',
|
||||
'month',
|
||||
'startyear',
|
||||
'maxgeneral',
|
||||
'npcmode',
|
||||
'turnterm',
|
||||
'show_img_level',
|
||||
]);
|
||||
|
||||
if($npcmode!=2){
|
||||
Json::die([
|
||||
@@ -55,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,
|
||||
@@ -64,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);
|
||||
@@ -101,34 +136,51 @@ $db->update('select_pool',[
|
||||
'reserved_until'=>null,
|
||||
], '(owner=%i or reserved_until < %s) AND general_id is NULL', $userID, $now);
|
||||
|
||||
if(key_exists('leadership', $info)){
|
||||
$generalObj->updateVar('leadership', $info['leadership']);
|
||||
$generalObj->updateVar('strength', $info['strength']);
|
||||
$generalObj->updateVar('intel', $info['intel']);
|
||||
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(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('picture', $info['picture']);
|
||||
}
|
||||
if(key_exists('generalName', $info)){
|
||||
$generalObj->updateVar('name', $info['generalName']);
|
||||
}
|
||||
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']);
|
||||
}
|
||||
$generalObj->setAuxVar('next_change', TimeUtil::nowAddMinutes(12 * $turnterm));
|
||||
|
||||
$userNick = $ownerInfo['name'];
|
||||
@@ -148,4 +200,4 @@ $generalObj->applyDB($db);
|
||||
Json::die([
|
||||
'result'=>true,
|
||||
'reason'=>'success'
|
||||
]);
|
||||
]);
|
||||
|
||||
+3
-2
@@ -51,8 +51,9 @@ function MessageBox($str)
|
||||
|
||||
function LogText($prefix, $variable)
|
||||
{
|
||||
$text = sprintf('%s : %s'."\r\n", $prefix, TVarDumper::dump($variable));
|
||||
file_put_contents(ROOT.'/d_log/'.UniqueConst::$serverName.'_dbg_logs.txt', $text, FILE_APPEND);
|
||||
return;
|
||||
//$text = sprintf('%s : %s'."\r\n", $prefix, TVarDumper::dump($variable));
|
||||
//file_put_contents(ROOT.'/d_log/'.UniqueConst::$serverName.'_dbg_logs.txt', $text, FILE_APPEND);
|
||||
}
|
||||
|
||||
function prepareDir(string $dirPath, bool $forceCreate=true):bool{
|
||||
|
||||
+9
-1
@@ -8,4 +8,12 @@ $session = Session::getInstance()->setReadOnly();
|
||||
|
||||
$db = DB::db();
|
||||
|
||||
TurnExecutionHelper::executeAllCommand();
|
||||
$updated = false;
|
||||
$locked = false;
|
||||
$lastExecuted = TurnExecutionHelper::executeAllCommand($updated, $locked);
|
||||
Json::die([
|
||||
'result' => true,
|
||||
'updated' => $updated,
|
||||
'locked' => $locked,
|
||||
'lastExecuted' => $lastExecuted,
|
||||
]);
|
||||
+3
-3
@@ -526,7 +526,7 @@ function DeleteConflict($nation)
|
||||
function getConquerNation($city): int
|
||||
{
|
||||
$conflict = Json::decode($city['conflict']);
|
||||
return Util::array_first_key($conflict);
|
||||
return array_key_first($conflict);
|
||||
}
|
||||
|
||||
function ConquerCity(array $admin, General $general, array $city, array $defenderCityGeneralList)
|
||||
@@ -785,8 +785,8 @@ function ConquerCity(array $admin, General $general, array $city, array $defende
|
||||
'officer_set' => 0,
|
||||
];
|
||||
if ($city['level'] > 3) {
|
||||
$query['def'] = 1000;
|
||||
$query['wall'] = 1000;
|
||||
$query['def'] = GameConst::$defaultCityWall;
|
||||
$query['wall'] = GameConst::$defaultCityWall;
|
||||
} else {
|
||||
$query['def'] = $db->sqleval('def_max/2');
|
||||
$query['wall'] = $db->sqleval('wall_max/2');
|
||||
|
||||
@@ -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>감추었습니다</>");
|
||||
|
||||
@@ -99,7 +99,7 @@ class Join extends \sammo\BaseAPI
|
||||
}
|
||||
|
||||
$gameID = UniqueConst::$serverID;
|
||||
$alreadyJoined = $db->queryFirstField('SELECT count(*) FROM `storage` WHERE `namespace` = %s AND `key` LIKE %s', 'inheritance_result', "{$gameID}_{$userID}_%");
|
||||
$alreadyJoined = $db->queryFirstField('SELECT COUNT(*) FROM `inheritance_result` WHERE `server_id` = %s AND `owner` = %i', $gameID, $userID);
|
||||
if ($alreadyJoined) {
|
||||
//이미 받았음
|
||||
return 0;
|
||||
@@ -157,7 +157,7 @@ class Join extends \sammo\BaseAPI
|
||||
|
||||
$penalty = [];
|
||||
foreach($penaltyInfo as $penaltyKey => $penaltyValue){
|
||||
if($penaltyValue['expire'] ?? 0 > TimeUtil::now()){
|
||||
if(($penaltyValue['expire'] ?? 0) > TimeUtil::now()){
|
||||
$penalty[$penaltyKey] = $penaltyValue['value'];
|
||||
}
|
||||
}
|
||||
@@ -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', [
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace sammo\API\InheritAction;
|
||||
|
||||
use sammo\Session;
|
||||
use DateTimeInterface;
|
||||
use sammo\CentennialAllStarGrowthService;
|
||||
use sammo\DB;
|
||||
use sammo\Enums\APIRecoveryType;
|
||||
use sammo\Enums\RankColumn;
|
||||
@@ -98,6 +99,10 @@ class ResetStat extends \sammo\BaseAPI
|
||||
return 'NPC는 능력치 초기화를 할 수 없습니다.';
|
||||
}
|
||||
|
||||
if (!CentennialAllStarGrowthService::isStatResetAllowed()) {
|
||||
return '100기 올스타 장수는 능력치 초기화를 사용할 수 없습니다.';
|
||||
}
|
||||
|
||||
|
||||
$db = DB::db();
|
||||
$gameStor = KVStorage::getStorage($db, 'game_env');
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ class event_전투특기_공성 extends \sammo\BaseItem{
|
||||
}
|
||||
|
||||
public function onCalcStat(General $general, string $statName, $value, $aux=null){
|
||||
if(\sammo\Util::starts_with($statName, 'dex')){
|
||||
if(str_starts_with($statName, 'dex')){
|
||||
$myArmType = 'dex'.GameUnitConst::T_SIEGE;
|
||||
$opposeArmType = 'dex'.$aux['opposeType']->armType;;
|
||||
if($aux['isAttacker'] && $opposeArmType === $statName){
|
||||
|
||||
@@ -28,7 +28,7 @@ class event_전투특기_궁병 extends \sammo\BaseItem{
|
||||
if($statName === 'warAvoidRatio'){
|
||||
return $value + 0.2;
|
||||
}
|
||||
if(\sammo\Util::starts_with($statName, 'dex')){
|
||||
if(str_starts_with($statName, 'dex')){
|
||||
$myArmType = 'dex'.GameUnitConst::T_ARCHER;
|
||||
$opposeArmType = 'dex'.$aux['opposeType']->armType;;
|
||||
if($aux['isAttacker'] && $opposeArmType === $statName){
|
||||
|
||||
@@ -28,7 +28,7 @@ class event_전투특기_귀병 extends \sammo\BaseItem{
|
||||
if($statName === 'warMagicSuccessProb'){
|
||||
return $value + 0.2;
|
||||
}
|
||||
if(\sammo\Util::starts_with($statName, 'dex')){
|
||||
if(str_starts_with($statName, 'dex')){
|
||||
$myArmType = 'dex'.GameUnitConst::T_WIZARD;
|
||||
$opposeArmType = 'dex'.$aux['opposeType']->armType;;
|
||||
if($aux['isAttacker'] && $opposeArmType === $statName){
|
||||
|
||||
@@ -32,7 +32,7 @@ class event_전투특기_기병 extends \sammo\BaseItem{
|
||||
}
|
||||
|
||||
public function onCalcStat(General $general, string $statName, $value, $aux=null){
|
||||
if(\sammo\Util::starts_with($statName, 'dex')){
|
||||
if(str_starts_with($statName, 'dex')){
|
||||
$myArmType = 'dex'.GameUnitConst::T_CAVALRY;
|
||||
$opposeArmType = 'dex'.$aux['opposeType']->armType;;
|
||||
if($aux['isAttacker'] && $opposeArmType === $statName){
|
||||
|
||||
@@ -33,7 +33,7 @@ class event_전투특기_보병 extends \sammo\BaseItem{
|
||||
}
|
||||
|
||||
public function onCalcStat(General $general, string $statName, $value, $aux=null){
|
||||
if(\sammo\Util::starts_with($statName, 'dex')){
|
||||
if(str_starts_with($statName, 'dex')){
|
||||
$myArmType = 'dex'.GameUnitConst::T_FOOTMAN;
|
||||
$opposeArmType = 'dex'.$aux['opposeType']->armType;;
|
||||
if($aux['isAttacker'] && $opposeArmType === $statName){
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace sammo\ActionScenarioEffect;
|
||||
|
||||
use \sammo\iAction;
|
||||
use sammo\Util;
|
||||
use sammo\WarUnit;
|
||||
use sammo\WarUnitCity;
|
||||
use sammo\WarUnitTrigger\che_전멸시페이즈증가;
|
||||
use sammo\WarUnitTriggerCaller;
|
||||
|
||||
class event_MoreEffect implements iAction
|
||||
{
|
||||
use \sammo\DefaultAction;
|
||||
|
||||
public function getWarPowerMultiplier(WarUnit $unit): array
|
||||
{
|
||||
if ($unit->isAttacker()) {
|
||||
return [1.4, 0.7143];
|
||||
}
|
||||
return [1, 1];
|
||||
}
|
||||
|
||||
public function onCalcDomestic(string $turnType, string $varType, float $value, $aux = null): float
|
||||
{
|
||||
$scoreMap = [
|
||||
'상업' => 2,
|
||||
'농업' => 2,
|
||||
'치안' => 2,
|
||||
'기술' => 2,
|
||||
'성벽' => 2,
|
||||
'수비' => 2,
|
||||
'인구' => 2,
|
||||
'민심' => 2,
|
||||
];
|
||||
if ($turnType == 'changeDefenceTrain') {
|
||||
return 0;
|
||||
}
|
||||
if ($varType === 'score' && key_exists($turnType, $scoreMap)) {
|
||||
$multiplier = $scoreMap[$turnType];
|
||||
$value *= $multiplier;
|
||||
return $value;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function onCalcNationalIncome(string $type, $amount){
|
||||
if($type == 'gold'){
|
||||
return $amount * 2;
|
||||
}
|
||||
if($type == 'rice'){
|
||||
return $amount * 2;
|
||||
}
|
||||
if($type == 'pop' && $amount > 0){
|
||||
return $amount * 2;
|
||||
}
|
||||
|
||||
return $amount;
|
||||
}
|
||||
|
||||
public function getBattlePhaseSkillTriggerList(\sammo\WarUnit $unit): ?WarUnitTriggerCaller
|
||||
{
|
||||
return new WarUnitTriggerCaller(
|
||||
new che_전멸시페이즈증가($unit),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,7 @@ class che_event_공성 extends \sammo\BaseSpecial{
|
||||
}
|
||||
|
||||
public function onCalcStat(General $general, string $statName, $value, $aux=null){
|
||||
if(\sammo\Util::starts_with($statName, 'dex')){
|
||||
if(str_starts_with($statName, 'dex')){
|
||||
$myArmType = 'dex'.GameUnitConst::T_SIEGE;
|
||||
$opposeArmType = 'dex'.$aux['opposeType']->armType;;
|
||||
if($aux['isAttacker'] && $opposeArmType === $statName){
|
||||
|
||||
@@ -31,7 +31,7 @@ class che_event_궁병 extends \sammo\BaseSpecial{
|
||||
if($statName === 'warAvoidRatio'){
|
||||
return $value + 0.2;
|
||||
}
|
||||
if(\sammo\Util::starts_with($statName, 'dex')){
|
||||
if(str_starts_with($statName, 'dex')){
|
||||
$myArmType = 'dex'.GameUnitConst::T_ARCHER;
|
||||
$opposeArmType = 'dex'.$aux['opposeType']->armType;;
|
||||
if($aux['isAttacker'] && $opposeArmType === $statName){
|
||||
|
||||
@@ -30,7 +30,7 @@ class che_event_귀병 extends \sammo\BaseSpecial{
|
||||
if($statName === 'warMagicSuccessProb'){
|
||||
return $value + 0.2;
|
||||
}
|
||||
if(\sammo\Util::starts_with($statName, 'dex')){
|
||||
if(str_starts_with($statName, 'dex')){
|
||||
$myArmType = 'dex'.GameUnitConst::T_WIZARD;
|
||||
$opposeArmType = 'dex'.$aux['opposeType']->armType;;
|
||||
if($aux['isAttacker'] && $opposeArmType === $statName){
|
||||
|
||||
@@ -35,7 +35,7 @@ class che_event_기병 extends \sammo\BaseSpecial{
|
||||
}
|
||||
|
||||
public function onCalcStat(General $general, string $statName, $value, $aux=null){
|
||||
if(\sammo\Util::starts_with($statName, 'dex')){
|
||||
if(str_starts_with($statName, 'dex')){
|
||||
$myArmType = 'dex'.GameUnitConst::T_CAVALRY;
|
||||
$opposeArmType = 'dex'.$aux['opposeType']->armType;;
|
||||
if($aux['isAttacker'] && $opposeArmType === $statName){
|
||||
|
||||
@@ -35,7 +35,7 @@ class che_event_보병 extends \sammo\BaseSpecial{
|
||||
}
|
||||
|
||||
public function onCalcStat(General $general, string $statName, $value, $aux=null){
|
||||
if(\sammo\Util::starts_with($statName, 'dex')){
|
||||
if(str_starts_with($statName, 'dex')){
|
||||
$myArmType = 'dex'.GameUnitConst::T_FOOTMAN;
|
||||
$opposeArmType = 'dex'.$aux['opposeType']->armType;;
|
||||
if($aux['isAttacker'] && $opposeArmType === $statName){
|
||||
|
||||
@@ -35,7 +35,7 @@ class che_공성 extends \sammo\BaseSpecial{
|
||||
}
|
||||
|
||||
public function onCalcStat(General $general, string $statName, $value, $aux=null){
|
||||
if(\sammo\Util::starts_with($statName, 'dex')){
|
||||
if(str_starts_with($statName, 'dex')){
|
||||
$myArmType = 'dex'.GameUnitConst::T_SIEGE;
|
||||
$opposeArmType = 'dex'.$aux['opposeType']->armType;;
|
||||
if($aux['isAttacker'] && $opposeArmType === $statName){
|
||||
|
||||
@@ -31,7 +31,7 @@ class che_궁병 extends \sammo\BaseSpecial{
|
||||
if($statName === 'warAvoidRatio'){
|
||||
return $value + 0.2;
|
||||
}
|
||||
if(\sammo\Util::starts_with($statName, 'dex')){
|
||||
if(str_starts_with($statName, 'dex')){
|
||||
$myArmType = 'dex'.GameUnitConst::T_ARCHER;
|
||||
$opposeArmType = 'dex'.$aux['opposeType']->armType;;
|
||||
if($aux['isAttacker'] && $opposeArmType === $statName){
|
||||
|
||||
@@ -30,7 +30,7 @@ class che_귀병 extends \sammo\BaseSpecial{
|
||||
if($statName === 'warMagicSuccessProb'){
|
||||
return $value + 0.2;
|
||||
}
|
||||
if(\sammo\Util::starts_with($statName, 'dex')){
|
||||
if(str_starts_with($statName, 'dex')){
|
||||
$myArmType = 'dex'.GameUnitConst::T_WIZARD;
|
||||
$opposeArmType = 'dex'.$aux['opposeType']->armType;;
|
||||
if($aux['isAttacker'] && $opposeArmType === $statName){
|
||||
|
||||
@@ -35,7 +35,7 @@ class che_기병 extends \sammo\BaseSpecial{
|
||||
}
|
||||
|
||||
public function onCalcStat(General $general, string $statName, $value, $aux=null){
|
||||
if(\sammo\Util::starts_with($statName, 'dex')){
|
||||
if(str_starts_with($statName, 'dex')){
|
||||
$myArmType = 'dex'.GameUnitConst::T_CAVALRY;
|
||||
$opposeArmType = 'dex'.$aux['opposeType']->armType;;
|
||||
if($aux['isAttacker'] && $opposeArmType === $statName){
|
||||
|
||||
@@ -35,7 +35,7 @@ class che_보병 extends \sammo\BaseSpecial{
|
||||
}
|
||||
|
||||
public function onCalcStat(General $general, string $statName, $value, $aux=null){
|
||||
if(\sammo\Util::starts_with($statName, 'dex')){
|
||||
if(str_starts_with($statName, 'dex')){
|
||||
$myArmType = 'dex'.GameUnitConst::T_FOOTMAN;
|
||||
$opposeArmType = 'dex'.$aux['opposeType']->armType;;
|
||||
if($aux['isAttacker'] && $opposeArmType === $statName){
|
||||
|
||||
@@ -0,0 +1,686 @@
|
||||
<?php
|
||||
|
||||
namespace sammo;
|
||||
|
||||
use sammo\Scenario\GeneralBuilder;
|
||||
|
||||
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;
|
||||
public const DEX_EXTENSION_START_YEAR_OFFSET = 20;
|
||||
public const DEX_EXTENSION_YEARS = 5;
|
||||
|
||||
private const STAT_KEYS = ['leadership', 'strength', 'intel'];
|
||||
private const DEX_KEYS = ['dex1', 'dex2', 'dex3', 'dex4', 'dex5'];
|
||||
|
||||
public static function isActive(): bool
|
||||
{
|
||||
return GameConst::$targetGeneralPool === self::POOL_CLASS;
|
||||
}
|
||||
|
||||
public static function isStatResetAllowed(): bool
|
||||
{
|
||||
return !self::isActive();
|
||||
}
|
||||
|
||||
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' => $granted,
|
||||
'dexConsumed' => array_fill_keys(self::DEX_KEYS, 0),
|
||||
'dexFloor' => array_fill_keys(self::DEX_KEYS, 0),
|
||||
'progressMonth' => -1,
|
||||
'milestone' => 0,
|
||||
'dexExtensionMilestone' => 0,
|
||||
'naturalSpecialDomestic' => null,
|
||||
'eventSpecialDomestic' => null,
|
||||
'userInitialStats' => $userInitialStats,
|
||||
'dexTargetRatio' => 1.0,
|
||||
];
|
||||
}
|
||||
|
||||
public static function attachInitialTarget(GeneralBuilder $builder, array $targetInfo): void
|
||||
{
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the event stat baseline that a newly selected target receives at
|
||||
* the supplied game date. Organic growth can still leave the actual stat
|
||||
* above this baseline.
|
||||
*
|
||||
* @return array{leadership:int,strength:int,intel:int}
|
||||
*/
|
||||
public static function calculateUserCurrentTargetStats(
|
||||
array $targetInfo,
|
||||
array $env
|
||||
): array {
|
||||
$initialStats = self::calculateUserInitialStats($targetInfo);
|
||||
$progress = self::calculateProgress(
|
||||
(int) $env['startyear'],
|
||||
(int) $env['year'],
|
||||
(int) $env['month']
|
||||
);
|
||||
$result = [];
|
||||
foreach (self::STAT_KEYS as $key) {
|
||||
$target = min(
|
||||
GameConst::$maxLevel,
|
||||
max(0, (int) ($targetInfo[$key] ?? 0))
|
||||
);
|
||||
$result[$key] = max(
|
||||
$initialStats[$key],
|
||||
CentennialAllStarGrowth::statFloor(
|
||||
$target,
|
||||
GameConst::$defaultStatMin,
|
||||
$progress
|
||||
)
|
||||
);
|
||||
}
|
||||
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,
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
public static function calculateDexTargetFloor(
|
||||
int $target,
|
||||
array $env,
|
||||
float $targetRatio = 1.0
|
||||
): int {
|
||||
if ($targetRatio < 0 || $targetRatio > 1) {
|
||||
throw new \InvalidArgumentException('dex target ratio must be between 0 and 1');
|
||||
}
|
||||
$target = self::calculateDexExtendedTarget($target, $env);
|
||||
$scaledTarget = (int) floor($target * $targetRatio);
|
||||
$progress = self::calculateProgress(
|
||||
(int) $env['startyear'],
|
||||
(int) $env['year'],
|
||||
(int) $env['month']
|
||||
);
|
||||
return CentennialAllStarGrowth::dexFloor($scaledTarget, $progress);
|
||||
}
|
||||
|
||||
public static function calculateDexExtensionProgress(array $env): float
|
||||
{
|
||||
return CentennialAllStarGrowth::progress(
|
||||
(int) $env['startyear'] + self::DEX_EXTENSION_START_YEAR_OFFSET,
|
||||
(int) $env['year'],
|
||||
(int) $env['month'],
|
||||
self::DEX_EXTENSION_YEARS
|
||||
);
|
||||
}
|
||||
|
||||
public static function calculateDexExtendedTarget(int $target, array $env): int
|
||||
{
|
||||
$target = max(0, $target);
|
||||
$baseTarget = min(GameConst::$dexLimit, $target);
|
||||
$extensionTarget = max(0, $target - $baseTarget);
|
||||
if ($extensionTarget === 0) {
|
||||
return $baseTarget;
|
||||
}
|
||||
|
||||
return $baseTarget + (int) floor(
|
||||
$extensionTarget * self::calculateDexExtensionProgress($env)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mutates the General object but leaves persistence to the caller.
|
||||
*
|
||||
* @return array{progress:float,milestone:int,previousMilestone:int,dexExtensionMilestone:int,previousDexExtensionMilestone:int,hasDexExtensionTarget:bool,targetChanged:bool,changed:bool}
|
||||
*/
|
||||
public static function applyTarget(
|
||||
General $general,
|
||||
array $targetInfo,
|
||||
array $env,
|
||||
float $progressMultiplier = 1.0,
|
||||
float $dexTargetRatio = 1.0
|
||||
): array
|
||||
{
|
||||
$startYear = (int) $env['startyear'];
|
||||
$year = (int) $env['year'];
|
||||
$month = (int) $env['month'];
|
||||
$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);
|
||||
if (!is_array($aux)) {
|
||||
$aux = self::initialAux($targetInfo);
|
||||
}
|
||||
$granted = is_array($aux['granted'] ?? null)
|
||||
? $aux['granted']
|
||||
: array_fill_keys(array_merge(self::STAT_KEYS, self::DEX_KEYS), 0);
|
||||
$targetChanged = ($aux['targetId'] ?? '') !== $targetId;
|
||||
$dexConsumed = is_array($aux['dexConsumed'] ?? null)
|
||||
? $aux['dexConsumed']
|
||||
: array_fill_keys(self::DEX_KEYS, 0);
|
||||
if ($targetChanged) {
|
||||
$dexConsumed = array_fill_keys(self::DEX_KEYS, 0);
|
||||
}
|
||||
$dexFloor = is_array($aux['dexFloor'] ?? null)
|
||||
? $aux['dexFloor']
|
||||
: array_fill_keys(self::DEX_KEYS, 0);
|
||||
$isUserTarget = is_array($aux['userInitialStats'] ?? null);
|
||||
$nextUserInitialStats = $targetChanged && $isUserTarget
|
||||
? self::calculateUserInitialStats($targetInfo)
|
||||
: ($aux['userInitialStats'] ?? null);
|
||||
$previousDexTargetRatio = is_numeric($aux['dexTargetRatio'] ?? null)
|
||||
? (float) $aux['dexTargetRatio']
|
||||
: 1.0;
|
||||
$dexTargetRatioChanged = $previousDexTargetRatio !== $dexTargetRatio;
|
||||
$userCurrentTargetStats = $isUserTarget
|
||||
? self::calculateUserCurrentTargetStats($targetInfo, $env)
|
||||
: null;
|
||||
$changed = $dexTargetRatioChanged;
|
||||
|
||||
foreach (self::STAT_KEYS as $key) {
|
||||
if (!array_key_exists($key, $targetInfo)) {
|
||||
continue;
|
||||
}
|
||||
$target = min(GameConst::$maxLevel, max(0, (int) $targetInfo[$key]));
|
||||
$floor = CentennialAllStarGrowth::statFloor(
|
||||
$target,
|
||||
GameConst::$defaultStatMin,
|
||||
$progress
|
||||
);
|
||||
if ($userCurrentTargetStats !== null) {
|
||||
$floor = $userCurrentTargetStats[$key];
|
||||
}
|
||||
$current = (int) $general->getVar($key);
|
||||
if ($targetChanged) {
|
||||
$result = CentennialAllStarGrowth::replaceTarget(
|
||||
$current,
|
||||
(int) ($granted[$key] ?? 0),
|
||||
$floor
|
||||
);
|
||||
} else {
|
||||
$result = CentennialAllStarGrowth::advance(
|
||||
$current,
|
||||
(int) ($granted[$key] ?? 0),
|
||||
$floor
|
||||
);
|
||||
}
|
||||
if ($result['value'] !== $current) {
|
||||
$general->updateVar($key, $result['value']);
|
||||
$changed = true;
|
||||
}
|
||||
$granted[$key] = $result['granted'];
|
||||
}
|
||||
|
||||
$targetDex = $targetInfo['dex'] ?? [];
|
||||
$hasDexExtensionTarget = false;
|
||||
foreach (self::DEX_KEYS as $idx => $key) {
|
||||
if (!array_key_exists($idx, $targetDex)) {
|
||||
continue;
|
||||
}
|
||||
if ((int) $targetDex[$idx] > GameConst::$dexLimit) {
|
||||
$hasDexExtensionTarget = true;
|
||||
}
|
||||
$floor = max(
|
||||
0,
|
||||
self::calculateDexTargetFloor(
|
||||
(int) $targetDex[$idx],
|
||||
$env,
|
||||
$dexTargetRatio
|
||||
) - max(0, (int) ($dexConsumed[$key] ?? 0))
|
||||
);
|
||||
$dexFloor[$key] = $floor;
|
||||
$current = (int) $general->getVar($key);
|
||||
if ($targetChanged || $dexTargetRatioChanged) {
|
||||
$result = CentennialAllStarGrowth::replaceTarget(
|
||||
$current,
|
||||
(int) ($granted[$key] ?? 0),
|
||||
$floor
|
||||
);
|
||||
} else {
|
||||
$result = CentennialAllStarGrowth::advance(
|
||||
$current,
|
||||
(int) ($granted[$key] ?? 0),
|
||||
$floor
|
||||
);
|
||||
}
|
||||
if ($result['value'] !== $current) {
|
||||
$general->updateVar($key, $result['value']);
|
||||
$changed = true;
|
||||
}
|
||||
$granted[$key] = $result['granted'];
|
||||
}
|
||||
|
||||
$oldEventSpecial = $aux['eventSpecialDomestic'] ?? null;
|
||||
if ($targetChanged && $oldEventSpecial !== null
|
||||
&& $general->getVar('special') === $oldEventSpecial
|
||||
) {
|
||||
$general->updateVar(
|
||||
'special',
|
||||
$aux['naturalSpecialDomestic'] ?? GameConst::$defaultSpecialDomestic
|
||||
);
|
||||
$changed = true;
|
||||
$aux['eventSpecialDomestic'] = null;
|
||||
}
|
||||
|
||||
$targetSpecial = $targetInfo['specialDomestic'] ?? null;
|
||||
if ($progress >= self::TRAIT_UNLOCK_PROGRESS && is_string($targetSpecial) && $targetSpecial !== '') {
|
||||
if (($aux['naturalSpecialDomestic'] ?? null) === null) {
|
||||
$aux['naturalSpecialDomestic'] = $general->getVar('special');
|
||||
}
|
||||
if ($general->getVar('special') !== $targetSpecial) {
|
||||
$general->updateVar('special', $targetSpecial);
|
||||
$changed = true;
|
||||
}
|
||||
$aux['eventSpecialDomestic'] = $targetSpecial;
|
||||
}
|
||||
|
||||
$previousMilestone = (int) ($aux['milestone'] ?? 0);
|
||||
$milestone = min(5, (int) floor($progress * 5 + 0.0000001));
|
||||
$previousDexExtensionMilestone = (int) ($aux['dexExtensionMilestone'] ?? 0);
|
||||
$dexExtensionProgress = self::calculateDexExtensionProgress($env);
|
||||
$dexExtensionMilestone = $hasDexExtensionTarget
|
||||
? min(5, (int) floor($dexExtensionProgress * 5 + 0.0000001))
|
||||
: 0;
|
||||
$aux['targetId'] = $targetId;
|
||||
$aux['granted'] = $granted;
|
||||
$aux['dexConsumed'] = $dexConsumed;
|
||||
$aux['dexFloor'] = $dexFloor;
|
||||
$aux['progressMonth'] = max((int) ($aux['progressMonth'] ?? -1), $progressMonth);
|
||||
$aux['milestone'] = max($previousMilestone, $milestone);
|
||||
$aux['dexExtensionMilestone'] = max(
|
||||
$previousDexExtensionMilestone,
|
||||
$dexExtensionMilestone
|
||||
);
|
||||
$aux['userInitialStats'] = $nextUserInitialStats;
|
||||
$aux['dexTargetRatio'] = $dexTargetRatio;
|
||||
$general->setAuxVar(self::AUX_KEY, $aux);
|
||||
|
||||
return [
|
||||
'progress' => $progress,
|
||||
'milestone' => $milestone,
|
||||
'previousMilestone' => $previousMilestone,
|
||||
'dexExtensionMilestone' => $dexExtensionMilestone,
|
||||
'previousDexExtensionMilestone' => $previousDexExtensionMilestone,
|
||||
'hasDexExtensionTarget' => $hasDexExtensionTarget,
|
||||
'targetChanged' => $targetChanged,
|
||||
'changed' => $changed
|
||||
|| $targetChanged
|
||||
|| $milestone > $previousMilestone
|
||||
|| $dexExtensionMilestone > $previousDexExtensionMilestone,
|
||||
];
|
||||
}
|
||||
|
||||
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 dexTargetRatioForNPCType(int $npcType): float
|
||||
{
|
||||
return in_array($npcType, [3, 4], true)
|
||||
? GameConst::$centennialNpcDexTargetRatio
|
||||
: 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;
|
||||
}
|
||||
self::initializeGeneratedNPC($general, $targetInfo);
|
||||
$result = self::applyTarget(
|
||||
$general,
|
||||
$targetInfo,
|
||||
$env,
|
||||
self::NPC_PROGRESS_MULTIPLIER,
|
||||
GameConst::$centennialNpcDexTargetRatio
|
||||
);
|
||||
$general->applyDB($db);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keeps the ordinary M/G-general stat total while aligning its strong,
|
||||
* middle, and weak stats with the selected all-star target. The target's
|
||||
* creation-date growth floor is applied immediately afterwards.
|
||||
*/
|
||||
public static function initializeGeneratedNPC(
|
||||
General $general,
|
||||
array $targetInfo
|
||||
): void {
|
||||
$generatedStats = [];
|
||||
foreach (self::STAT_KEYS as $key) {
|
||||
$generatedStats[$key] = (int) $general->getVar($key);
|
||||
}
|
||||
$initialStats = self::calculateGeneratedNPCInitialStats(
|
||||
$targetInfo,
|
||||
$generatedStats
|
||||
);
|
||||
foreach (self::STAT_KEYS as $key) {
|
||||
$general->updateVar($key, $initialStats[$key]);
|
||||
}
|
||||
foreach (self::DEX_KEYS as $key) {
|
||||
$general->updateVar($key, 0);
|
||||
}
|
||||
$general->setAuxVar(self::AUX_KEY, self::initialAux($targetInfo));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $targetInfo
|
||||
* @param array{leadership:int,strength:int,intel:int} $generatedStats
|
||||
* @return array{leadership:int,strength:int,intel:int}
|
||||
*/
|
||||
public static function calculateGeneratedNPCInitialStats(
|
||||
array $targetInfo,
|
||||
array $generatedStats
|
||||
): array {
|
||||
$targetOrder = self::STAT_KEYS;
|
||||
$keyOrder = array_flip(self::STAT_KEYS);
|
||||
usort(
|
||||
$targetOrder,
|
||||
static function (string $lhs, string $rhs) use ($targetInfo, $keyOrder): int {
|
||||
$targetCompare = (int) ($targetInfo[$rhs] ?? 0)
|
||||
<=> (int) ($targetInfo[$lhs] ?? 0);
|
||||
return $targetCompare !== 0
|
||||
? $targetCompare
|
||||
: $keyOrder[$lhs] <=> $keyOrder[$rhs];
|
||||
}
|
||||
);
|
||||
|
||||
$generatedValues = array_map(
|
||||
static fn (string $key): int => (int) ($generatedStats[$key] ?? 0),
|
||||
self::STAT_KEYS
|
||||
);
|
||||
rsort($generatedValues, SORT_NUMERIC);
|
||||
|
||||
$result = array_fill_keys(self::STAT_KEYS, 0);
|
||||
foreach ($targetOrder as $idx => $key) {
|
||||
$result[$key] = $generatedValues[$idx];
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the event-backed part of a dex conversion with the converted
|
||||
* value and consumes any guaranteed floor crossed by the source value.
|
||||
* This keeps the monthly floor from refilling points already converted.
|
||||
*/
|
||||
public static function reconcileDexConversion(
|
||||
General $general,
|
||||
string $sourceKey,
|
||||
string $destinationKey,
|
||||
int $sourceBefore,
|
||||
int $sourceAfter,
|
||||
int $destinationBefore,
|
||||
int $destinationAfter,
|
||||
float $convertCoeff
|
||||
): void {
|
||||
if (!in_array($sourceKey, self::DEX_KEYS, true)
|
||||
|| !in_array($destinationKey, self::DEX_KEYS, true)
|
||||
|| $sourceKey === $destinationKey
|
||||
) {
|
||||
throw new \InvalidArgumentException('invalid dex conversion keys');
|
||||
}
|
||||
if ($convertCoeff < 0 || $convertCoeff > 1) {
|
||||
throw new \InvalidArgumentException('dex conversion coefficient must be between 0 and 1');
|
||||
}
|
||||
|
||||
$sourceDecrease = max(0, $sourceBefore - $sourceAfter);
|
||||
$destinationIncrease = max(0, $destinationAfter - $destinationBefore);
|
||||
if ($sourceDecrease === 0 && $destinationIncrease === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$aux = $general->getAuxVar(self::AUX_KEY);
|
||||
if (!is_array($aux)) {
|
||||
return;
|
||||
}
|
||||
$granted = is_array($aux['granted'] ?? null)
|
||||
? $aux['granted']
|
||||
: array_fill_keys(array_merge(self::STAT_KEYS, self::DEX_KEYS), 0);
|
||||
$dexConsumed = is_array($aux['dexConsumed'] ?? null)
|
||||
? $aux['dexConsumed']
|
||||
: array_fill_keys(self::DEX_KEYS, 0);
|
||||
$dexFloor = is_array($aux['dexFloor'] ?? null)
|
||||
? $aux['dexFloor']
|
||||
: [];
|
||||
|
||||
$sourceGrantedBefore = min(
|
||||
max(0, $sourceBefore),
|
||||
max(0, (int) ($granted[$sourceKey] ?? 0))
|
||||
);
|
||||
/*
|
||||
* A dex value can contain both organic and event-backed points. Split
|
||||
* the actual command deltas by their share of the source total instead
|
||||
* of consuming either bucket first. This keeps the combined ownership
|
||||
* stable when conversion and target reselection are interleaved.
|
||||
*/
|
||||
$eventGrantRemoved = $sourceBefore > 0
|
||||
? intdiv($sourceDecrease * $sourceGrantedBefore, $sourceBefore)
|
||||
: 0;
|
||||
$sourceGrantedAfter = max(0, $sourceGrantedBefore - $eventGrantRemoved);
|
||||
|
||||
$destinationGrantedBefore = min(
|
||||
max(0, $destinationBefore),
|
||||
max(0, (int) ($granted[$destinationKey] ?? 0))
|
||||
);
|
||||
$eventGrantTransferred = $sourceBefore > 0
|
||||
? intdiv($destinationIncrease * $sourceGrantedBefore, $sourceBefore)
|
||||
: 0;
|
||||
$eventGrantTransferred = min(
|
||||
$destinationIncrease,
|
||||
$eventGrantRemoved,
|
||||
$eventGrantTransferred
|
||||
);
|
||||
$granted[$sourceKey] = $sourceGrantedAfter;
|
||||
$granted[$destinationKey] = min(
|
||||
max(0, $destinationAfter),
|
||||
$destinationGrantedBefore + $eventGrantTransferred
|
||||
);
|
||||
|
||||
$sourceFloor = max(
|
||||
0,
|
||||
(int) ($dexFloor[$sourceKey] ?? $sourceBefore)
|
||||
);
|
||||
$gapBefore = max(0, $sourceFloor - $sourceBefore);
|
||||
$gapAfter = max(0, $sourceFloor - $sourceAfter);
|
||||
$dexConsumed[$sourceKey] = max(
|
||||
0,
|
||||
(int) ($dexConsumed[$sourceKey] ?? 0)
|
||||
+ max(0, $gapAfter - $gapBefore)
|
||||
);
|
||||
|
||||
$aux['granted'] = $granted;
|
||||
$aux['dexConsumed'] = $dexConsumed;
|
||||
$general->setAuxVar(self::AUX_KEY, $aux);
|
||||
}
|
||||
|
||||
public static function recordableValue(General $general, string $key): int
|
||||
{
|
||||
$aux = $general->getAuxVar(self::AUX_KEY);
|
||||
$granted = is_array($aux) && is_array($aux['granted'] ?? null)
|
||||
? (int) ($aux['granted'][$key] ?? 0)
|
||||
: 0;
|
||||
return CentennialAllStarGrowth::recordableValue((int) $general->getVar($key), $granted);
|
||||
}
|
||||
|
||||
public static function recordableRawValue(array $general, string $key): int
|
||||
{
|
||||
$aux = Json::decode($general['aux'] ?? '{}');
|
||||
$eventAux = is_array($aux[self::AUX_KEY] ?? null) ? $aux[self::AUX_KEY] : [];
|
||||
$granted = is_array($eventAux['granted'] ?? null)
|
||||
? (int) ($eventAux['granted'][$key] ?? 0)
|
||||
: 0;
|
||||
return CentennialAllStarGrowth::recordableValue((int) ($general[$key] ?? 0), $granted);
|
||||
}
|
||||
}
|
||||
@@ -181,7 +181,7 @@ class che_랜덤임관 extends Command\GeneralCommand
|
||||
if ($notIn) {
|
||||
|
||||
$rawGeneralsCnt = $db->query(
|
||||
"SELECT g.`nation`, n.`gennum`, n.name, SUM((ra.value + 1000)/(rb.value + 1000)*(CASE WHEN g.`npc` < 2 THEN 1.2 ELSE 1 END)*(CASE WHEN g.`leadership` >= 40 THEN g.`leadership` ELSE 0 END)) AS warpower, SUM(SQRT(g.intel * g.strength) * 2 + g.leadership / 2)/5 AS develpower
|
||||
"SELECT g.`nation`, n.`gennum`, n.name, SUM((ra.value + 50000)/(rb.value + 50000)*(CASE WHEN g.`npc` < 2 THEN 1.15 ELSE 1 END)*(CASE WHEN g.`leadership` >= 40 THEN g.`leadership` ELSE 0 END)) AS warpower, SUM(SQRT(g.intel * g.strength) * 2 + g.leadership / 2)/5 AS develpower
|
||||
FROM general AS g
|
||||
LEFT JOIN `rank_data` AS ra ON g.`no` = ra.general_id AND ra.`type` = 'killcrew_person'
|
||||
LEFT JOIN `rank_data` AS rb ON g.`no` = rb.general_id AND rb.`type` = 'deathcrew_person'
|
||||
@@ -193,7 +193,7 @@ class che_랜덤임관 extends Command\GeneralCommand
|
||||
);
|
||||
} else {
|
||||
$rawGeneralsCnt = $db->query(
|
||||
"SELECT g.`nation`, n.`gennum`, n.name, SUM((ra.value + 100)/(rb.value + 100)*(CASE WHEN g.`npc` < 2 THEN 1.2 ELSE 1 END)*(CASE WHEN g.`leadership` >= 40 THEN g.`leadership` ELSE 0 END)) AS warpower, SUM(SQRT(g.intel * g.strength) * 2 + g.leadership / 2)/5 AS develpower
|
||||
"SELECT g.`nation`, n.`gennum`, n.name, SUM((ra.value + 50000)/(rb.value + 50000)*(CASE WHEN g.`npc` < 2 THEN 1.15 ELSE 1 END)*(CASE WHEN g.`leadership` >= 40 THEN g.`leadership` ELSE 0 END)) AS warpower, SUM(SQRT(g.intel * g.strength) * 2 + g.leadership / 2)/5 AS develpower
|
||||
FROM general AS g
|
||||
LEFT JOIN `rank_data` AS ra ON g.`no` = ra.general_id AND ra.`type` = 'killcrew_person'
|
||||
LEFT JOIN `rank_data` AS rb ON g.`no` = rb.general_id AND rb.`type` = 'deathcrew_person'
|
||||
@@ -208,7 +208,7 @@ class che_랜덤임관 extends Command\GeneralCommand
|
||||
foreach ($rawGeneralsCnt as $nation) {
|
||||
$calcCnt = $nation['warpower'] + $nation['develpower'];
|
||||
|
||||
if ($general->getNPCType() < 2 && Util::starts_with($nation['name'], 'ⓤ')) {
|
||||
if ($general->getNPCType() < 2 && str_starts_with($nation['name'], 'ⓤ')) {
|
||||
$calcCnt *= 100;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ use function \sammo\tryUniqueItemLottery;
|
||||
|
||||
use \sammo\Constraint\ConstraintHelper;
|
||||
use sammo\StaticEventHandler;
|
||||
use sammo\CentennialAllStarGrowthService;
|
||||
|
||||
class che_숙련전환 extends Command\GeneralCommand
|
||||
{
|
||||
@@ -157,6 +158,7 @@ class che_숙련전환 extends Command\GeneralCommand
|
||||
$logger = $general->getLogger();
|
||||
|
||||
$srcDex = $general->getVar('dex' . $this->srcArmType);
|
||||
$destDex = $general->getVar('dex' . $this->destArmType);
|
||||
$cutDex = Util::toInt($srcDex * static::$decreaseCoeff);
|
||||
$cutDexText = number_format($cutDex);
|
||||
$addDex = Util::toInt($cutDex * static::$convertCoeff);
|
||||
@@ -164,6 +166,19 @@ class che_숙련전환 extends Command\GeneralCommand
|
||||
|
||||
$general->increaseVar('dex' . $this->srcArmType, -$cutDex);
|
||||
$general->increaseVar('dex' . $this->destArmType, $addDex);
|
||||
// 100기 이벤트 지급분을 목적 숙련으로 옮기고 소비한 성장 하한은 다시 채우지 않는다.
|
||||
if (CentennialAllStarGrowthService::isActive()) {
|
||||
CentennialAllStarGrowthService::reconcileDexConversion(
|
||||
$general,
|
||||
'dex' . $this->srcArmType,
|
||||
'dex' . $this->destArmType,
|
||||
$srcDex,
|
||||
$general->getVar('dex' . $this->srcArmType),
|
||||
$destDex,
|
||||
$general->getVar('dex' . $this->destArmType),
|
||||
static::$convertCoeff
|
||||
);
|
||||
}
|
||||
|
||||
$josaUl = JosaUtil::pick($cutDex, '을');
|
||||
$josaRo = JosaUtil::pick($addDex, '로');
|
||||
|
||||
@@ -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, '라');
|
||||
|
||||
@@ -164,7 +164,7 @@ class che_출병 extends Command\GeneralCommand
|
||||
|
||||
$currDist = 999;
|
||||
|
||||
$minDist = Util::array_first_key($distanceList);
|
||||
$minDist = array_key_first($distanceList);
|
||||
do {
|
||||
//1: 최단 거리 도시 중 공격 대상이 있는가 확인
|
||||
//2: 최단 거리 + 1 도시 중 공격 대상이 있는가 확인
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -162,7 +162,7 @@ class che_초토화 extends Command\NationCommand{
|
||||
$aux = $this->nation['aux'];
|
||||
|
||||
if($destCity['level'] >= 8){
|
||||
$aux[NationAuxKey::did_특성초토화->value] = $aux[NationAuxKey::did_특성초토화->value] ?? 0 + 1;
|
||||
$aux[NationAuxKey::did_특성초토화->value] = ($aux[NationAuxKey::did_특성초토화->value] ?? 0) + 1;
|
||||
}
|
||||
|
||||
$db->update('general', [
|
||||
|
||||
@@ -50,7 +50,7 @@ class AllowJoinDestNation extends Constraint{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(($this->general['npc']??2 < 2) && \str_starts_with($this->destNation['name'], 'ⓤ')){
|
||||
if((($this->general['npc']??2) < 2) && \str_starts_with($this->destNation['name'], 'ⓤ')){
|
||||
$this->reason = "유저장은 태수국에 임관할 수 없습니다.";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace sammo\Event\Action;
|
||||
|
||||
use sammo\ActionLogger;
|
||||
use sammo\CentennialAllStarGrowthService;
|
||||
use sammo\DB;
|
||||
use sammo\General;
|
||||
use sammo\Json;
|
||||
|
||||
class AdvanceCentennialAllStar extends \sammo\Event\Action
|
||||
{
|
||||
public function run(array $env)
|
||||
{
|
||||
if (!CentennialAllStarGrowthService::isActive()) {
|
||||
return [__CLASS__, 0];
|
||||
}
|
||||
|
||||
$db = DB::db();
|
||||
$updated = 0;
|
||||
foreach ($db->query(
|
||||
'SELECT general.no, select_pool.info
|
||||
FROM general
|
||||
JOIN select_pool ON select_pool.general_id = general.no'
|
||||
) as $row) {
|
||||
$general = General::createObjFromDB((int) $row['no']);
|
||||
$targetInfo = Json::decode($row['info']);
|
||||
$result = CentennialAllStarGrowthService::applyTarget(
|
||||
$general,
|
||||
$targetInfo,
|
||||
$env,
|
||||
CentennialAllStarGrowthService::progressMultiplierFor($general),
|
||||
CentennialAllStarGrowthService::dexTargetRatioForNPCType(
|
||||
$general->getNPCType()
|
||||
)
|
||||
);
|
||||
|
||||
if ($result['milestone'] > $result['previousMilestone']) {
|
||||
$percent = $result['milestone'] * 20;
|
||||
$general->getLogger()->pushGeneralActionLog(
|
||||
"<L>올스타 동조율</>이 <C>{$percent}%</>에 도달했습니다!",
|
||||
ActionLogger::PLAIN
|
||||
);
|
||||
$general->getLogger()->pushGeneralHistoryLog(
|
||||
"<L>올스타 동조율 {$percent}% 달성</>"
|
||||
);
|
||||
}
|
||||
if ($result['hasDexExtensionTarget']
|
||||
&& $result['dexExtensionMilestone'] > $result['previousDexExtensionMilestone']
|
||||
) {
|
||||
$percent = $result['dexExtensionMilestone'] * 20;
|
||||
$general->getLogger()->pushGeneralActionLog(
|
||||
"<L>추가 숙련 동조율</>이 <C>{$percent}%</>에 도달했습니다!",
|
||||
ActionLogger::PLAIN
|
||||
);
|
||||
$general->getLogger()->pushGeneralHistoryLog(
|
||||
"<L>추가 숙련 동조율 {$percent}% 달성</>"
|
||||
);
|
||||
}
|
||||
if ($general->applyDB($db)) {
|
||||
$updated++;
|
||||
}
|
||||
}
|
||||
|
||||
return [__CLASS__, $updated];
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,13 @@ class ChangeCity extends \sammo\Event\Action{
|
||||
'def'=>true,
|
||||
'wall'=>true,
|
||||
'trade'=>true,
|
||||
|
||||
'pop_max'=>true,
|
||||
'agri_max'=>true,
|
||||
'comm_max'=>true,
|
||||
'secu_max'=>true,
|
||||
'def_max'=>true,
|
||||
'wall_max'=>true,
|
||||
];
|
||||
const REGEXP_PERCENT = '/^(\d+(\.\d+)?)%$/';// 123.5% [1]=float
|
||||
const REGEXP_MATH = '/^([\+\-\/\*])(\d+(\.\d+)?)$/'; //+30 [1]=기호, [2]=float
|
||||
@@ -120,6 +127,16 @@ class ChangeCity extends \sammo\Event\Action{
|
||||
return DB::db()->sqleval('ROUND(%b * %d, 0)', $keyMax, $value/100);
|
||||
}
|
||||
|
||||
if(str_ends_with($key, '_max') && preg_match(self::REGEXP_MATH, $value, $matches)){
|
||||
//key가 아예 최대치에 대한 값이면 연산은 결과값이 0보다만 높게.
|
||||
$op = $matches[1];
|
||||
$value = $matches[2];
|
||||
if($op == '/' && $value == 0){
|
||||
throw new \InvalidArgumentException('0으로 나눌 수 없습니다.');
|
||||
}
|
||||
return DB::db()->sqleval('greatest(0, ROUND(%b '.$op.' %i, 0))', $key, $value);
|
||||
}
|
||||
|
||||
if(preg_match(self::REGEXP_MATH, $value, $matches)){
|
||||
$op = $matches[1];
|
||||
$value = $matches[2];
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -94,6 +94,11 @@ class GameConstBase
|
||||
/** @var int 최대 레벨 */
|
||||
public static $maxLevel = 255;
|
||||
|
||||
/** @var int 기술등급 허용 증가 단위 년 */
|
||||
public static $techLevelIncYear = 5;
|
||||
/** @var int 초기 기술등급 */
|
||||
public static $initialAllowedTechLevel = 1;
|
||||
|
||||
/** @var int 최소 인구 증가량 */
|
||||
public static $basePopIncreaseAmount = 5000;
|
||||
/** @var int 증축시 인구 증가량 */
|
||||
@@ -109,6 +114,9 @@ class GameConstBase
|
||||
/** @var int 징병 허용 최소 인구 */
|
||||
public static $minAvailableRecruitPop = 30000;
|
||||
|
||||
/** @var int 점령 후 일반 도시 성벽 */
|
||||
public static $defaultCityWall = 1000;
|
||||
|
||||
/** @var int 초기 제한시 장수 제한 */
|
||||
public static $initialNationGenLimit = 10;
|
||||
|
||||
@@ -176,6 +184,10 @@ class GameConstBase
|
||||
/** @var array 선택할 수 없으나 게임 내에 유효한 장수 내정 특기 */
|
||||
public static $optionalSpecialDomestic = [
|
||||
'None',
|
||||
'che_event_귀병', 'che_event_신산', 'che_event_환술', 'che_event_집중', 'che_event_신중',
|
||||
'che_event_반계', 'che_event_보병', 'che_event_궁병', 'che_event_기병', 'che_event_공성',
|
||||
'che_event_돌격', 'che_event_무쌍', 'che_event_견고', 'che_event_위압', 'che_event_저격',
|
||||
'che_event_필살', 'che_event_징병', 'che_event_의술', 'che_event_격노', 'che_event_척사',
|
||||
];
|
||||
|
||||
/** @var string 기본 전투 특기 */
|
||||
@@ -408,6 +420,8 @@ class GameConstBase
|
||||
public static $retirementYear = 80;
|
||||
|
||||
public static $targetGeneralPool = 'RandomNameGeneral';
|
||||
/** @var float 100기 올스타 NPC의 원본 목표 대비 최종 숙련 비율 */
|
||||
public static $centennialNpcDexTargetRatio = 0.4;
|
||||
public static $generalPoolAllowOption = ['stat', 'ego', 'picture'];
|
||||
|
||||
public static $randGenFirstName = [
|
||||
|
||||
@@ -2084,7 +2084,7 @@ class GeneralAI
|
||||
}
|
||||
}
|
||||
|
||||
$finalCityID = Util::array_first_key($cityScoreList);
|
||||
$finalCityID = array_key_first($cityScoreList);
|
||||
$dist = $distanceList[$capital][$finalCityID];
|
||||
$targetCityID = $finalCityID;
|
||||
if ($dist > 1) {
|
||||
@@ -3129,8 +3129,14 @@ class GeneralAI
|
||||
$db = DB::db();
|
||||
|
||||
$dupLord = $db->queryFirstField('SELECT COUNT(*) FROM general WHERE officer_level = 12 AND city = %i', $this->general->getCityID());
|
||||
if($dupLord <= 1 && !in_array($this->general->getRawCity()['level'], [5, 6])) {
|
||||
return null;
|
||||
if($dupLord <= 1) {
|
||||
if ($this->general->getRawCity() === null) {
|
||||
$rawCity = $db->queryFirstRow('SELECT * FROM city WHERE city = %i', $this->general->getCityID());
|
||||
$this->general->setRawCity($rawCity);
|
||||
}
|
||||
if (in_array($this->general->getRawCity()['level'], [5, 6])) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
$general = $this->general;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace sammo\GeneralPool;
|
||||
|
||||
use sammo\AbsFromUserPool;
|
||||
use sammo\CentennialAllStarGrowthService;
|
||||
use sammo\Json;
|
||||
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;
|
||||
$initialInfo = $info;
|
||||
foreach ([
|
||||
'leadership',
|
||||
'strength',
|
||||
'intel',
|
||||
'experience',
|
||||
'dedication',
|
||||
'dex',
|
||||
'specialDomestic',
|
||||
'specialWar',
|
||||
] as $targetKey) {
|
||||
unset($initialInfo[$targetKey]);
|
||||
}
|
||||
|
||||
parent::__construct($db, $rng, $initialInfo, $validUntil);
|
||||
$this->info = $targetInfo;
|
||||
CentennialAllStarGrowthService::attachInitialTarget($this->builder, $targetInfo);
|
||||
}
|
||||
|
||||
public static function getPoolName(): string
|
||||
{
|
||||
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'));
|
||||
$columns = $jsonData['columns'];
|
||||
$sqlValues = [];
|
||||
foreach ($jsonData['data'] as $idx => $rawItem) {
|
||||
if (count($rawItem) !== count($columns)) {
|
||||
throw new \RuntimeException(($rawItem[0] ?? (string) $idx) . ' Error');
|
||||
}
|
||||
$item = array_combine($columns, $rawItem);
|
||||
$uniqueName = sprintf('A100%04d', $idx + 1);
|
||||
$item['uniqueName'] = $uniqueName;
|
||||
$item['event100Growth'] = true;
|
||||
$sqlValues[] = [
|
||||
'unique_name' => $uniqueName,
|
||||
'info' => Json::encode($item),
|
||||
];
|
||||
}
|
||||
$db->insert('select_pool', $sqlValues);
|
||||
}
|
||||
}
|
||||
@@ -334,11 +334,19 @@ class InheritancePointManager
|
||||
$inheritStor->setValue($key, [$point, $aux]);
|
||||
}
|
||||
|
||||
$oldInheritStor = KVStorage::getStorage(DB::db(), "inheritance_result");
|
||||
$db = DB::db();
|
||||
$serverID = UniqueConst::$serverID;
|
||||
$year = $gameStor->year;
|
||||
$month = $gameStor->month;
|
||||
$oldInheritStor->setValue("{$serverID}_{$ownerID}_{$general->getID()}_{$year}_{$month}", $inheritStor->getAll(true));
|
||||
|
||||
$db->insert('inheritance_result', [
|
||||
'server_id'=>$serverID,
|
||||
'owner'=>$ownerID,
|
||||
'general_id'=>$general->getID(),
|
||||
'year'=>$year,
|
||||
'month'=>$month,
|
||||
'value'=>Json::encode($inheritStor->getAll(true))
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ abstract class TriggerCaller{
|
||||
return $this;
|
||||
}
|
||||
|
||||
$lastKey = Util::array_last_key($this->triggerListByPriority);
|
||||
$lastKey = array_key_last($this->triggerListByPriority);
|
||||
if($lastKey < $priority){
|
||||
$this->triggerListByPriority[$priority] = [$uniqueID=>$trigger];
|
||||
return $this;
|
||||
|
||||
@@ -422,7 +422,7 @@ class TurnExecutionHelper
|
||||
|
||||
$date = TimeUtil::now(true);
|
||||
// 최종 처리 월턴의 다음 월턴시간 구함
|
||||
$lastExecuted = $gameStor->turntime;
|
||||
//$lastExecuted = $gameStor->turntime;
|
||||
$prevTurn = cutTurn($gameStor->turntime, $gameStor->turnterm);
|
||||
$nextTurn = addTurn($prevTurn, $gameStor->turnterm);
|
||||
|
||||
@@ -455,9 +455,6 @@ class TurnExecutionHelper
|
||||
$gameStor->turntime = $currentTurn;
|
||||
}
|
||||
unlock();
|
||||
if($lastExecuted !== $currentTurn){
|
||||
$executed = true;
|
||||
}
|
||||
return $gameStor->turntime;
|
||||
}
|
||||
|
||||
@@ -516,10 +513,6 @@ class TurnExecutionHelper
|
||||
$gameStor->resetCache();
|
||||
unlock();
|
||||
|
||||
if($lastExecuted !== $currentTurn){
|
||||
$executed = true;
|
||||
}
|
||||
|
||||
return $turntime;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,974 @@
|
||||
{
|
||||
"title":"【역사모드2-2】 반동탁연합 결성(정사)",
|
||||
"startYear":187,
|
||||
"life":1,
|
||||
"fiction":0,
|
||||
"const":{
|
||||
"defaultMaxGeneral":600
|
||||
},
|
||||
"nation":[
|
||||
["동탁", "#A9A9A9", 12000, 80000, "천자를 등에 업은 동탁", 2000, "명가", 5, ["낙양", "호로", "사수", "함곡", "장안", "홍농", "사곡", "안정"]],
|
||||
["조조", "#0000FF", 10000, 40000, "동탁의거의 조조", 1000, "법가", 1, ["진류"]],
|
||||
["유대", "#800000", 10000, 10000, "연주자사 유대, 유요의 형", 1000, "병가", 3, ["복양"]],
|
||||
["공손찬", "#FF00FF", 10000, 10000, "백마의 공손찬", 1000, "병가", 1, ["북평"]],
|
||||
["장양", "#7B68EE", 10000, 10000, "병주자사 장양", 1000, "병가", 3, ["진양"]],
|
||||
["유언", "#483D8B", 10000, 30000, "익주목 유언", 1000, "유가", 4, ["성도", "자동", "면죽"]],
|
||||
["원술", "#FFC0CB", 15000, 40000, "후장군 원술", 1000, "병가", 2, ["완"]],
|
||||
["도겸", "#00FF00", 10000, 10000, "서주목 도겸", 1000, "유가", 4, ["서주", "하비.광릉"]],
|
||||
["진온", "#808000", 10000, 10000, "양주자사 진온", 1000, "유가", 3, ["수춘", "합비"]],
|
||||
["마등", "#808000", 10000, 10000, "서량태수 마등", 1000, "병가", 1, ["서량"]],
|
||||
["공손도", "#A0522D", 10000, 10000, "요동의 왕 공손도", 1000, "덕가", 1, ["안평"]],
|
||||
["한복", "#A0522D", 10000, 10000, "기주자사 한복", 1000, "병가", 3, ["업"]],
|
||||
["유표", "#E0FFFF", 10000, 10000, "형주자사 유표", 1000, "유가", 3, ["양양", "장판", "강하"]],
|
||||
["장로", "#20B2AA", 10000, 10000, "사실 이때 없는 애지만 장로", 1000, "오두미도", 1, ["한중"]],
|
||||
["왕광", "#483D8B", 10000, 10000, "하내태수 왕광", 1000, "유가", 1, ["하내"]],
|
||||
["유우", "#6495ED", 10000, 10000, "유주자사 유우", 1000, "유가", 5, ["계", "역경"]],
|
||||
["원소", "#FFFF00", 10000, 10000, "발해태수 원소", 1000, "병가", 1, ["남피"]],
|
||||
["사섭", "#800000", 10000, 10000, "교주자사 사섭", 1000, "유가", 3, ["교지", "남해"]],
|
||||
["공융", "#800080", 10000, 10000, "북해태수 공융", 1000, "유가", 1, ["북해"]],
|
||||
["한수", "#FFFFFF", 10000, 10000, "서량의 모반자 한수", 1000, "병가", 1, ["천수"]],
|
||||
["공주", "#800080", 10000, 10000, "예주 자사 공주", 1000, "유가", 3, ["초"]],
|
||||
["유총", "#7B68EE", 10000, 10000, "진민왕 유총", 1000, "덕가", 6, ["여남"]]
|
||||
|
||||
],
|
||||
"diplomacy":[
|
||||
[1,2,1,36],
|
||||
[1,3,1,36],
|
||||
[1,5,1,36],
|
||||
[1,6,1,36],
|
||||
[1,7,1,36],
|
||||
[1,8,1,36],
|
||||
[1,9,1,36],
|
||||
[1,10,1,36],
|
||||
[1,11,1,36],
|
||||
[1,12,1,36],
|
||||
[1,13,1,36],
|
||||
[1,14,1,36],
|
||||
[1,15,1,36],
|
||||
[1,16,1,36],
|
||||
[1,17,1,36],
|
||||
[1,18,1,36],
|
||||
[1,19,1,36],
|
||||
[1,20,1,36],
|
||||
[1,21,1,36],
|
||||
[1,22,1,36],
|
||||
|
||||
[2,3,7,24],
|
||||
[2,5,7,24],
|
||||
[2,6,7,24],
|
||||
[2,7,7,24],
|
||||
[2,8,7,24],
|
||||
[2,9,7,24],
|
||||
[2,10,7,24],
|
||||
[2,11,7,24],
|
||||
[2,12,7,24],
|
||||
[2,13,7,24],
|
||||
[2,14,7,24],
|
||||
[2,15,7,24],
|
||||
[2,16,7,24],
|
||||
[2,17,7,24],
|
||||
[2,18,7,24],
|
||||
[2,19,7,24],
|
||||
[2,20,7,24],
|
||||
[2,21,7,24],
|
||||
[2,22,7,24],
|
||||
|
||||
[3,5,7,24],
|
||||
[3,6,7,24],
|
||||
[3,7,7,24],
|
||||
[3,8,7,24],
|
||||
[3,9,7,24],
|
||||
[3,10,7,24],
|
||||
[3,11,7,24],
|
||||
[3,12,7,24],
|
||||
[3,13,7,24],
|
||||
[3,14,7,24],
|
||||
[3,15,7,24],
|
||||
[3,16,7,24],
|
||||
[3,17,7,24],
|
||||
[3,18,7,24],
|
||||
[3,19,7,24],
|
||||
[3,20,7,24],
|
||||
[3,21,7,24],
|
||||
[3,22,7,24],
|
||||
|
||||
[5,6,7,24],
|
||||
[5,7,7,24],
|
||||
[5,8,7,24],
|
||||
[5,9,7,24],
|
||||
[5,10,7,24],
|
||||
[5,11,7,24],
|
||||
[5,12,7,24],
|
||||
[5,13,7,24],
|
||||
[5,14,7,24],
|
||||
[5,15,7,24],
|
||||
[5,16,7,24],
|
||||
[5,17,7,24],
|
||||
[5,18,7,24],
|
||||
[5,19,7,24],
|
||||
[5,20,7,24],
|
||||
[5,21,7,24],
|
||||
[5,22,7,24],
|
||||
|
||||
[6,7,7,24],
|
||||
[6,8,7,24],
|
||||
[6,9,7,24],
|
||||
[6,10,7,24],
|
||||
[6,11,7,24],
|
||||
[6,12,7,24],
|
||||
[6,13,7,24],
|
||||
[6,14,7,24],
|
||||
[6,15,7,24],
|
||||
[6,16,7,24],
|
||||
[6,17,7,24],
|
||||
[6,18,7,24],
|
||||
[6,19,7,24],
|
||||
[6,20,7,24],
|
||||
[6,21,7,24],
|
||||
[6,22,7,24],
|
||||
|
||||
[7,8,7,24],
|
||||
[7,9,7,24],
|
||||
[7,10,7,24],
|
||||
[7,11,7,24],
|
||||
[7,12,7,24],
|
||||
[7,13,7,24],
|
||||
[7,14,7,24],
|
||||
[7,15,7,24],
|
||||
[7,16,7,24],
|
||||
[7,17,7,24],
|
||||
[7,18,7,24],
|
||||
[7,19,7,24],
|
||||
[7,20,7,24],
|
||||
[7,21,7,24],
|
||||
[7,22,7,24],
|
||||
|
||||
[8,9,7,24],
|
||||
[8,10,7,24],
|
||||
[8,11,7,24],
|
||||
[8,12,7,24],
|
||||
[8,13,7,24],
|
||||
[8,14,7,24],
|
||||
[8,15,7,24],
|
||||
[8,16,7,24],
|
||||
[8,17,7,24],
|
||||
[8,18,7,24],
|
||||
[8,19,7,24],
|
||||
[8,20,7,24],
|
||||
[8,21,7,24],
|
||||
[8,22,7,24],
|
||||
[9,10,7,24],
|
||||
[9,11,7,24],
|
||||
[9,12,7,24],
|
||||
[9,13,7,24],
|
||||
[9,14,7,24],
|
||||
[9,15,7,24],
|
||||
[9,16,7,24],
|
||||
[9,17,7,24],
|
||||
[9,18,7,24],
|
||||
[9,19,7,24],
|
||||
[9,20,7,24],
|
||||
[9,21,7,24],
|
||||
[9,22,7,24],
|
||||
[10,11,7,24],
|
||||
[10,12,7,24],
|
||||
[10,13,7,24],
|
||||
[10,14,7,24],
|
||||
[10,15,7,24],
|
||||
[10,16,7,24],
|
||||
[10,17,7,24],
|
||||
[10,18,7,24],
|
||||
[10,19,7,24],
|
||||
[10,20,7,24],
|
||||
[10,21,7,24],
|
||||
[10,22,7,24],
|
||||
[11,12,7,24],
|
||||
[11,13,7,24],
|
||||
[11,14,7,24],
|
||||
[11,15,7,24],
|
||||
[11,16,7,24],
|
||||
[11,17,7,24],
|
||||
[11,18,7,24],
|
||||
[11,19,7,24],
|
||||
[11,20,7,24],
|
||||
[11,21,7,24],
|
||||
[11,22,7,24],
|
||||
[12,13,7,24],
|
||||
[12,14,7,24],
|
||||
[12,15,7,24],
|
||||
[12,16,7,24],
|
||||
[12,17,7,24],
|
||||
[12,18,7,24],
|
||||
[12,19,7,24],
|
||||
[12,20,7,24],
|
||||
[12,21,7,24],
|
||||
[12,22,7,24],
|
||||
[13,14,7,24],
|
||||
[13,15,7,24],
|
||||
[13,16,7,24],
|
||||
[13,17,7,24],
|
||||
[13,18,7,24],
|
||||
[13,19,7,24],
|
||||
[13,20,7,24],
|
||||
[13,21,7,24],
|
||||
[13,22,7,24],
|
||||
[14,15,7,24],
|
||||
[14,16,7,24],
|
||||
[14,17,7,24],
|
||||
[14,18,7,24],
|
||||
[14,19,7,24],
|
||||
[14,20,7,24],
|
||||
[14,21,7,24],
|
||||
[14,22,7,24],
|
||||
[15,16,7,24],
|
||||
[15,17,7,24],
|
||||
[15,18,7,24],
|
||||
[15,19,7,24],
|
||||
[15,20,7,24],
|
||||
[15,21,7,24],
|
||||
[15,22,7,24],
|
||||
[16,17,7,24],
|
||||
[16,18,7,24],
|
||||
[16,19,7,24],
|
||||
[16,20,7,24],
|
||||
[16,21,7,24],
|
||||
[16,22,7,24],
|
||||
[17,18,7,24],
|
||||
[17,19,7,24],
|
||||
[17,20,7,24],
|
||||
[17,21,7,24],
|
||||
[17,22,7,24],
|
||||
[18,19,7,24],
|
||||
[18,20,7,24],
|
||||
[18,21,7,24],
|
||||
[18,22,7,24],
|
||||
[19,20,7,24],
|
||||
[19,21,7,24],
|
||||
[19,22,7,24],
|
||||
[20,21,7,24],
|
||||
[20,22,7,24],
|
||||
[21,22,7,24]
|
||||
],
|
||||
"general":[
|
||||
[0, "아회남", null, 0, null, 65, 74, 26, 0, 190, 225, null, null],
|
||||
[0, "이적", null, 0, null, 29, 24, 84, 0, 162, 273, null, null],
|
||||
[0, "윤상", null, 0, null, 51, 44, 62, 0, 194, 260, null, null],
|
||||
[0, "윤묵", null, 0, null, 26, 15, 66, 0, 183, 239, null, null],
|
||||
[0, "우금1", null, 0, null, 83, 78, 74, 0, 159, 221, null, null],
|
||||
[0, "위관", null, 0, null, 69, 46, 79, 0, 220, 291, null, null],
|
||||
[0, "원윤", null, 7, "완", 15, 14, 39, 1, 150, 199, null, null],
|
||||
[0, "원희", null, 17, "남피", 66, 51, 63, 1, 176, 207, null, null],
|
||||
[0, "염행", null, 20, "천수", 76, 84, 66, 1, 159, 215, null, null],
|
||||
[0, "염유", null, 0, "계", 71, 57, 71, 0, 168, 227, null, null],
|
||||
[0, "원술", null, 7, "완", 44, 65, 65, 12, 155, 199, null, null, "흥 마궁수 주제에 어딜 앞에 나서느냐! 썩 꺼져라!"],
|
||||
[0, "원상", null, 0, null, 63, 75, 42, 0, 179, 207, null, null],
|
||||
[0, "원소", null, 17, "남피", 81, 69, 70, 12, 154, 202, null, null, "안량과 문추만 이곳에 있엇어도…"],
|
||||
[0, "염상", null, 0, null, 30, 27, 71, 0, 158, 199, null, null],
|
||||
[0, "원담", null, 17, "남피", 60, 69, 30, 1, 173, 205, null, null],
|
||||
[0, "염포", null, 14, "한중", 29, 25, 82, 11, 167, 231, null, null],
|
||||
[0, "왕위", null, 0, null, 60, 70, 62, 0, 163, 222, null, null],
|
||||
[0, "왕윤", null, 1, "장안", 26, 6, 68, 1, 137, 192, null, null],
|
||||
[0, "왕항", null, 0, null, 76, 60, 61, 0, 184, 254, null, null],
|
||||
[0, "왕찬", null, 0, null, 15, 3, 79, 0, 177, 217, null, null],
|
||||
[0, "왕수", null, 19, "북해", 54, 27, 77, 11, 168, 218, null, null],
|
||||
[0, "왕숙", null, 0, null, 36, 22, 76, 0, 195, 256, null, null],
|
||||
[0, "왕준", null, 0, null, 81, 73, 77, 0, 206, 286, null, null],
|
||||
[0, "왕쌍", null, 0, null, 67, 88, 19, 0, 195, 228, null, null],
|
||||
[0, "왕창", null, 0, null, 74, 64, 79, 0, 188, 259, null, null],
|
||||
[0, "왕평", null, 0, null, 83, 79, 76, 0, 192, 248, null, null],
|
||||
[0, "왕보", null, 0, null, 62, 41, 79, 0, 171, 219, null, null],
|
||||
[0, "왕문", null, 0, null, 65, 64, 31, 0, 162, 205, null, null],
|
||||
[0, "왕릉", null, 0, null, 74, 64, 71, 0, 172, 251, null, null],
|
||||
[0, "왕루", null, 0, null, 28, 30, 79, 0, 173, 211, null, null],
|
||||
[0, "왕랑", null, 8, "서주", 47, 35, 79, 1, 167, 228, null, null],
|
||||
[0, "온회", null, 0, null, 62, 36, 74, 0, 178, 228, null, null],
|
||||
[0, "하안", null, 0, null, 15, 30, 72, 0, 190, 249, null, null],
|
||||
[0, "괴월", null, 13, "양양", 47, 27, 82, 11, 157, 214, null, null],
|
||||
[0, "괴량", null, 13, "양양", 68, 33, 88, 1, 155, 204, null, null],
|
||||
[0, "가화", null, 0, null, 49, 65, 41, 0, 176, 222, null, null],
|
||||
[0, "가규", null, 0, null, 78, 61, 84, 0, 177, 231, null, null],
|
||||
[0, "하의", null, 0, null, 56, 69, 36, 0, 161, 195, null, null],
|
||||
[0, "화흠", null, 1, "낙양", 33, 33, 82, 1, 157, 231, null, null],
|
||||
[0, "가후", null, 1, "안정", 88, 48, 97, 1, 147, 223, null, null],
|
||||
[0, "곽가", null, 0, null, 88, 15, 96, 0, 170, 207, null, null],
|
||||
[0, "악환", null, 0, null, 65, 82, 42, 0, 196, 251, null, null],
|
||||
[0, "곽사", null, 1, "안정", 64, 76, 13, 1, 146, 197, null, null],
|
||||
[0, "악취", null, 0, null, 53, 66, 61, 0, 157, 195, null, null],
|
||||
[0, "곽준", null, 0, null, 73, 66, 73, 0, 178, 217, null, null],
|
||||
[0, "학소", null, 0, null, 86, 79, 78, 0, 185, 229, null, null],
|
||||
[0, "악진", null, 2, "진류", 80, 84, 56, 1, 159, 218, null, null],
|
||||
[0, "곽도", null, 17, "남피", 52, 50, 82, 1, 157, 205, null, null],
|
||||
[0, "학맹", null, 0, null, 62, 67, 42, 0, 156, 300, null, null],
|
||||
[0, "곽유지", null, 0, null, 28, 9, 69, 0, 190, 259, null, null],
|
||||
[0, "곽익", null, 0, null, 73, 69, 69, 0, 207, 270, null, null],
|
||||
[0, "곽회", null, 0, null, 87, 78, 81, 0, 187, 255, null, null],
|
||||
[0, "하후위", null, 0, null, 71, 73, 49, 0, 204, 253, null, null],
|
||||
[0, "하후연", null, 2, "진류", 90, 91, 68, 1, 162, 219, null, null],
|
||||
[0, "하후은", null, 0, null, 63, 72, 53, 0, 180, 208, null, null],
|
||||
[0, "하후화", null, 0, null, 48, 51, 71, 0, 207, 265, null, null],
|
||||
[0, "하후혜", null, 0, null, 50, 44, 76, 0, 206, 242, null, null],
|
||||
[0, "하후현", null, 0, null, 54, 39, 76, 0, 208, 254, null, null],
|
||||
[0, "하후상", null, 0, null, 79, 72, 75, 0, 181, 225, null, null],
|
||||
[0, "하후덕", null, 0, null, 69, 73, 32, 0, 178, 218, null, null],
|
||||
[0, "하후돈", null, 2, "진류", 92, 90, 63, 1, 156, 220, null, null],
|
||||
[0, "하후패", null, 0, null, 79, 84, 77, 0, 202, 262, null, null],
|
||||
[0, "하후무", null, 0, null, 20, 7, 4, 0, 201, 259, null, null],
|
||||
[0, "가충", null, 0, null, 54, 31, 89, 0, 217, 282, null, null],
|
||||
[0, "하진", null, 0, null, 39, 40, 6, 0, 135, 189, null, null],
|
||||
[0, "화웅", null, 1, "호로", 88, 92, 61, 1, 155, 190, null, null],
|
||||
[0, "한윤", null, 7, "완", 26, 29, 65, 1, 155, 197, null, null],
|
||||
[0, "관우", null, 0, "진류", 97, 97, 79, 0, 162, 219, null, null, "술이 식기 전에 돌아오겠소"],
|
||||
[0, "환계", null, 7, "완", 25, 26, 68, 1, 156, 221, null, null],
|
||||
[0, "관해", null, 0, null, 70, 82, 10, 0, 160, 193, null, null],
|
||||
[0, "관구검", null, 0, null, 78, 74, 55, 0, 202, 255, null, null],
|
||||
[0, "한현", null, 0, null, 22, 33, 62, 0, 163, 220, null, null],
|
||||
[0, "한호", null, 15, "하내", 69, 72, 84, 11, 164, 224, null, null],
|
||||
[0, "관흥", null, 0, null, 76, 86, 67, 0, 199, 234, null, null],
|
||||
[0, "관색", null, 0, null, 74, 88, 56, 0, 200, 239, null, null],
|
||||
[0, "한수", null, 20, "천수", 81, 75, 82, 12, 142, 215, null, null],
|
||||
[0, "한숭", null, 0, null, 25, 15, 71, 0, 154, 210, null, null],
|
||||
[0, "관정", null, 4, "북평", 36, 52, 73, 11, 158, 199, null, null],
|
||||
[0, "한섬", null, 0, null, 69, 66, 36, 0, 159, 196, null, null],
|
||||
[0, "감택", null, 0, null, 43, 49, 83, 0, 182, 243, null, null],
|
||||
[0, "한충", null, 0, null, 64, 67, 18, 0, 151, 184, null, null],
|
||||
[0, "한당", null, 7, "완", 76, 85, 61, 1, 156, 227, null, null],
|
||||
[0, "한덕", null, 0, null, 63, 76, 25, 0, 171, 228, null, null],
|
||||
[0, "감녕", null, 0, null, 93, 94, 76, 0, 174, 219, null, null],
|
||||
[0, "한복", null, 12, "업", 28, 3, 27, 12, 149, 193, null, null],
|
||||
[0, "관평", null, 0, null, 80, 82, 72, 0, 186, 219, null, null],
|
||||
[0, "간옹", null, 0, "진류", 20, 32, 75, 0, 164, 225, null, null],
|
||||
[0, "안량", null, 17, "남피", 88, 94, 21, 1, 160, 200, null, null],
|
||||
[0, "위연", null, 0, null, 85, 92, 72, 0, 175, 234, null, null],
|
||||
[0, "국의", null, 12, "업", 82, 78, 51, 1, 146, 197, null, null],
|
||||
[0, "희지재", null, 0, null, 63, 7, 88, 0, 157, 196, null, null],
|
||||
[0, "위속", null, 0, null, 67, 78, 31, 0, 156, 200, null, null],
|
||||
[0, "위유", null, 16, "계", 46, 29, 74, 11, 151, 193, null, null],
|
||||
[0, "우금2", null, 0, null, 72, 79, 38, 0, 173, 226, null, null],
|
||||
[0, "우보", null, 1, "천수", 38, 60, 21, 1, 159, 192, null, null],
|
||||
[0, "강유", null, 0, null, 92, 89, 90, 0, 202, 264, null, null],
|
||||
[0, "공지", null, 0, null, 43, 53, 62, 0, 178, 197, null, null],
|
||||
[0, "교유", null, 0, null, 64, 69, 38, 0, 143, 195, null, null],
|
||||
[0, "공도", null, 0, null, 59, 70, 23, 0, 164, 200, null, null],
|
||||
[0, "허공", null, 0, null, 44, 66, 64, 0, 155, 200, null, null],
|
||||
[0, "허정", null, 21, "초", 23, 5, 68, 11, 152, 222, null, null],
|
||||
[0, "허저", null, 0, null, 74, 96, 36, 0, 169, 226, null, null],
|
||||
[0, "허유", null, 17, "남피", 36, 21, 80, 1, 155, 204, null, null],
|
||||
[0, "기령", null, 7, "완", 78, 83, 51, 1, 155, 199, null, null],
|
||||
[0, "김의", null, 0, null, 32, 38, 65, 0, 177, 218, null, null],
|
||||
[0, "김배삼결", null, 0, null, 64, 76, 19, 0, 192, 225, null, null],
|
||||
[0, "김선", null, 1, "장안", 52, 68, 13, 1, 155, 220, null, null],
|
||||
[0, "우번", null, 0, null, 43, 46, 86, 0, 164, 233, null, null],
|
||||
[0, "형도영", null, 0, null, 69, 80, 41, 0, 174, 208, null, null],
|
||||
[0, "엄안", null, 6, "자동", 82, 83, 72, 1, 151, 222, null, null],
|
||||
[0, "곽마", null, 0, null, 68, 71, 40, 0, 239, 281, null, null],
|
||||
[0, "엄준", null, 0, null, 28, 2, 71, 0, 169, 246, null, null],
|
||||
[0, "엄정", null, 0, null, 50, 55, 39, 0, 151, 189, null, null],
|
||||
[0, "엄백호", null, 0, null, 67, 70, 23, 0, 142, 198, null, null, "이 몸은 동오의 덕왕 엄백호라고 하오!"],
|
||||
[0, "엄여", null, 0, null, 61, 78, 44, 0, 145, 196, null, null],
|
||||
[0, "오의", null, 0, null, 79, 73, 70, 0, 165, 237, null, null],
|
||||
[0, "황개", null, 7, "완", 80, 83, 70, 1, 154, 218, null, null],
|
||||
[0, "고간", null, 0, null, 74, 56, 48, 0, 168, 206, null, null],
|
||||
[0, "황권", null, 0, null, 75, 59, 82, 0, 167, 240, null, null],
|
||||
[0, "고유", null, 0, null, 57, 43, 72, 0, 174, 263, null, null],
|
||||
[0, "고순", null, 0, null, 85, 86, 60, 0, 162, 198, null, null],
|
||||
[0, "고상", null, 0, null, 68, 68, 53, 0, 194, 252, null, null],
|
||||
[0, "고승", null, 0, null, 67, 73, 41, 0, 145, 184, null, null],
|
||||
[0, "후성", null, 0, null, 74, 75, 68, 0, 158, 199, null, null],
|
||||
[0, "후선", null, 20, "천수", 63, 66, 34, 1, 175, 228, null, null],
|
||||
[0, "황조", null, 13, "강하", 74, 66, 55, 1, 148, 208, null, null],
|
||||
[0, "공손월", null, 4, "북평", 74, 72, 47, 1, 160, 192, null, null],
|
||||
[0, "공손연", null, 0, null, 65, 70, 60, 0, 205, 238, null, null],
|
||||
[0, "공손공", null, 11, "안평", 39, 17, 69, 1, 174, 240, null, null],
|
||||
[0, "공손강", null, 11, "안평", 72, 66, 65, 1, 172, 211, null, null],
|
||||
[0, "공손찬", null, 4, "북평", 84, 83, 75, 12, 152, 199, null, null],
|
||||
[0, "공손속", null, 4, "북평", 63, 68, 57, 1, 176, 199, null, null],
|
||||
[0, "공손도", null, 11, "안평", 67, 71, 66, 12, 154, 204, null, null],
|
||||
[0, "공손범", null, 4, "북평", 73, 69, 66, 1, 158, 199, null, null],
|
||||
[0, "황충", null, 0, null, 90, 93, 65, 0, 154, 220, null, null],
|
||||
[0, "고정", null, 0, null, 55, 63, 43, 0, 190, 251, null, null],
|
||||
[0, "고패", null, 0, null, 66, 61, 69, 0, 170, 212, null, null],
|
||||
[0, "황보숭", null, 1, "홍농", 92, 61, 73, 1, 132, 195, null, null],
|
||||
[0, "공융", null, 19, "북해", 30, 5, 73, 12, 153, 208, null, null],
|
||||
[0, "고람", null, 17, "남피", 76, 82, 68, 1, 159, 201, null, null],
|
||||
[0, "오경", null, 0, null, 69, 66, 52, 0, 151, 211, null, null],
|
||||
[0, "호거아", null, 1, "사곡", 70, 82, 41, 1, 164, 206, null, null],
|
||||
[0, "호진", null, 1, "호로", 65, 74, 12, 1, 148, 190, null, null],
|
||||
[0, "올돌골", null, 0, null, 79, 86, 1, 0, 186, 225, null, null],
|
||||
[0, "호반", null, 0, null, 55, 55, 59, 0, 179, 233, null, null],
|
||||
[0, "오반", null, 0, null, 74, 71, 59, 0, 171, 234, null, null],
|
||||
[0, "고옹", null, 0, null, 43, 18, 80, 0, 168, 243, null, null],
|
||||
[0, "오란", null, 6, "성도", 68, 80, 35, 1, 170, 218, null, null],
|
||||
[0, "채화", null, 13, "장판", 39, 50, 1, 1, 166, 208, null, null],
|
||||
[0, "채중", null, 13, "장판", 40, 54, 2, 1, 168, 208, null, null],
|
||||
[0, "채모", null, 13, "양양", 70, 70, 77, 1, 160, 215, null, null],
|
||||
[0, "착융", null, 0, null, 60, 69, 38, 0, 161, 195, null, null],
|
||||
[0, "사마의", null, 0, "하내", 98, 63, 98, 0, 179, 251, null, null],
|
||||
[0, "사마염", null, 0, null, 69, 59, 77, 0, 238, 290, null, null],
|
||||
[0, "사마사", null, 0, null, 79, 64, 88, 0, 208, 255, null, null],
|
||||
[0, "사마소", null, 0, null, 76, 57, 87, 0, 211, 265, null, null],
|
||||
[0, "사마부", null, 0, "하내", 70, 37, 77, 0, 180, 272, null, null],
|
||||
[0, "사마랑", null, 1, "낙양", 20, 21, 72, 1, 171, 217, null, null],
|
||||
[0, "사마가", null, 0, null, 66, 84, 25, 0, 167, 222, null, null],
|
||||
[0, "주흔", null, 9, "수춘", 65, 62, 73, 11, 159, 196, null, null],
|
||||
[0, "주앙", null, 17, "남피", 74, 65, 65, 1, 162, 195, null, null],
|
||||
[0, "주지", null, 0, null, 65, 85, 41, 0, 233, 295, null, null],
|
||||
[0, "주창", null, 0, "여남", 68, 84, 42, 0, 164, 219, null, null],
|
||||
[0, "주태유평", null, 0, null, 84, 91, 48, 0, 171, 225, null, null],
|
||||
[0, "주방", null, 0, null, 52, 41, 80, 0, 200, 240, null, null],
|
||||
[0, "주유", null, 0, null, 95, 71, 96, 0, 175, 210, null, null],
|
||||
[0, "주환", null, 0, null, 84, 82, 77, 0, 177, 238, null, null],
|
||||
[0, "주거", null, 0, null, 77, 57, 72, 0, 190, 246, null, null],
|
||||
[0, "축융", null, 0, null, 74, 85, 29, 0, 182, 230, null, null],
|
||||
[0, "주준", null, 1, "낙양", 89, 65, 70, 1, 149, 195, null, null],
|
||||
[0, "주연", null, 0, null, 79, 69, 71, 0, 182, 249, null, null],
|
||||
[0, "주치", null, 0, null, 71, 56, 72, 0, 156, 224, null, null],
|
||||
[0, "주포", null, 0, null, 68, 73, 34, 0, 191, 225, null, null],
|
||||
[0, "주령", null, 0, null, 71, 73, 67, 0, 170, 236, null, null],
|
||||
[0, "순욱", null, 0, null, 62, 14, 95, 0, 163, 212, null, null],
|
||||
[0, "순우경", null, 17, "남피", 72, 66, 29, 1, 146, 200, null, null],
|
||||
[0, "순심", null, 0, null, 34, 25, 78, 0, 164, 208, null, null],
|
||||
[0, "순유", null, 0, "낙양", 73, 26, 94, 0, 157, 214, null, null],
|
||||
[0, "장완", null, 0, null, 78, 34, 84, 0, 188, 246, null, null],
|
||||
[0, "종회", null, 0, null, 82, 51, 91, 0, 225, 264, null, null],
|
||||
[0, "장간", null, 0, null, 19, 6, 66, 0, 175, 208, null, null],
|
||||
[0, "장의거", null, 17, "남피", 71, 58, 59, 1, 159, 205, null, null],
|
||||
[0, "장흠", null, 0, null, 78, 84, 54, 0, 159, 208, null, null],
|
||||
[0, "장제3", null, 0, null, 48, 43, 85, 0, 188, 249, null, null],
|
||||
[0, "초주", null, 0, null, 16, 3, 67, 0, 201, 270, null, null],
|
||||
[0, "초촉", null, 0, null, 65, 76, 33, 0, 159, 208, null, null],
|
||||
[0, "상총", null, 0, null, 76, 61, 77, 0, 195, 240, null, null],
|
||||
[0, "종요", null, 0, null, 70, 24, 77, 0, 151, 230, null, null],
|
||||
[0, "상랑", null, 0, null, 28, 18, 74, 0, 167, 247, null, null],
|
||||
[0, "서영", null, 1, "사수", 80, 76, 62, 1, 147, 192, null, null],
|
||||
[0, "제갈각", null, 0, null, 72, 47, 90, 0, 203, 253, null, null],
|
||||
[0, "제갈근", null, 0, null, 75, 34, 81, 0, 174, 241, null, null],
|
||||
[0, "제갈균", null, 0, null, 29, 25, 52, 0, 185, 252, null, null],
|
||||
[0, "제갈탄", null, 0, null, 72, 72, 76, 0, 206, 258, null, null],
|
||||
[0, "제갈량", null, 0, null, 98, 38, 100, 0, 184, 234, null, null],
|
||||
[0, "서황", null, 0, null, 91, 90, 78, 0, 165, 228, null, null],
|
||||
[0, "서서", null, 0, null, 87, 64, 93, 0, 160, 300, null, null],
|
||||
[0, "서성", null, 0, null, 86, 81, 78, 0, 178, 232, null, null],
|
||||
[0, "서막", null, 0, null, 55, 32, 68, 0, 171, 249, null, null],
|
||||
[0, "신의", null, 0, null, 55, 59, 60, 0, 190, 252, null, null],
|
||||
[0, "신탐", null, 0, null, 56, 62, 54, 0, 188, 254, null, null],
|
||||
[0, "심배", null, 12, "업", 80, 60, 83, 1, 156, 204, null, null],
|
||||
[0, "신비", null, 0, null, 39, 24, 79, 0, 171, 240, null, null],
|
||||
[0, "신평", null, 17, "남피", 69, 43, 77, 1, 165, 204, null, null],
|
||||
[0, "진복", null, 0, null, 31, 7, 73, 0, 160, 226, null, null],
|
||||
[0, "진랑", null, 0, null, 58, 70, 35, 0, 192, 234, null, null],
|
||||
[0, "휴고", null, 0, null, 63, 71, 38, 0, 151, 199, null, null],
|
||||
[0, "추단", null, 0, null, 61, 64, 34, 0, 144, 193, null, null],
|
||||
[0, "성의", null, 20, "천수", 74, 72, 41, 1, 168, 211, null, null],
|
||||
[0, "성공영", null, 20, "천수", 73, 71, 80, 11, 171, 220, null, null],
|
||||
[0, "설종", null, 0, null, 34, 16, 73, 0, 187, 243, null, null],
|
||||
[0, "선경", null, 4, "북평", 71, 68, 43, 1, 156, 199, null, null],
|
||||
[0, "전종", null, 0, null, 78, 72, 75, 0, 183, 249, null, null],
|
||||
[0, "조예", null, 0, null, 72, 52, 82, 0, 205, 239, null, null],
|
||||
[0, "조휴", null, 0, null, 74, 75, 62, 0, 174, 228, null, null],
|
||||
[0, "송헌", null, 0, null, 68, 77, 38, 0, 157, 200, null, null],
|
||||
[0, "조앙", null, 2, "진류", 74, 67, 69, 1, 161, 197, null, null],
|
||||
[0, "조홍 자렴", null, 2, "진류", 79, 81, 44, 1, 169, 232, null, null, "좌절감이 사나이를 키우는 것이다!"],
|
||||
[0, "조순", null, 2, "진류", 75, 71, 63, 1, 170, 210, null, null],
|
||||
[0, "조창", null, 0, null, 82, 90, 40, 0, 190, 223, null, null],
|
||||
[0, "조식", null, 0, null, 33, 22, 81, 0, 192, 232, null, null],
|
||||
[0, "조진", null, 0, null, 81, 74, 70, 0, 185, 231, null, null],
|
||||
[0, "조인", null, 2, "진류", 89, 86, 62, 1, 168, 223, null, null],
|
||||
[0, "조성", null, 0, null, 54, 74, 38, 0, 163, 198, null, null],
|
||||
[0, "조조", null, 2, "진류", 99, 72, 91, 12, 151, 220, null, null, "내가 천하 사람들을 저버릴지언정, 천하 사람들이 나를 저버리게 하지는 않을 것이다."],
|
||||
[0, "조충", null, 0, null, 31, 21, 85, 0, 196, 207, null, null],
|
||||
[0, "장패", null, 0, null, 78, 75, 53, 0, 165, 231, null, null],
|
||||
[0, "조비", null, 0, null, 70, 71, 83, 0, 187, 226, null, null],
|
||||
[0, "조표", null, 8, "서주", 66, 69, 32, 1, 151, 196, null, null],
|
||||
[0, "저수", null, 12, "업", 78, 35, 90, 11, 156, 201, null, null],
|
||||
[0, "소비", null, 0, null, 69, 63, 66, 0, 172, 221, null, null],
|
||||
[0, "조무", null, 7, "완", 70, 71, 66, 1, 155, 193, null, null],
|
||||
[0, "손환", null, 0, null, 82, 73, 76, 0, 166, 228, null, null],
|
||||
[0, "손견", null, 7, "완", 94, 90, 79, 11, 156, 192, null, null, "내가 옥새를 가지고 있다면 나는 칼과 화살을 맞아 죽을 것이다."],
|
||||
[0, "손건", null, 0, null, 34, 33, 79, 0, 165, 215, null, null],
|
||||
[0, "손권", null, 0, null, 79, 67, 80, 0, 182, 252, null, null],
|
||||
[0, "손교", null, 0, null, 77, 72, 67, 0, 181, 219, null, null],
|
||||
[0, "손책", null, 0, null, 96, 92, 74, 0, 175, 200, null, null],
|
||||
[0, "손소", null, 0, null, 80, 79, 76, 0, 188, 241, null, null],
|
||||
[0, "손정", null, 7, "완", 66, 53, 73, 1, 160, 206, null, null],
|
||||
[0, "손중", null, 0, null, 62, 71, 36, 0, 154, 184, null, null],
|
||||
[0, "손등", null, 0, null, 62, 37, 79, 0, 209, 241, null, null],
|
||||
[0, "손유", null, 0, null, 77, 70, 68, 0, 177, 215, null, null],
|
||||
[0, "손익", null, 0, null, 67, 78, 27, 0, 184, 204, null, null],
|
||||
[0, "손례", null, 0, null, 78, 81, 67, 0, 180, 250, null, null],
|
||||
[0, "태사자", null, 0, "안평", 90, 93, 69, 0, 166, 206, null, null],
|
||||
[0, "대래동주", null, 0, null, 51, 53, 54, 0, 195, 249, null, null],
|
||||
[0, "대릉", null, 0, null, 70, 75, 39, 0, 199, 258, null, null],
|
||||
[0, "타사대왕", null, 0, null, 62, 59, 70, 0, 186, 225, null, null],
|
||||
[0, "담웅", null, 0, null, 58, 73, 14, 0, 188, 221, null, null],
|
||||
[0, "장윤", null, 13, "양양", 72, 67, 42, 1, 163, 208, null, null],
|
||||
[0, "조운", null, 0, "진양", 96, 96, 77, 0, 168, 229, null, null],
|
||||
[0, "장위", null, 14, "한중", 74, 66, 45, 1, 172, 228, null, null, "쌀 다섯 말만 가지고 오시오~ 모두 고쳐드리리다."],
|
||||
[0, "장영", null, 0, null, 75, 74, 41, 0, 154, 195, null, null],
|
||||
[0, "장연", null, 0, "진양", 76, 81, 57, 0, 153, 210, null, null],
|
||||
[0, "장횡", null, 0, null, 59, 70, 23, 0, 178, 211, null, null],
|
||||
[0, "장온", null, 0, null, 32, 10, 72, 0, 193, 231, null, null],
|
||||
[0, "장개", null, 8, "하비", 34, 66, 8, 1, 155, 194, null, null],
|
||||
[0, "장각", null, 0, null, 87, 25, 86, 0, 140, 184, null, null],
|
||||
[0, "장기", null, 0, null, 74, 35, 76, 0, 170, 223, null, null],
|
||||
[0, "장의", null, 0, null, 79, 72, 75, 0, 153, 215, null, null],
|
||||
[0, "장훈", null, 7, "완", 72, 68, 41, 1, 156, 206, null, null],
|
||||
[0, "장굉", null, 0, null, 24, 22, 86, 0, 153, 212, null, null],
|
||||
[0, "장합", null, 12, "업", 89, 89, 73, 1, 167, 231, null, null],
|
||||
[0, "조홍", null, 0, null, 66, 72, 28, 0, 156, 184, null, null],
|
||||
[0, "장제1", null, 1, "장안", 70, 66, 55, 1, 144, 196, null, null],
|
||||
[0, "장수", null, 1, "함곡", 80, 73, 64, 1, 154, 207, null, null],
|
||||
[0, "장소 자포", null, 0, null, 33, 3, 84, 0, 156, 236, null, null],
|
||||
[0, "장송", null, 0, null, 25, 6, 88, 0, 170, 212, null, null],
|
||||
[0, "장임", null, 6, "성도", 84, 84, 78, 1, 169, 214, null, null],
|
||||
[0, "장제", null, 0, null, 67, 43, 80, 0, 236, 280, null, null],
|
||||
[0, "장막", null, 2, "진류", 52, 53, 71, 1, 155, 195, null, null],
|
||||
[0, "조범", null, 0, null, 28, 21, 32, 0, 168, 218, null, null],
|
||||
[0, "장비", null, 0, "진류", 94, 98, 30, 0, 165, 221, null, null],
|
||||
[0, "장포", null, 0, null, 78, 87, 48, 0, 198, 219, null, null],
|
||||
[0, "장보", null, 0, null, 83, 71, 81, 0, 148, 184, null, null],
|
||||
[0, "장만성", null, 0, null, 75, 81, 45, 0, 143, 184, null, null],
|
||||
[0, "장양", null, 5, "진양", 72, 70, 66, 12, 151, 195, null, null],
|
||||
[0, "장익", null, 0, null, 75, 74, 76, 0, 188, 264, null, null],
|
||||
[0, "장료", null, 1, "사곡", 95, 92, 78, 1, 169, 222, null, null],
|
||||
[0, "장량", null, 0, null, 78, 80, 74, 0, 153, 184, null, null],
|
||||
[0, "조루", null, 0, null, 70, 43, 68, 0, 183, 219, null, null],
|
||||
[0, "장로", null, 14, "한중", 51, 26, 74, 12, 163, 216, null, null],
|
||||
[0, "진횡", null, 0, null, 64, 63, 26, 0, 161, 195, null, null],
|
||||
[0, "진응", null, 0, null, 62, 72, 45, 0, 172, 208, null, null],
|
||||
[0, "진기", null, 0, null, 58, 65, 43, 0, 165, 198, null, null],
|
||||
[0, "진궁", null, 2, "진류", 84, 55, 89, 11, 154, 198, null, null],
|
||||
[0, "진교", null, 0, null, 61, 27, 77, 0, 175, 237, null, null],
|
||||
[0, "진군", null, 0, null, 32, 14, 75, 0, 167, 236, null, null],
|
||||
[0, "진규", null, 8, "서주", 30, 4, 82, 11, 132, 206, null, null],
|
||||
[0, "진식", null, 0, null, 70, 71, 25, 0, 191, 230, null, null],
|
||||
[0, "진진", null, 0, null, 46, 46, 69, 0, 170, 235, null, null],
|
||||
[0, "진태", null, 0, null, 84, 77, 86, 0, 210, 260, null, null],
|
||||
[0, "진등", null, 8, "하비", 79, 64, 81, 1, 169, 208, null, null],
|
||||
[0, "진도", null, 0, null, 76, 71, 67, 0, 171, 237, null, null],
|
||||
[0, "진무", null, 0, null, 76, 87, 43, 0, 176, 215, null, null],
|
||||
[0, "진란", null, 0, null, 66, 69, 40, 0, 157, 204, null, null],
|
||||
[0, "진림", null, 0, null, 25, 9, 75, 0, 160, 217, null, null],
|
||||
[0, "정욱", null, 0, null, 70, 49, 90, 0, 141, 220, null, null],
|
||||
[0, "정원지", null, 0, null, 69, 74, 17, 0, 145, 184, null, null],
|
||||
[0, "정은", null, 0, null, 69, 73, 40, 0, 184, 220, null, null],
|
||||
[0, "정원", null, 0, null, 70, 76, 35, 0, 137, 189, null, null],
|
||||
[0, "정보", null, 7, "완", 84, 79, 79, 1, 151, 216, null, null],
|
||||
[0, "정봉승연", null, 0, null, 81, 80, 75, 0, 190, 271, null, null],
|
||||
[0, "전위", null, 0, null, 72, 95, 35, 0, 160, 197, null, null],
|
||||
[0, "전해", null, 4, "북평", 68, 65, 58, 1, 154, 199, null, null],
|
||||
[0, "전주", null, 0, null, 66, 66, 73, 0, 169, 214, null, null],
|
||||
[0, "전풍", null, 12, "업", 72, 29, 93, 1, 162, 200, null, null],
|
||||
[0, "전예", null, 0, null, 80, 72, 80, 0, 171, 252, null, null],
|
||||
[0, "동윤", null, 0, null, 47, 28, 79, 0, 192, 246, null, null],
|
||||
[0, "동화", null, 0, null, 57, 34, 75, 0, 168, 219, null, null],
|
||||
[0, "등애", null, 0, null, 93, 87, 89, 0, 197, 264, null, null],
|
||||
[0, "도겸", null, 8, "서주", 51, 27, 64, 12, 132, 194, null, null],
|
||||
[0, "등현", null, 0, null, 61, 73, 45, 0, 188, 248, null, null],
|
||||
[0, "등지", null, 0, null, 73, 52, 81, 0, 178, 251, null, null],
|
||||
[0, "동습", null, 0, null, 72, 85, 50, 0, 169, 215, null, null],
|
||||
[0, "동승", null, 1, "낙양", 56, 53, 65, 1, 154, 200, null, null],
|
||||
[0, "동소", null, 0, null, 34, 25, 80, 0, 156, 236, null, null],
|
||||
[0, "동탁", null, 1, "낙양", 85, 87, 73, 12, 139, 192, null, null, "크하하, 천하는 이제 나의 것이다!"],
|
||||
[0, "동도나", null, 0, null, 68, 73, 31, 0, 189, 225, null, null],
|
||||
[0, "동민", null, 1, "장안", 49, 60, 25, 1, 149, 192, null, null],
|
||||
[0, "등무", null, 0, null, 61, 75, 29, 0, 147, 184, null, null],
|
||||
[0, "두기", null, 0, null, 66, 32, 75, 0, 163, 224, null, null],
|
||||
[0, "두예", null, 0, null, 84, 30, 85, 0, 222, 284, null, null],
|
||||
[0, "배원소", null, 0, null, 49, 65, 25, 0, 169, 200, null, null],
|
||||
[0, "마완", null, 0, null, 71, 74, 16, 0, 170, 211, null, null],
|
||||
[0, "마휴", null, 0, null, 68, 73, 48, 0, 178, 212, null, null],
|
||||
[0, "파재", null, 0, null, 74, 74, 55, 0, 145, 184, null, null],
|
||||
[0, "마준", null, 0, null, 38, 52, 31, 0, 196, 230, null, null],
|
||||
[0, "마속", null, 0, null, 61, 67, 87, 0, 190, 228, null, null],
|
||||
[0, "마대", null, 0, null, 79, 85, 62, 0, 183, 246, null, null],
|
||||
[0, "마충", null, 0, null, 78, 72, 68, 0, 187, 249, null, null],
|
||||
[0, "마초", null, 0, null, 93, 97, 44, 0, 176, 222, null, null],
|
||||
[0, "마철", null, 0, null, 70, 62, 58, 0, 179, 212, null, null],
|
||||
[0, "마등", null, 10, "서량", 82, 80, 52, 12, 149, 212, null, null],
|
||||
[0, "마량", null, 0, null, 53, 23, 88, 0, 187, 222, null, null],
|
||||
[0, "반준", null, 0, null, 61, 18, 83, 0, 174, 239, null, null],
|
||||
[0, "반장", null, 0, null, 76, 80, 74, 0, 177, 234, null, null],
|
||||
[0, "번조", null, 1, "안정", 66, 73, 31, 1, 149, 192, null, null],
|
||||
[0, "번능", null, 0, null, 69, 61, 36, 0, 158, 195, null, null],
|
||||
[0, "반봉", null, 12, "업", 56, 77, 4, 1, 155, 190, null, null],
|
||||
[0, "비의", null, 0, null, 77, 30, 83, 0, 193, 253, null, null],
|
||||
[0, "비연", null, 0, null, 60, 66, 21, 0, 196, 238, null, null],
|
||||
[0, "비시", null, 0, null, 18, 28, 65, 0, 176, 240, null, null],
|
||||
[0, "미축", null, 8, "하비", 33, 29, 78, 1, 165, 220, null, null],
|
||||
[0, "미방", null, 8, "광릉", 54, 61, 32, 1, 169, 226, null, null],
|
||||
[0, "비요", null, 0, null, 70, 66, 73, 0, 192, 228, null, null],
|
||||
[0, "무안국", null, 19, "북해", 70, 83, 34, 1, 156, 193, null, null],
|
||||
[0, "풍습", null, 0, null, 72, 71, 39, 0, 182, 222, null, null],
|
||||
[0, "부사인", null, 0, null, 46, 63, 37, 0, 182, 225, null, null],
|
||||
[0, "부첨", null, 0, null, 73, 85, 76, 0, 216, 263, null, null],
|
||||
[0, "부손", null, 0, null, 29, 13, 70, 0, 162, 230, null, null],
|
||||
[0, "문앙", null, 0, null, 76, 93, 65, 0, 238, 291, null, null],
|
||||
[0, "문흠", null, 0, null, 74, 83, 43, 0, 200, 258, null, null],
|
||||
[0, "문추", null, 17, "남피", 89, 93, 47, 1, 169, 200, null, null],
|
||||
[0, "문빙", null, 0, null, 80, 82, 69, 0, 178, 237, null, null],
|
||||
[0, "변희", null, 0, null, 56, 70, 62, 0, 169, 200, null, null],
|
||||
[0, "망아장", null, 0, null, 45, 79, 3, 0, 191, 225, null, null],
|
||||
[0, "봉기", null, 17, "남피", 43, 21, 84, 11, 153, 202, null, null],
|
||||
[0, "방희", null, 0, null, 60, 38, 69, 0, 152, 192, null, null],
|
||||
[0, "법정", null, 0, null, 82, 47, 94, 0, 176, 220, null, null],
|
||||
[0, "방통", null, 0, null, 85, 34, 97, 0, 178, 213, null, null],
|
||||
[0, "방덕", null, 10, "서량", 89, 94, 75, 11, 170, 219, null, null],
|
||||
[0, "포륭", null, 0, null, 58, 76, 49, 0, 174, 208, null, null],
|
||||
[0, "목순", null, 0, null, 52, 77, 11, 0, 157, 190, null, null],
|
||||
[0, "목록대왕", null, 0, null, 65, 71, 39, 0, 184, 225, null, null],
|
||||
[0, "보즐", null, 0, null, 72, 51, 84, 0, 177, 247, null, null],
|
||||
[0, "만총", null, 0, null, 84, 64, 82, 0, 170, 242, null, null],
|
||||
[0, "모개", null, 0, null, 63, 39, 60, 0, 161, 216, null, null],
|
||||
[0, "맹획", null, 0, null, 87, 87, 42, 0, 186, 245, null, null],
|
||||
[0, "맹달", null, 0, null, 75, 73, 75, 0, 172, 228, null, null],
|
||||
[0, "맹우", null, 0, null, 50, 68, 22, 0, 190, 243, null, null],
|
||||
[0, "유섭", null, 7, "완", 62, 79, 24, 1, 158, 190, null, null],
|
||||
[0, "양부2", null, 0, null, 62, 68, 68, 0, 167, 212, null, null],
|
||||
[0, "옹개", null, 0, null, 73, 75, 57, 0, 188, 225, null, null],
|
||||
[0, "양의", null, 0, null, 68, 40, 82, 0, 190, 235, null, null],
|
||||
[0, "양호", null, 0, null, 90, 64, 84, 0, 221, 278, null, null],
|
||||
[0, "양홍", null, 7, "완", 33, 15, 77, 1, 152, 199, null, null],
|
||||
[0, "양앙", null, 0, null, 68, 72, 38, 0, 172, 215, null, null],
|
||||
[0, "양추1", null, 0, null, 52, 65, 40, 0, 159, 199, null, null],
|
||||
[0, "양수", null, 0, null, 25, 4, 83, 0, 175, 219, null, null],
|
||||
[0, "양추2", null, 0, null, 67, 64, 60, 0, 172, 238, null, null],
|
||||
[0, "양송", null, 0, null, 1, 5, 28, 0, 167, 215, null, null],
|
||||
[0, "양임", null, 14, "한중", 70, 78, 56, 1, 170, 215, null, null],
|
||||
[0, "양조", null, 0, null, 53, 61, 36, 0, 223, 286, null, null],
|
||||
[0, "양백", null, 14, "한중", 44, 45, 19, 1, 171, 214, null, null],
|
||||
[0, "양부1", null, 0, null, 67, 50, 83, 0, 178, 239, null, null],
|
||||
[0, "양봉1", null, 0, null, 66, 65, 31, 0, 153, 197, null, null],
|
||||
[0, "양봉2", null, 0, null, 54, 62, 53, 0, 191, 252, null, null],
|
||||
[0, "뇌동", null, 0, null, 69, 78, 51, 0, 172, 218, null, null],
|
||||
[0, "뇌박", null, 0, null, 62, 70, 36, 0, 157, 206, null, null],
|
||||
[0, "이이", null, 0, null, 63, 77, 19, 0, 187, 222, null, null],
|
||||
[0, "이회", null, 0, null, 79, 65, 79, 0, 175, 231, null, null],
|
||||
[0, "이각", null, 1, "낙양", 69, 72, 24, 1, 148, 198, null, null],
|
||||
[0, "육항", null, 0, null, 91, 63, 87, 0, 226, 274, null, null],
|
||||
[0, "육적", null, 0, null, 20, 7, 72, 0, 187, 219, null, null],
|
||||
[0, "육손", null, 0, null, 94, 69, 95, 0, 183, 245, null, null],
|
||||
[0, "이엄", null, 0, null, 83, 84, 76, 0, 172, 234, null, null],
|
||||
[0, "이유", null, 1, "낙양", 61, 26, 93, 11, 150, 192, null, null, "장안으로 천도를 하는것이 어떻겠습니까?"],
|
||||
[0, "이숙", null, 1, "낙양", 46, 69, 61, 1, 156, 192, null, null],
|
||||
[0, "이통", null, 0, "여남", 73, 83, 62, 0, 168, 209, null, null],
|
||||
[0, "이전", null, 0, null, 78, 77, 79, 0, 181, 216, null, null],
|
||||
[0, "이풍", null, 0, null, 69, 74, 50, 0, 158, 199, null, null],
|
||||
[0, "유언", null, 6, "성도", 53, 38, 80, 12, 132, 194, null, null],
|
||||
[0, "유괴", null, 6, "성도", 71, 73, 67, 1, 165, 214, null, null],
|
||||
[0, "유기", null, 13, "양양", 52, 12, 63, 1, 174, 210, null, null],
|
||||
[0, "유우", null, 16, "계", 55, 33, 70, 12, 145, 193, null, null],
|
||||
[0, "유훈", null, 0, null, 47, 63, 35, 0, 163, 216, null, null],
|
||||
[0, "유현", null, 0, null, 54, 66, 51, 0, 188, 252, null, null],
|
||||
[0, "유순", null, 0, null, 63, 46, 41, 0, 172, 255, null, null],
|
||||
[0, "유장", null, 6, "성도", 31, 5, 34, 1, 162, 219, null, null],
|
||||
[0, "유심", null, 0, null, 60, 62, 70, 0, 238, 264, null, null],
|
||||
[0, "유선", null, 0, null, 3, 5, 9, 0, 208, 270, null, null],
|
||||
[0, "유종", null, 0, null, 24, 22, 65, 0, 195, 220, null, null],
|
||||
[0, "유도", null, 0, null, 25, 23, 26, 0, 168, 214, null, null],
|
||||
[0, "유파", null, 0, null, 29, 22, 75, 0, 186, 222, null, null],
|
||||
[0, "유반", null, 0, null, 69, 76, 47, 0, 168, 210, null, null],
|
||||
[0, "유비", null, 0, "진류", 81, 77, 80, 0, 161, 223, null, null],
|
||||
[0, "유표1", null, 13, "양양", 48, 31, 72, 12, 142, 208, null, null, "강하 팔준에서 제일가는 이 유표에게로 오시오!"],
|
||||
[0, "유복", null, 0, null, 64, 49, 74, 0, 164, 208, null, null],
|
||||
[0, "유벽", null, 0, null, 70, 72, 47, 0, 145, 201, null, null],
|
||||
[0, "유봉", null, 0, null, 75, 79, 44, 0, 188, 220, null, null],
|
||||
[0, "유엽", null, 0, null, 56, 32, 92, 0, 176, 235, null, null],
|
||||
[0, "유요", null, 0, null, 62, 67, 46, 0, 156, 198, null, null],
|
||||
[0, "여위황", null, 0, null, 58, 63, 29, 0, 159, 200, null, null],
|
||||
[0, "요화", null, 0, null, 73, 76, 69, 0, 168, 264, null, null],
|
||||
[0, "양흥", null, 10, "서량", 61, 65, 19, 1, 169, 211, null, null],
|
||||
[0, "양습", null, 0, null, 73, 40, 74, 0, 168, 230, null, null],
|
||||
[0, "양서", null, 0, null, 56, 62, 66, 0, 198, 260, null, null],
|
||||
[0, "능조", null, 0, null, 75, 81, 42, 0, 168, 203, null, null],
|
||||
[0, "능통", null, 0, null, 77, 89, 60, 0, 189, 217, null, null],
|
||||
[0, "여개", null, 0, null, 53, 30, 67, 0, 194, 227, null, null],
|
||||
[0, "여건", null, 0, null, 57, 70, 62, 0, 173, 238, null, null],
|
||||
[0, "여광", null, 0, null, 56, 70, 13, 0, 162, 207, null, null],
|
||||
[0, "여상", null, 0, null, 54, 71, 12, 0, 164, 207, null, null],
|
||||
[0, "여대", null, 0, null, 75, 71, 70, 0, 161, 256, null, null],
|
||||
[0, "여범", null, 0, null, 73, 63, 75, 0, 169, 228, null, null],
|
||||
[0, "여포", null, 1, "사수", 97, 100, 26, 1, 156, 198, null, null],
|
||||
[0, "여몽", null, 0, null, 82, 81, 20, 0, 178, 219, null, null],
|
||||
[0, "윤직", null, 0, null, 46, 42, 73, 0, 196, 237, null, null],
|
||||
[0, "냉포", null, 0, null, 71, 82, 61, 0, 176, 214, null, null],
|
||||
[0, "진취", null, 0, null, 56, 63, 45, 0, 174, 208, null, null],
|
||||
[0, "노숙", null, 0, null, 84, 56, 92, 0, 172, 217, null, null],
|
||||
[0, "노식", null, 0, null, 86, 63, 82, 0, 139, 192, null, null],
|
||||
[0, "범강", null, 0, null, 49, 50, 36, 0, 174, 222, null, null],
|
||||
[0, "장달", null, 0, null, 43, 56, 34, 0, 175, 222, null, null],
|
||||
[0, "규람", null, 0, null, 55, 70, 58, 0, 165, 204, null, null],
|
||||
[0, "대원", null, 0, null, 38, 32, 68, 0, 166, 204, null, null],
|
||||
[0, "왕기", null, 0, null, 77, 72, 86, 0, 190, 261, null, null],
|
||||
[0, "왕상", null, 0, null, 23, 19, 68, 0, 180, 268, null, null],
|
||||
[0, "왕돈", null, 0, null, 60, 66, 46, 0, 198, 256, null, null],
|
||||
[0, "악침", null, 0, null, 73, 75, 43, 0, 196, 257, null, null],
|
||||
[0, "하제", null, 0, null, 83, 70, 82, 0, 171, 227, null, null],
|
||||
[0, "환범", null, 0, null, 40, 11, 78, 0, 199, 249, null, null],
|
||||
[0, "견초", null, 0, null, 72, 79, 73, 0, 168, 244, null, null],
|
||||
[0, "고당륭", null, 0, null, 35, 24, 76, 0, 190, 244, null, null],
|
||||
[0, "오찬", null, 0, null, 68, 41, 78, 0, 181, 245, null, null],
|
||||
[0, "호질", null, 0, null, 73, 46, 74, 0, 192, 250, null, null],
|
||||
[0, "오질", null, 0, null, 16, 29, 68, 0, 177, 230, null, null],
|
||||
[0, "호준", null, 0, null, 79, 76, 62, 0, 200, 256, null, null],
|
||||
[0, "고담", null, 0, null, 53, 23, 77, 0, 203, 244, null, null],
|
||||
[0, "최림", null, 0, null, 32, 22, 66, 0, 187, 244, null, null],
|
||||
[0, "사마망", null, 0, null, 78, 58, 83, 0, 205, 271, null, null],
|
||||
[0, "주이", null, 0, null, 70, 73, 61, 0, 201, 257, null, null],
|
||||
[0, "전단", null, 0, null, 67, 64, 39, 0, 204, 261, null, null],
|
||||
[0, "조우", null, 0, null, 50, 43, 37, 0, 199, 278, null, null],
|
||||
[0, "조상", null, 0, null, 39, 33, 30, 0, 207, 249, null, null],
|
||||
[0, "조방", null, 0, null, 20, 14, 27, 0, 232, 274, null, null],
|
||||
[0, "태사향", null, 0, null, 57, 66, 48, 0, 189, 246, null, null],
|
||||
[0, "장휴", null, 0, null, 63, 27, 74, 0, 204, 244, null, null],
|
||||
[0, "장호", null, 0, null, 75, 74, 51, 0, 195, 240, null, null],
|
||||
[0, "조광", null, 0, null, 67, 71, 53, 0, 210, 263, null, null],
|
||||
[0, "장집", null, 0, null, 76, 29, 80, 0, 196, 254, null, null],
|
||||
[0, "장승", null, 0, null, 77, 70, 75, 0, 178, 244, null, null],
|
||||
[0, "장소", null, 0, null, 23, 56, 31, 0, 202, 264, null, null],
|
||||
[0, "조통", null, 0, null, 68, 67, 61, 0, 209, 260, null, null],
|
||||
[0, "진표", null, 0, null, 66, 42, 74, 0, 207, 237, null, null],
|
||||
[0, "정무", null, 0, null, 56, 35, 75, 0, 201, 265, null, null],
|
||||
[0, "정봉", null, 0, null, 66, 66, 53, 0, 198, 266, null, null],
|
||||
[0, "전만", null, 0, null, 51, 73, 39, 0, 181, 235, null, null],
|
||||
[0, "마균", null, 0, null, 22, 17, 71, 0, 200, 259, null, null],
|
||||
[0, "방회", null, 0, null, 69, 75, 49, 0, 205, 272, null, null],
|
||||
[0, "서구", null, 0, null, 69, 42, 70, 0, 149, 208, null, null],
|
||||
[0, "육개", null, 0, null, 65, 61, 79, 0, 198, 269, null, null],
|
||||
[0, "유찬", null, 0, null, 78, 81, 64, 0, 172, 255, null, null],
|
||||
[0, "유소", null, 0, null, 68, 53, 74, 0, 195, 264, null, null],
|
||||
[0, "요립", null, 0, null, 32, 15, 76, 0, 181, 250, null, null],
|
||||
[0, "여거", null, 0, null, 78, 69, 71, 0, 196, 256, null, null],
|
||||
[0, "제갈첨", null, 0, null, 70, 57, 76, 0, 227, 263, null, null],
|
||||
[0, "손휴", null, 0, null, 57, 42, 78, 0, 235, 264, null, null],
|
||||
[0, "극정", null, 0, null, 42, 23, 68, 0, 208, 278, null, null],
|
||||
[0, "호분", null, 0, null, 71, 76, 50, 0, 222, 288, null, null],
|
||||
[0, "손량", null, 0, null, 32, 26, 70, 0, 243, 260, null, null],
|
||||
[0, "마막", null, 0, null, 34, 12, 44, 0, 221, 266, null, null],
|
||||
[0, "손호", null, 0, null, 41, 73, 71, 0, 242, 284, null, null],
|
||||
[0, "조모", null, 0, null, 54, 61, 59, 0, 241, 260, null, null],
|
||||
[0, "조환", null, 0, null, 30, 28, 30, 0, 246, 302, null, null],
|
||||
[0, "사마유", null, 0, null, 71, 68, 75, 0, 248, 283, null, null],
|
||||
[0, "제갈상", null, 0, null, 64, 79, 62, 0, 246, 263, null, null],
|
||||
[0, "손화", null, 0, null, 31, 39, 68, 0, 224, 253, null, null],
|
||||
[0, "염우", null, 0, null, 52, 61, 51, 0, 209, 264, null, null],
|
||||
[0, "왕경", null, 0, null, 67, 63, 71, 0, 206, 260, null, null],
|
||||
[0, "왕혼", null, 0, null, 74, 71, 63, 0, 223, 297, null, null],
|
||||
[0, "곽혁", null, 0, null, 33, 29, 70, 0, 187, 225, null, null],
|
||||
[0, "가범", null, 0, null, 60, 62, 73, 0, 202, 237, null, null],
|
||||
[0, "서질", null, 0, null, 68, 83, 24, 0, 207, 253, null, null],
|
||||
[0, "손준", null, 0, null, 64, 70, 65, 0, 219, 256, null, null],
|
||||
[0, "손침", null, 0, null, 58, 55, 65, 0, 231, 258, null, null],
|
||||
[0, "우사", null, 0, null, 61, 34, 74, 0, 217, 273, null, null],
|
||||
[0, "진건", null, 0, null, 72, 71, 65, 0, 214, 292, null, null],
|
||||
[0, "동궐", null, 0, null, 65, 59, 76, 0, 204, 271, null, null],
|
||||
[0, "당자", null, 0, null, 61, 71, 50, 0, 196, 265, null, null],
|
||||
[0, "등충", null, 0, null, 76, 86, 69, 0, 230, 264, null, null],
|
||||
[0, "황숭", null, 0, null, 67, 63, 72, 0, 208, 263, null, null],
|
||||
[0, "화핵", null, 0, null, 20, 24, 71, 0, 217, 278, null, null],
|
||||
[0, "허의", null, 0, null, 51, 74, 15, 0, 213, 263, null, null],
|
||||
[0, "송겸", null, 0, null, 66, 78, 42, 0, 175, 227, null, null],
|
||||
[0, "호제", null, 0, null, 57, 39, 68, 0, 207, 264, null, null],
|
||||
[0, "나헌", null, 0, null, 85, 70, 77, 0, 218, 270, null, null],
|
||||
[0, "아들노숙", null, 0, null, 73, 53, 74, 0, 217, 272, null, null],
|
||||
[0, "구성", null, 0, null, 63, 70, 38, 0, 157, 187, null, null],
|
||||
[0, "어부라", null, 0, null, 72, 68, 21, 0, 150, 195, null, null],
|
||||
[0, "아하소과", null, 0, null, 61, 76, 13, 0, 204, 253, null, null],
|
||||
[0, "가비능", null, 0, null, 77, 69, 52, 0, 172, 235, null, null],
|
||||
[0, "구력거", null, 0, null, 80, 68, 56, 0, 152, 193, null, null],
|
||||
[0, "강단", null, 0, null, 65, 83, 48, 0, 168, 234, null, null],
|
||||
[0, "황란", null, 0, null, 63, 75, 44, 0, 200, 264, null, null],
|
||||
[0, "호주천", null, 0, null, 70, 71, 40, 0, 169, 230, null, null],
|
||||
[0, "종리목", null, 0, null, 80, 72, 73, 0, 214, 269, null, null],
|
||||
[0, "제갈교", null, 0, null, 62, 27, 75, 0, 204, 228, null, null],
|
||||
[0, "답돈", null, 0, null, 84, 82, 61, 0, 158, 207, null, null],
|
||||
[0, "반임", null, 0, null, 66, 79, 38, 0, 168, 225, null, null],
|
||||
[0, "보도근", null, 0, null, 64, 73, 50, 0, 170, 233, null, null],
|
||||
[0, "미당대왕", null, 0, null, 59, 69, 21, 0, 202, 260, null, null],
|
||||
[0, "유표(흉노)", null, 0, null, 73, 74, 48, 0, 173, 252, null, null],
|
||||
[0, "루반", null, 0, null, 65, 76, 39, 0, 178, 207, null, null],
|
||||
[0, "손관", null, 0, null, 72, 78, 51, 0, 166, 217, null, null],
|
||||
[0, "곽석", null, 0, null, 59, 71, 48, 0, 158, 187, null, null],
|
||||
[0, "주조", null, 0, null, 63, 68, 26, 0, 152, 187, null, null],
|
||||
[0, "장거", null, 0, null, 79, 75, 56, 0, 150, 188, null, null],
|
||||
[0, "장순", null, 0, null, 73, 81, 60, 0, 152, 188, null, null],
|
||||
[0, "해니", null, 0, null, 62, 78, 35, 0, 191, 233, null, null],
|
||||
[0, "토안", null, 0, null, 53, 82, 30, 0, 191, 233, null, null],
|
||||
[0, "유주", null, 0, null, 67, 78, 42, 0, 172, 233, null, null],
|
||||
[0, "비잔", null, 0, null, 60, 71, 51, 0, 181, 216, null, null],
|
||||
[0, "월길", null, 0, null, 81, 88, 26, 0, 190, 227, null, null],
|
||||
[0, "아단", null, 0, null, 72, 65, 78, 0, 186, 259, null, null],
|
||||
[0, "철리길", null, 0, null, 77, 70, 52, 0, 179, 259, null, null],
|
||||
[0, "주삭", null, 0, null, 54, 60, 50, 0, 174, 253, null, null],
|
||||
[0, "왕이", null, 0, null, 87, 59, 77, 0, 171, 230, null, null],
|
||||
[0, "왕원희", null, 0, null, 53, 37, 73, 0, 217, 268, null, null],
|
||||
[0, "곽씨", null, 0, null, 42, 4, 55, 0, 184, 235, null, null],
|
||||
[0, "하후씨", null, 0, null, 34, 13, 42, 0, 188, 243, null, null],
|
||||
[0, "하후령녀", null, 0, null, 22, 33, 60, 0, 207, 257, null, null],
|
||||
[0, "하씨", null, 0, null, 51, 14, 67, 0, 150, 189, null, null],
|
||||
[0, "관은병", null, 0, null, 87, 84, 75, 0, 204, 271, null, null],
|
||||
[0, "감씨", null, 0, null, 14, 10, 44, 0, 175, 210, null, null],
|
||||
[0, "황월영", null, 0, null, 62, 33, 90, 0, 186, 235, null, null],
|
||||
[0, "채염", null, 0, null, 53, 14, 80, 0, 168, 224, null, null],
|
||||
[0, "채씨", null, 0, null, 38, 7, 70, 0, 159, 212, null, null],
|
||||
[0, "소교", null, 0, null, 40, 3, 70, 0, 178, 212, null, null],
|
||||
[0, "서씨", null, 0, null, 48, 16, 80, 0, 183, 232, null, null],
|
||||
[0, "신헌영", null, 0, null, 48, 28, 84, 0, 191, 269, null, null],
|
||||
[0, "견씨", null, 0, null, 42, 18, 74, 0, 182, 221, null, null],
|
||||
[0, "추씨", null, 1, "낙양", 4, 9, 66, 1, 165, 200, null, null],
|
||||
[0, "조절", null, 0, null, 32, 33, 79, 0, 190, 260, null, null],
|
||||
[0, "손씨", null, 0, null, 32, 14, 55, 0, 188, 250, null, null],
|
||||
[0, "손상향", null, 0, null, 77, 82, 55, 0, 193, 222, null, null],
|
||||
[0, "손노육", null, 0, null, 52, 10, 66, 0, 204, 253, null, null],
|
||||
[0, "손노반", null, 0, null, 17, 15, 80, 0, 202, 258, null, null],
|
||||
[0, "대교", null, 0, null, 33, 30, 66, 0, 177, 215, null, null],
|
||||
[0, "장씨", null, 0, null, 55, 35, 66, 0, 207, 237, null, null],
|
||||
[0, "장춘화", null, 0, null, 66, 29, 77, 0, 179, 234, null, null],
|
||||
[0, "초선", null, 0, null, 42, 18, 81, 0, 176, 230, null, null],
|
||||
[0, "번씨", null, 0, null, 6, 12, 50, 0, 183, 225, null, null],
|
||||
[0, "미씨", null, 0, null, 13, 6, 72, 0, 176, 208, null, null],
|
||||
[0, "변씨", null, 0, null, 30, 23, 55, 0, 160, 230, null, null],
|
||||
[0, "보련사", null, 0, null, 45, 30, 77, 0, 198, 248, null, null],
|
||||
[0, "유씨", null, 0, null, 8, 8, 44, 0, 164, 215, null, null],
|
||||
[0, "화만", null, 0, null, 85, 92, 40, 0, 191, 233, null, null],
|
||||
[0, "포삼낭", null, 0, null, 87, 83, 36, 0, 191, 240, null, null],
|
||||
[0, "성렴", null, 1, "사수", 66, 72, 43, 1, 168, 199, null, null],
|
||||
[0, "위월", null, 0, null, 68, 76, 39, 0, 170, 199, null, null],
|
||||
[0, "진의록", null, 0, null, 46, 43, 17, 0, 168, 199, null, null],
|
||||
[0, "채옹", null, 1, "장안", 27, 16, 81, 1, 133, 192, null, null],
|
||||
[0, "지양군", null, 1, "낙양", 13, 8, 55, 1, 103, 192, null, null],
|
||||
[0, "위소", null, 0, null, 21, 23, 65, 0, 204, 273, null, null],
|
||||
[0, "위강", null, 0, null, 58, 35, 56, 0, 168, 213, null, null],
|
||||
[0, "원요", null, 0, null, 28, 41, 50, 0, 177, 206, null, null],
|
||||
[0, "왕기2", null, 0, null, 69, 67, 61, 0, 217, 281, null, null],
|
||||
[0, "원환", null, 0, null, 30, 17, 72, 0, 165, 225, null, null],
|
||||
[0, "우전", null, 0, null, 64, 76, 40, 0, 204, 258, null, null],
|
||||
[0, "왕광", null, 15, "하내", 33, 65, 55, 12, 150, 190, null, null],
|
||||
[0, "왕업", null, 0, null, 33, 6, 66, 0, 220, 280, null, null],
|
||||
[0, "왕융", null, 0, null, 40, 57, 68, 0, 234, 305, null, null],
|
||||
[0, "왕충", null, 0, null, 46, 39, 57, 0, 152, 214, null, null],
|
||||
[0, "위풍", null, 0, null, 52, 23, 72, 0, 182, 219, null, null],
|
||||
[0, "구건", null, 0, null, 39, 52, 65, 0, 239, 272, null, null],
|
||||
[0, "구본", null, 0, null, 50, 41, 67, 0, 232, 269, null, null],
|
||||
[0, "동백", null, 1, "장안", 12, 7, 52, 1, 173, 192, null, null],
|
||||
[0, "마운", null, 0, null, 78, 88, 53, 0, 183, 225, null, null],
|
||||
[0, "사섭", null, 18, "교지", 53, 31, 76, 12, 137, 226, null, null],
|
||||
[0, "사지", null, 18, "교지", 58, 51, 52, 1, 163, 227, null, null],
|
||||
[0, "사휘", null, 18, "남해", 69, 64, 44, 1, 165, 227, null, null],
|
||||
[0, "사일", null, 18, "남해", 48, 36, 67, 1, 153, 230, null, null],
|
||||
[0, "사광", null, 18, "교지", 58, 45, 64, 1, 175, 235, null, null],
|
||||
[0, "후음", null, 0, null, 76, 72, 45, 0, 184, 219, null, null],
|
||||
[0, "공주", null, 21, "초", 26, 16, 68, 12, 151, 191, null, null],
|
||||
[0, "고담륭", null, 0, null, 35, 24, 76, 0, 190, 251, null, null],
|
||||
[0, "국연", null, 0, null, 50, 19, 77, 0, 160, 219, null, null],
|
||||
[0, "오언", null, 0, null, 74, 72, 77, 0, 235, 297, null, null],
|
||||
[0, "오강", null, 0, null, 48, 38, 65, 0, 216, 275, null, null],
|
||||
[0, "최염", null, 0, null, 18, 55, 77, 0, 162, 216, null, null],
|
||||
[0, "좌혁", null, 0, null, 62, 66, 51, 0, 232, 280, null, null],
|
||||
[0, "차주", null, 0, null, 49, 49, 40, 0, 164, 199, null, null],
|
||||
[0, "주태", null, 0, null, 71, 64, 71, 0, 207, 261, null, null],
|
||||
[0, "북궁백옥", null, 0, null, 66, 72, 49, 0, 146, 187, null, null],
|
||||
[0, "변장", null, 0, null, 69, 56, 47, 0, 133, 187, null, null],
|
||||
[0, "순의", null, 0, null, 16, 13, 72, 0, 207, 274, null, null],
|
||||
[0, "서진순욱", null, 0, null, 8, 19, 77, 0, 225, 289, null, null],
|
||||
[0, "사마준", null, 0, null, 85, 82, 80, 0, 232, 286, null, null],
|
||||
[0, "종육", null, 0, null, 32, 12, 71, 0, 223, 263, null, null],
|
||||
[0, "초이", null, 0, null, 62, 69, 63, 0, 219, 266, null, null],
|
||||
[0, "제갈정", null, 0, null, 58, 63, 66, 0, 246, 300, null, null],
|
||||
[0, "보협", null, 0, null, 62, 49, 53, 0, 216, 264, null, null],
|
||||
[0, "복양흥", null, 0, null, 32, 70, 65, 0, 224, 264, null, null],
|
||||
[0, "보천", null, 0, null, 71, 60, 71, 0, 222, 272, null, null],
|
||||
[0, "유대연주", null, 3, "복양", 53, 32, 55, 12, 148, 192, null, null],
|
||||
[0, "유대", null, 0, null, 63, 58, 30, 0, 155, 202, null, null],
|
||||
[0, "유승", null, 0, null, 45, 65, 33, 0, 215, 258, null, null],
|
||||
[0, "여영기", null, 0, null, 79, 88, 37, 0, 182, 198, null, null],
|
||||
[0, "독발수기능", null, 0, null, 89, 93, 77, 0, 232, 279, null, null],
|
||||
[0, "진수", null, 0, null, 12, 22, 68, 0, 233, 297, null, null],
|
||||
[0, "마륭", null, 0, null, 84, 75, 76, 0, 231, 296, null, null],
|
||||
[0, "도황", null, 0, null, 76, 68, 72, 0, 232, 290, null, null],
|
||||
[0, "진온", null, 9, "수춘", 60, 38, 48, 12, 167, 192, null, null],
|
||||
[0, "교모", null, 3, "복양", 54, 45, 69, 11, 137, 190, null, null],
|
||||
[0, "교국로", null, 0, null, 38, 17, 55, 0, 150, 220, null, null],
|
||||
[0, "포신", null, 2, "진류", 77, 67, 81, 1, 152, 192, null, null],
|
||||
[0, "포훈", null, 0, null, 41, 35, 75, 0, 180, 226, null, null],
|
||||
[0, "포도", null, 2, "진류", 57, 70, 43, 1, 159, 190, null, null],
|
||||
[0, "위자", null, 2, "진류", 58, 38, 62, 1, 150, 190, null, null],
|
||||
[0, "유화", null, 16, "계", 21, 16, 59, 1, 165, 216, null, null],
|
||||
[0, "이이자", null, 0, null, 32, 18, 48, 0, 157, 199, null, null],
|
||||
[0, "선우보", null, 16, "계", 74, 69, 65, 1, 161, 229, null, null],
|
||||
[0, "원유", null, 3, "복양", 57, 39, 73, 1, 150, 193, null, null],
|
||||
[0, "사마휘", null, 0, null, 21, 8, 92, 0, 173, 208, "은둔", null, "좋지~좋아"],
|
||||
[0, "화타", null, 0, null, 8, 7, 68, 0, 173, 207, "은둔", "의술"],
|
||||
[0, "관로", null, 0, null, 13, 9, 72, 0, 200, 233, "은둔", null],
|
||||
[0, "예형", null, 0, null, 13, 4, 90, 0, 173, 199, null, null],
|
||||
[0, "원혁", null, 0, null, 18, 18, 18, 0, 180, 246, null, null],
|
||||
[0, "헌제", null, 1, "낙양", 23, 17, 66, 1, 184, 234, null, null, "한 황실은 이대로 끝나는 것인가…"],
|
||||
[0, "황완", null, 1, "낙양", 40, 34, 71, 1, 141, 192, null, null],
|
||||
[0, "마일제", null, 1, "낙양", 43, 25, 70, 1, 130, 194, null, null],
|
||||
[0, "유총", null, 22, "여남", 82, 45, 60, 12, 151, 197, null, null],
|
||||
[0, "낙준", null, 0, null, 51, 37, 66, 0, 152, 197, null, null],
|
||||
[0, "낙통", null, 0, null, 76, 57, 73, 0, 193, 228, null, null],
|
||||
[0, "이리", null, 1, "홍농", 65, 72, 38, 1, 171, 198, null, null],
|
||||
[0, "허소", null, 0, null, 46, 13, 78, 0, 150, 195, null, null, "자네는 치세의 능신, 난세의 간옹이 되겠군."],
|
||||
[0, "허사", null, 0, null, 24, 20, 60, 0, 163, 199, null, null],
|
||||
[0, "장초", null, 0, null, 61, 32, 60, 0, 168, 196, null, null],
|
||||
[0, "양표", null, 1, "낙양", 24, 20, 60, 1, 142, 225, null, null],
|
||||
[0, "왕필", null, 0, null, 53, 44, 59, 0, 170, 228, null, null],
|
||||
[0, "사환", null, 2, "진류", 70, 71, 45, 1, 173, 209, null, null],
|
||||
[0, "정비", null, 0, null, 34, 30, 57, 0, 158, 214, null, null],
|
||||
[0, "두씨", null, 1, "낙양", 26, 41, 52, 1, 172, 236, null, null],
|
||||
[0, "누규", null, 13, "양양", 58, 40, 87, 1, 143, 212, null, null],
|
||||
[0, "단외", null, 1, "장안", 55, 48, 55, 1, 148, 209, null, null],
|
||||
[0, "원사", null, 0, null, 65, 43, 78, 0, 159, 210, null, null],
|
||||
[0, "사의관", null, 0, null, 22, 12, 67, 0, 168, 199, null, null],
|
||||
[0, "송건", null, 0, null, 66, 77, 40, 0, 164, 214, null, null],
|
||||
[0, "곽원", null, 17, "업", 66, 73, 36, 1, 162, 205, null, null],
|
||||
[0, "소유", null, 0, null, 51, 59, 48, 0, 167, 210, null, null],
|
||||
[0, "주한", null, 0, null, 49, 51, 26, 0, 162, 204, null, null],
|
||||
[0, "뇌서", null, 0, null, 68, 65, 34, 0, 170, 212, null, null],
|
||||
[0, "장홍", null, 0, "복양", 72, 81, 65, 0, 164, 196, null, null],
|
||||
[0, "유위대", null, 4, "북평", 53, 44, 62, 1, 152, 199, null, null],
|
||||
[0, "문칙", null, 4, "계", 59, 53, 32, 1, 164, 199, null, null],
|
||||
[0, "습진", null, 0, null, 64, 68, 51, 0, 189, 221, null, null],
|
||||
[0, "습정", null, 0, null, 49, 29, 70, 0, 181, 229, null, null]
|
||||
],
|
||||
"general_ex":[],
|
||||
"cities":[
|
||||
|
||||
],
|
||||
"history":[
|
||||
"<C>●</>190년 1월:<L><b>【역사모드2-2】</b></>반동탁연합 결성(정사)",
|
||||
"<C>●</>190년 1월:<G><b>【연합】</b></>동탁을 토벌하기 위해 18로 제후가 뭉쳤다."
|
||||
],
|
||||
"initialEvents":[
|
||||
[
|
||||
true,
|
||||
["ChangeCity", "free", {
|
||||
"pop":"70%",
|
||||
"agri":"70%",
|
||||
"comm":"70%",
|
||||
"secu":"70%",
|
||||
"trust":80
|
||||
}],
|
||||
["ChangeCity", "occupied", {
|
||||
"pop":"70%",
|
||||
"agri":"70%",
|
||||
"comm":"70%",
|
||||
"secu":"70%",
|
||||
"trust":80,
|
||||
"def":"70%",
|
||||
"wall":"70%"
|
||||
}]
|
||||
]
|
||||
],
|
||||
"events":[
|
||||
[
|
||||
"destroy_nation", 1000,
|
||||
["and",
|
||||
["Date", ">=", 183, 1],
|
||||
["RemainNation", "==", 1]
|
||||
],
|
||||
["BlockScoutAction"],
|
||||
["DeleteEvent"]
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -31,7 +31,7 @@
|
||||
["여포", "#A9A9A9", 100000, 100000, "조조의 본거지를 빼앗은 당대 최고의 맹장 여포", 1000, "병가", 2, [
|
||||
"복양"
|
||||
]],
|
||||
["손견", "#FF0000", 100000, 100000, "손견의 뒤를 이은 소패왕 손책", 1000, "병가", 2, [
|
||||
["손책", "#FF0000", 100000, 100000, "손견의 뒤를 이은 소패왕 손책", 1000, "병가", 2, [
|
||||
"여강"
|
||||
]],
|
||||
["공손찬", "#FF00FF", 100000, 100000, "북평 태수 공손찬", 1000, "병가", 3, [
|
||||
|
||||
@@ -0,0 +1,766 @@
|
||||
{
|
||||
"title":"【역사모드3-2】 군웅축록",
|
||||
"startYear":192,
|
||||
"life":1,
|
||||
"fiction":0,
|
||||
"const":{
|
||||
"defaultMaxGeneral":600
|
||||
},
|
||||
"nation":[
|
||||
["이각", "#A9A9A9", 100000, 100000, "천자를 등에 업은 이각", 1000, "명가", 5, ["함곡", "장안", "홍농", "사곡", "안정"]],
|
||||
["조조", "#0000FF", 10000, 40000, "위태로운 조조", 1000, "병가", 3, ["진류", "정도", ""]],
|
||||
["여포", "#A9A9A9", 10000, 10000, "조조를 공격하는 여포", 1000, "병가", 1, ["복양"]],
|
||||
["공손찬", "#FF00FF", 10000, 10000, "백마의 공손찬", 1000, "병가", 3, ["북평", "역경", "계"]],
|
||||
["장양", "#7B68EE", 10000, 10000, "병주자사 장양", 1000, "병가", 3, ["하내"]],
|
||||
["유장", "#483D8B", 10000, 30000, "익주목 유장", 1000, "유가", 4, ["성도", "자동", "면죽", "강주"]],
|
||||
["원술", "#FFC0CB", 15000, 40000, "후장군 원술", 1000, "병가", 2, ["수춘"]],
|
||||
["유비", "#008000", 10000, 10000, "서주목 유비", 1000, "유가", 4, ["서주", "하비", "소패"]],
|
||||
["유요", "#808000", 10000, 10000, "양주자사 유요", 1000, "유가", 3, ["합비", "건업"]],
|
||||
["마등", "#808000", 10000, 10000, "서량태수 마등", 1000, "병가", 3, ["서량"]],
|
||||
["공손도", "#A0522D", 10000, 10000, "요동의 왕 공손도", 1000, "덕가", 1, ["안평"]],
|
||||
["엄백호", "#800080", 10000, 10000, "동오의 호족, 엄백호", 1000, "병가", 1, ["오"]],
|
||||
["유표", "#E0FFFF", 10000, 10000, "형주자사 유표", 1000, "유가", 4, ["양양", "장판", "강하"]],
|
||||
["장로", "#20B2AA", 10000, 10000, "오두미도 장로", 1000, "오두미도", 1, ["한중", "양평"]],
|
||||
["장연", "#87CEEB", 10000, 10000, "흑산적 장연", 1000, "도적", 2, ["진양"]],
|
||||
["화흠", "#6495ED", 10000, 10000, "예장태수 화흠", 1000, "유가", 1, ["시상"]],
|
||||
["원소", "#FFFF00", 10000, 10000, "기주목 원소", 1000, "병가", 4, ["남피", "업", "계교", "평원"]],
|
||||
["사섭", "#800000", 10000, 10000, "교주자사 사섭", 1000, "유가", 3, ["교지", "남해"]],
|
||||
["공융", "#800080", 10000, 10000, "북해태수 공융", 1000, "유가", 1, ["북해"]],
|
||||
["한수", "#FFFFFF", 10000, 10000, "서량의 모반자 한수", 1000, "병가", 1, ["천수"]],
|
||||
["왕랑", "#87CEEB", 10000, 10000, "회계태수 왕랑", 1000, "유가", 1, ["회계"]],
|
||||
["유총", "#7B68EE", 10000, 10000, "진민왕 유총", 1000, "덕가", 6, ["초"]],
|
||||
["손책", "#FF0000", 10000, 10000, "원술에게서 벗어나려는 손책", 1000, "병가", 2, ["여강"]],
|
||||
["유벽", "#FFD700", 10000, 10000, "여남 황건적", 1000, "도적", 1, ["여남"]]
|
||||
],
|
||||
"diplomacy":[
|
||||
[4, 17, 1, 36],
|
||||
[23, 9, 1, 36],
|
||||
[4, 8, 7, 48],
|
||||
[17, 2, 7, 48],
|
||||
[7, 23, 7, 48],
|
||||
[3, 2, 1, 36]
|
||||
],
|
||||
"general":[
|
||||
[0, "아회남", null, 0, null, 65, 74, 26, 0, 190, 225, null, null],
|
||||
[0, "이적", null, 0, null, 29, 24, 84, 0, 162, 273, null, null],
|
||||
[0, "윤상", null, 0, null, 51, 44, 62, 0, 194, 260, null, null],
|
||||
[0, "윤묵", null, 0, null, 26, 15, 66, 0, 183, 239, null, null],
|
||||
[0, "우금1", null, 2, "진류", 83, 78, 74, 1, 159, 221, null, null],
|
||||
[0, "위관", null, 0, null, 69, 46, 79, 0, 220, 291, null, null],
|
||||
[0, "원윤", null, 7, "수춘", 15, 14, 39, 1, 150, 199, null, null],
|
||||
[0, "원희", null, 17, "업", 66, 51, 63, 1, 176, 207, null, null],
|
||||
[0, "염행", null, 20, "천수", 76, 84, 66, 1, 159, 215, null, null],
|
||||
[0, "염유", null, 0, "계", 71, 57, 71, 0, 168, 227, null, null],
|
||||
[0, "원술", null, 7, "수춘", 44, 65, 65, 12, 155, 199, null, null, "꿀물~! 꿀을 더 가져 오거라"],
|
||||
[0, "원상", null, 0, null, 63, 75, 42, 0, 179, 207, null, null],
|
||||
[0, "원소", null, 17, "업", 81, 69, 70, 12, 154, 202, null, null, "사세 삼공의 이 원소가 모조리 통일하겠소"],
|
||||
[0, "염상", null, 7, "수춘", 30, 27, 71, 11, 158, 199, null, null],
|
||||
[0, "원담", null, 17, "평원", 60, 69, 30, 1, 173, 205, null, null],
|
||||
[0, "염포", null, 14, "한중", 29, 25, 82, 11, 167, 231, null, null],
|
||||
[0, "왕위", null, 0, null, 60, 70, 62, 0, 163, 222, null, null],
|
||||
[0, "왕윤", null, 0, null, 26, 6, 68, 0, 137, 192, null, null],
|
||||
[0, "왕항", null, 0, null, 76, 60, 61, 0, 184, 254, null, null],
|
||||
[0, "왕찬", null, 13, "양양", 15, 3, 79, 1, 177, 217, null, null],
|
||||
[0, "왕수", null, 19, "북해", 54, 27, 77, 11, 168, 218, null, null],
|
||||
[0, "왕숙", null, 0, null, 36, 22, 76, 0, 195, 256, null, null],
|
||||
[0, "왕준", null, 0, null, 81, 73, 77, 0, 206, 286, null, null],
|
||||
[0, "왕쌍", null, 0, null, 67, 88, 19, 0, 195, 228, null, null],
|
||||
[0, "왕창", null, 0, null, 74, 64, 79, 0, 188, 259, null, null],
|
||||
[0, "왕평", null, 0, null, 83, 79, 76, 0, 192, 248, null, null],
|
||||
[0, "왕보", null, 0, null, 62, 41, 79, 0, 171, 219, null, null],
|
||||
[0, "왕문", null, 17, "남피", 65, 64, 31, 1, 162, 205, null, null],
|
||||
[0, "왕릉", null, 0, null, 74, 64, 71, 0, 172, 251, null, null],
|
||||
[0, "왕루", null, 0, null, 28, 30, 79, 0, 173, 211, null, null],
|
||||
[0, "왕랑", null, 21, "회계", 47, 35, 79, 12, 167, 228, null, null],
|
||||
[0, "온회", null, 0, null, 62, 36, 74, 0, 178, 228, null, null],
|
||||
[0, "하안", null, 0, null, 15, 30, 72, 0, 190, 249, null, null],
|
||||
[0, "괴월", null, 13, "양양", 47, 27, 82, 1, 157, 214, null, null],
|
||||
[0, "괴량", null, 13, "양양", 68, 33, 88, 11, 155, 204, null, null],
|
||||
[0, "가화", null, 0, null, 49, 65, 41, 0, 176, 222, null, null],
|
||||
[0, "가규", null, 0, null, 78, 61, 84, 0, 177, 231, null, null],
|
||||
[0, "하의", null, 7, "수춘", 56, 69, 36, 1, 161, 196, null, null],
|
||||
[0, "화흠", null, 16, "시상", 33, 33, 82, 12, 157, 231, null, null],
|
||||
[0, "가후", null, 1, "장안", 88, 48, 97, 11, 147, 223, null, null],
|
||||
[0, "곽가", null, 2, "진류", 88, 15, 96, 11, 170, 207, null, null],
|
||||
[0, "악환", null, 0, null, 65, 82, 42, 0, 196, 251, null, null],
|
||||
[0, "곽사", null, 1, "안정", 64, 76, 13, 1, 146, 197, null, null],
|
||||
[0, "악취", null, 0, null, 53, 66, 61, 0, 157, 195, null, null],
|
||||
[0, "곽준", null, 0, null, 73, 66, 73, 0, 178, 217, null, null],
|
||||
[0, "학소", null, 0, null, 86, 79, 78, 0, 185, 229, null, null],
|
||||
[0, "악진", null, 2, "진류", 80, 84, 56, 1, 159, 218, null, null],
|
||||
[0, "곽도", null, 17, "업", 52, 50, 82, 1, 157, 205, null, null],
|
||||
[0, "학맹", null, 0, null, 62, 67, 42, 0, 156, 300, null, null],
|
||||
[0, "곽유지", null, 0, null, 28, 9, 69, 0, 190, 259, null, null],
|
||||
[0, "곽익", null, 0, null, 73, 69, 69, 0, 207, 270, null, null],
|
||||
[0, "곽회", null, 0, null, 87, 78, 81, 0, 187, 255, null, null],
|
||||
[0, "하후위", null, 0, null, 71, 73, 49, 0, 204, 253, null, null],
|
||||
[0, "하후연", null, 2, "진류", 90, 91, 68, 1, 162, 219, null, null],
|
||||
[0, "하후은", null, 0, null, 63, 72, 53, 0, 180, 208, null, null],
|
||||
[0, "하후화", null, 0, null, 48, 51, 71, 0, 207, 265, null, null],
|
||||
[0, "하후혜", null, 0, null, 50, 44, 76, 0, 206, 242, null, null],
|
||||
[0, "하후현", null, 0, null, 54, 39, 76, 0, 208, 254, null, null],
|
||||
[0, "하후상", null, 0, null, 79, 72, 75, 0, 181, 225, null, null],
|
||||
[0, "하후덕", null, 0, null, 69, 73, 32, 0, 178, 218, null, null],
|
||||
[0, "하후돈", null, 2, "진류", 92, 90, 63, 1, 156, 220, null, null],
|
||||
[0, "하후패", null, 0, null, 79, 84, 77, 0, 202, 262, null, null],
|
||||
[0, "하후무", null, 0, null, 20, 7, 4, 0, 201, 259, null, null],
|
||||
[0, "가충", null, 0, null, 54, 31, 89, 0, 217, 282, null, null],
|
||||
[0, "하진", null, 0, null, 39, 40, 6, 0, 135, 189, null, null],
|
||||
[0, "화웅", null, 0, null, 88, 92, 61, 0, 155, 190, null, null],
|
||||
[0, "한윤", null, 7, "수춘", 26, 29, 65, 1, 155, 197, null, null],
|
||||
[0, "관우", null, 8, "서주", 97, 97, 79, 1, 162, 219, null, null, "술이 식기 전에 돌아오겠소"],
|
||||
[0, "환계", null, 23, "여강", 25, 26, 68, 1, 156, 221, null, null],
|
||||
[0, "관해", null, 0, null, 70, 82, 10, 0, 160, 193, null, null],
|
||||
[0, "관구검", null, 0, null, 78, 74, 55, 0, 202, 255, null, null],
|
||||
[0, "한현", null, 0, null, 22, 33, 62, 0, 163, 220, null, null],
|
||||
[0, "한호", null, 2, "진류", 69, 72, 84, 1, 164, 224, null, null],
|
||||
[0, "관흥", null, 0, null, 76, 86, 67, 0, 199, 234, null, null],
|
||||
[0, "관색", null, 0, null, 74, 88, 56, 0, 200, 239, null, null],
|
||||
[0, "한수", null, 20, "천수", 81, 75, 82, 12, 142, 215, null, null],
|
||||
[0, "한숭", null, 13, "강하", 25, 15, 71, 1, 154, 210, null, null],
|
||||
[0, "관정", null, 4, "북평", 36, 52, 73, 11, 158, 199, null, null],
|
||||
[0, "한섬", null, 0, null, 69, 66, 36, 0, 159, 196, null, null],
|
||||
[0, "감택", null, 0, null, 43, 49, 83, 0, 182, 243, null, null],
|
||||
[0, "한충", null, 0, null, 64, 67, 18, 0, 151, 184, null, null],
|
||||
[0, "한당", null, 23, "여강", 76, 85, 61, 1, 156, 227, null, null],
|
||||
[0, "한덕", null, 0, null, 63, 76, 25, 0, 171, 228, null, null],
|
||||
[0, "감녕", null, 13, "강하", 93, 94, 76, 1, 174, 219, null, null],
|
||||
[0, "한복", null, 0, null, 28, 3, 27, 0, 149, 193, null, null],
|
||||
[0, "관평", null, 0, null, 80, 82, 72, 0, 186, 219, null, null],
|
||||
[0, "간옹", null, 8, "하비", 20, 32, 75, 1, 164, 225, null, null],
|
||||
[0, "안량", null, 17, "업", 88, 94, 21, 1, 160, 200, null, null],
|
||||
[0, "위연", null, 0, null, 85, 92, 72, 0, 175, 234, null, null],
|
||||
[0, "국의", null, 17, "업", 82, 78, 51, 1, 146, 197, null, null],
|
||||
[0, "희지재", null, 2, "진류", 63, 7, 88, 1, 157, 196, null, null],
|
||||
[0, "위속", null, 3, "복양", 67, 78, 31, 1, 156, 200, null, null],
|
||||
[0, "위유", null, 0, null, 46, 29, 74, 0, 151, 193, null, null],
|
||||
[0, "우금2", null, 0, null, 72, 79, 38, 0, 173, 226, null, null],
|
||||
[0, "우보", null, 0, null, 38, 60, 21, 0, 159, 192, null, null],
|
||||
[0, "강유", null, 0, null, 92, 89, 90, 0, 202, 264, null, null],
|
||||
[0, "공지", null, 0, null, 43, 53, 62, 0, 178, 197, null, null],
|
||||
[0, "교유", null, 7, "수춘", 64, 69, 38, 1, 143, 195, null, null],
|
||||
[0, "공도", null, 24, "여남", 59, 70, 23, 1, 164, 200, null, null],
|
||||
[0, "허공", null, 0, null, 44, 66, 64, 0, 155, 200, null, null],
|
||||
[0, "허정", null, 21, "회계", 23, 5, 68, 1, 152, 222, null, null],
|
||||
[0, "허저", null, 2, "진류", 74, 96, 36, 1, 169, 226, null, null],
|
||||
[0, "허유", null, 17, "업", 36, 21, 80, 1, 155, 204, null, null],
|
||||
[0, "기령", null, 7, "수춘", 78, 83, 51, 1, 155, 199, null, null],
|
||||
[0, "김의", null, 0, null, 32, 38, 65, 0, 177, 218, null, null],
|
||||
[0, "김배삼결", null, 0, null, 64, 76, 19, 0, 192, 225, null, null],
|
||||
[0, "김선", null, 0, null, 52, 68, 13, 0, 155, 220, null, null],
|
||||
[0, "우번", null, 21, "회계", 43, 46, 86, 11, 164, 233, null, null],
|
||||
[0, "형도영", null, 0, null, 69, 80, 41, 0, 174, 208, null, null],
|
||||
[0, "엄안", null, 6, "자동", 82, 83, 72, 1, 151, 222, null, null],
|
||||
[0, "곽마", null, 0, null, 68, 71, 40, 0, 239, 281, null, null],
|
||||
[0, "엄준", null, 0, null, 28, 2, 71, 0, 169, 246, null, null],
|
||||
[0, "엄정", null, 0, null, 50, 55, 39, 0, 151, 189, null, null],
|
||||
[0, "엄백호", null, 12, "오", 67, 70, 23, 12, 142, 198, null, null, "이 몸은 동오의 덕왕 엄백호라고 하오!"],
|
||||
[0, "엄여", null, 12, "오", 61, 78, 44, 1, 145, 196, null, null],
|
||||
[0, "오의", null, 0, null, 79, 73, 70, 0, 165, 237, null, null],
|
||||
[0, "황개", null, 23, "여강", 80, 83, 70, 1, 154, 218, null, null],
|
||||
[0, "고간", null, 17, "업", 74, 56, 48, 1, 168, 206, null, null],
|
||||
[0, "황권", null, 6, "성도", 75, 59, 82, 11, 167, 240, null, null],
|
||||
[0, "고유", null, 17, "업", 57, 43, 72, 1, 174, 263, null, null],
|
||||
[0, "고순", null, 3, "복양", 85, 86, 60, 1, 162, 198, null, null],
|
||||
[0, "고상", null, 0, null, 68, 68, 53, 0, 194, 252, null, null],
|
||||
[0, "고승", null, 0, null, 67, 73, 41, 0, 145, 184, null, null],
|
||||
[0, "후성", null, 3, "복양", 74, 75, 68, 1, 158, 199, null, null],
|
||||
[0, "후선", null, 20, "천수", 63, 66, 34, 1, 175, 228, null, null],
|
||||
[0, "황조", null, 13, "강하", 74, 66, 55, 1, 148, 208, null, null],
|
||||
[0, "공손월", null, 0, null, 74, 72, 47, 0, 160, 192, null, null],
|
||||
[0, "공손연", null, 0, null, 65, 70, 60, 0, 205, 238, null, null],
|
||||
[0, "공손공", null, 11, "안평", 39, 17, 69, 1, 174, 240, null, null],
|
||||
[0, "공손강", null, 11, "안평", 72, 66, 65, 1, 172, 211, null, null],
|
||||
[0, "공손찬", null, 4, "북평", 84, 83, 75, 12, 152, 199, null, null],
|
||||
[0, "공손속", null, 4, "북평", 63, 68, 57, 1, 176, 199, null, null],
|
||||
[0, "공손도", null, 11, "안평", 67, 71, 66, 12, 154, 204, null, null],
|
||||
[0, "공손범", null, 4, "북평", 73, 69, 66, 1, 158, 199, null, null],
|
||||
[0, "황충", null, 0, null, 90, 93, 65, 0, 154, 220, null, null],
|
||||
[0, "고정", null, 0, null, 55, 63, 43, 0, 190, 251, null, null],
|
||||
[0, "고패", null, 0, null, 66, 61, 69, 0, 170, 212, null, null],
|
||||
[0, "황보숭", null, 0, null, 92, 61, 73, 0, 132, 195, null, null],
|
||||
[0, "공융", null, 19, "북해", 30, 5, 73, 12, 153, 208, null, null],
|
||||
[0, "고람", null, 17, "평원", 76, 82, 68, 1, 159, 201, null, null],
|
||||
[0, "오경", null, 0, null, 69, 66, 52, 0, 151, 211, null, null],
|
||||
[0, "호거아", null, 1, "홍농", 70, 82, 41, 1, 164, 206, null, null],
|
||||
[0, "호진", null, 0, null, 65, 74, 12, 0, 148, 190, null, null],
|
||||
[0, "올돌골", null, 0, null, 79, 86, 1, 0, 186, 225, null, null],
|
||||
[0, "호반", null, 0, null, 55, 55, 59, 0, 179, 233, null, null],
|
||||
[0, "오반", null, 0, null, 74, 71, 59, 0, 171, 234, null, null],
|
||||
[0, "고옹", null, 0, null, 43, 18, 80, 0, 168, 243, null, null],
|
||||
[0, "오란", null, 6, "성도", 68, 80, 35, 1, 170, 218, null, null],
|
||||
[0, "채화", null, 13, "장판", 39, 50, 1, 1, 166, 208, null, null],
|
||||
[0, "채중", null, 13, "장판", 40, 54, 2, 1, 168, 208, null, null],
|
||||
[0, "채모", null, 13, "양양", 70, 70, 77, 1, 160, 215, null, null],
|
||||
[0, "착융", null, 9, "건업", 60, 69, 38, 1, 161, 196, null, null],
|
||||
[0, "사마의", null, 0, null, 98, 63, 98, 0, 179, 251, null, null],
|
||||
[0, "사마염", null, 0, null, 69, 59, 77, 0, 238, 290, null, null],
|
||||
[0, "사마사", null, 0, null, 79, 64, 88, 0, 208, 255, null, null],
|
||||
[0, "사마소", null, 0, null, 76, 57, 87, 0, 211, 265, null, null],
|
||||
[0, "사마부", null, 0, null, 70, 37, 77, 0, 180, 272, null, null],
|
||||
[0, "사마랑", null, 0, null, 20, 21, 72, 0, 171, 217, null, null],
|
||||
[0, "사마가", null, 0, null, 66, 84, 25, 0, 167, 222, null, null],
|
||||
[0, "주흔", null, 21, "회계", 65, 62, 73, 1, 159, 196, null, null],
|
||||
[0, "주앙", null, 17, "남피", 74, 65, 65, 1, 162, 195, null, null],
|
||||
[0, "주지", null, 0, null, 65, 85, 41, 0, 233, 295, null, null],
|
||||
[0, "주창", null, 0, null, 68, 84, 42, 0, 164, 219, null, null],
|
||||
[0, "주태유평", null, 23, "여강", 84, 91, 48, 1, 171, 225, null, null],
|
||||
[0, "주방", null, 0, null, 52, 41, 80, 0, 200, 240, null, null],
|
||||
[0, "주유", null, 23, "여강", 95, 71, 96, 11, 175, 210, null, null],
|
||||
[0, "주환", null, 0, null, 84, 82, 77, 0, 177, 238, null, null],
|
||||
[0, "주거", null, 0, null, 77, 57, 72, 0, 190, 246, null, null],
|
||||
[0, "축융", null, 0, null, 74, 85, 29, 0, 182, 230, null, null],
|
||||
[0, "주준", null, 1, "장안", 89, 65, 70, 1, 149, 195, null, null],
|
||||
[0, "주연", null, 0, null, 79, 69, 71, 0, 182, 249, null, null],
|
||||
[0, "주치", null, 0, null, 71, 56, 72, 0, 156, 224, null, null],
|
||||
[0, "주포", null, 0, null, 68, 73, 34, 0, 191, 225, null, null],
|
||||
[0, "주령", null, 2, "진류", 71, 73, 67, 1, 170, 236, null, null],
|
||||
[0, "순욱", null, 2, "진류", 62, 14, 95, 1, 163, 212, null, null],
|
||||
[0, "순우경", null, 17, "남피", 72, 66, 29, 1, 146, 200, null, null],
|
||||
[0, "순심", null, 17, "업", 34, 25, 78, 1, 164, 208, null, null],
|
||||
[0, "순유", null, 2, "진류", 73, 26, 94, 1, 157, 214, null, null],
|
||||
[0, "장완", null, 0, null, 78, 34, 84, 0, 188, 246, null, null],
|
||||
[0, "종회", null, 0, null, 82, 51, 91, 0, 225, 264, null, null],
|
||||
[0, "장간", null, 0, null, 19, 6, 66, 0, 175, 208, null, null],
|
||||
[0, "장의거", null, 17, "업", 71, 58, 59, 1, 159, 205, null, null],
|
||||
[0, "장흠", null, 23, "여강", 78, 84, 54, 1, 159, 208, null, null],
|
||||
[0, "장제3", null, 0, null, 48, 43, 85, 0, 188, 249, null, null],
|
||||
[0, "초주", null, 0, null, 16, 3, 67, 0, 201, 270, null, null],
|
||||
[0, "초촉", null, 17, "업", 65, 76, 33, 1, 159, 208, null, null],
|
||||
[0, "상총", null, 0, null, 76, 61, 77, 0, 195, 240, null, null],
|
||||
[0, "종요", null, 1, "장안", 70, 24, 77, 1, 151, 230, null, null],
|
||||
[0, "상랑", null, 0, null, 28, 18, 74, 0, 167, 247, null, null],
|
||||
[0, "서영", null, 0, null, 80, 76, 62, 0, 147, 192, null, null],
|
||||
[0, "제갈각", null, 0, null, 72, 47, 90, 0, 203, 253, null, null],
|
||||
[0, "제갈근", null, 0, null, 75, 34, 81, 0, 174, 241, null, null],
|
||||
[0, "제갈균", null, 0, null, 29, 25, 52, 0, 185, 252, null, null],
|
||||
[0, "제갈탄", null, 0, null, 72, 72, 76, 0, 206, 258, null, null],
|
||||
[0, "제갈량", null, 0, null, 98, 38, 100, 0, 184, 234, null, null],
|
||||
[0, "서황", null, 1, "사곡", 91, 90, 78, 1, 165, 228, null, null],
|
||||
[0, "서서", null, 0, null, 87, 64, 93, 0, 160, 300, null, null],
|
||||
[0, "서성", null, 0, null, 86, 81, 78, 0, 178, 232, null, null],
|
||||
[0, "서막", null, 0, null, 55, 32, 68, 0, 171, 249, null, null],
|
||||
[0, "신의", null, 0, null, 55, 59, 60, 0, 190, 252, null, null],
|
||||
[0, "신탐", null, 0, null, 56, 62, 54, 0, 188, 254, null, null],
|
||||
[0, "심배", null, 17, "업", 80, 60, 83, 1, 156, 204, null, null],
|
||||
[0, "신비", null, 17, "남피", 39, 24, 79, 1, 171, 240, null, null],
|
||||
[0, "신평", null, 17, "남피", 69, 43, 77, 1, 165, 204, null, null],
|
||||
[0, "진복", null, 0, null, 31, 7, 73, 0, 160, 226, null, null],
|
||||
[0, "진랑", null, 0, null, 58, 70, 35, 0, 192, 234, null, null],
|
||||
[0, "휴고", null, 15, "진양", 63, 71, 38, 1, 151, 199, null, null],
|
||||
[0, "추단", null, 0, null, 61, 64, 34, 0, 144, 193, null, null],
|
||||
[0, "성의", null, 20, "천수", 74, 72, 41, 1, 168, 211, null, null],
|
||||
[0, "성공영", null, 20, "천수", 73, 71, 80, 11, 172, 220, null, null],
|
||||
[0, "설종", null, 0, null, 34, 16, 73, 0, 187, 243, null, null],
|
||||
[0, "선경", null, 4, "북평", 71, 68, 43, 1, 156, 199, null, null],
|
||||
[0, "전종", null, 0, null, 78, 72, 75, 0, 183, 249, null, null],
|
||||
[0, "조예", null, 0, null, 72, 52, 82, 0, 205, 239, null, null],
|
||||
[0, "조휴", null, 2, "진류", 74, 75, 62, 1, 174, 228, null, null],
|
||||
[0, "송헌", null, 3, "복양", 68, 77, 38, 1, 157, 200, null, null],
|
||||
[0, "조앙", null, 2, "진류", 74, 67, 69, 1, 161, 197, null, null],
|
||||
[0, "조홍 자렴", null, 2, "진류", 79, 81, 44, 1, 169, 232, null, null, "좌절감이 사나이를 키우는 것이다!"],
|
||||
[0, "조순", null, 2, "진류", 75, 71, 63, 1, 170, 210, null, null],
|
||||
[0, "조창", null, 0, null, 82, 90, 40, 0, 190, 223, null, null],
|
||||
[0, "조식", null, 0, null, 33, 22, 81, 0, 192, 232, null, null],
|
||||
[0, "조진", null, 0, null, 81, 74, 70, 0, 185, 231, null, null],
|
||||
[0, "조인", null, 2, "진류", 89, 86, 62, 1, 168, 223, null, null],
|
||||
[0, "조성", null, 0, null, 54, 74, 38, 0, 163, 198, null, null],
|
||||
[0, "조조", null, 2, "진류", 99, 72, 91, 12, 151, 220, null, null, "내가 천하 사람들을 저버릴지언정, 천하 사람들이 나를 저버리게 하지는 않을 것이다."],
|
||||
[0, "조충", null, 0, null, 31, 21, 85, 0, 196, 207, null, null],
|
||||
[0, "장패", null, 0, null, 78, 75, 53, 0, 165, 231, null, null],
|
||||
[0, "조비", null, 0, null, 70, 71, 83, 0, 187, 226, null, null],
|
||||
[0, "조표", null, 8, "서주", 66, 69, 32, 1, 151, 196, null, null],
|
||||
[0, "저수", null, 17, "업", 78, 35, 90, 1, 156, 201, null, null],
|
||||
[0, "소비", null, 0, null, 69, 63, 66, 0, 172, 221, null, null],
|
||||
[0, "조무", null, 0, null, 70, 71, 66, 0, 155, 193, null, null],
|
||||
[0, "손환", null, 0, null, 82, 73, 76, 0, 166, 228, null, null],
|
||||
[0, "손견", null, 0, null, 94, 90, 79, 0, 156, 192, null, null, "내가 옥새를 가지고 있다면 나는 칼과 화살을 맞아 죽을 것이다."],
|
||||
[0, "손건", null, 0, null, 34, 33, 79, 0, 165, 215, null, null],
|
||||
[0, "손권", null, 0, null, 79, 67, 80, 0, 182, 252, null, null],
|
||||
[0, "손교", null, 0, null, 77, 72, 67, 0, 181, 219, null, null],
|
||||
[0, "손책", null, 23, "여강", 96, 92, 74, 12, 175, 200, null, null],
|
||||
[0, "손소", null, 0, null, 80, 79, 76, 0, 188, 241, null, null],
|
||||
[0, "손정", null, 23, "여강", 66, 53, 73, 1, 160, 206, null, null],
|
||||
[0, "손중", null, 0, null, 62, 71, 36, 0, 154, 184, null, null],
|
||||
[0, "손등", null, 0, null, 62, 37, 79, 0, 209, 241, null, null],
|
||||
[0, "손유", null, 0, null, 77, 70, 68, 0, 177, 215, null, null],
|
||||
[0, "손익", null, 0, null, 67, 78, 27, 0, 184, 204, null, null],
|
||||
[0, "손례", null, 0, null, 78, 81, 67, 0, 180, 250, null, null],
|
||||
[0, "태사자", null, 9, "건업", 90, 93, 69, 1, 166, 206, null, null],
|
||||
[0, "대래동주", null, 0, null, 51, 53, 54, 0, 195, 249, null, null],
|
||||
[0, "대릉", null, 0, null, 70, 75, 39, 0, 199, 258, null, null],
|
||||
[0, "타사대왕", null, 0, null, 62, 59, 70, 0, 186, 225, null, null],
|
||||
[0, "담웅", null, 0, null, 58, 73, 14, 0, 188, 221, null, null],
|
||||
[0, "장윤", null, 13, "양양", 72, 67, 42, 1, 163, 208, null, null],
|
||||
[0, "조운", null, 4, "북평", 96, 96, 77, 1, 168, 229, null, null],
|
||||
[0, "장위", null, 14, "한중", 74, 66, 45, 1, 172, 228, null, null, "쌀 다섯 말만 가지고 오시오~ 모두 고쳐드리리다."],
|
||||
[0, "장영", null, 9, "건업", 75, 74, 41, 1, 154, 196, null, null],
|
||||
[0, "장연", null, 15, "진양", 76, 81, 57, 12, 153, 210, null, null],
|
||||
[0, "장횡", null, 9, "건업", 59, 70, 23, 1, 178, 211, null, null],
|
||||
[0, "장온", null, 0, null, 32, 10, 72, 0, 193, 231, null, null],
|
||||
[0, "장개", null, 0, null, 34, 66, 8, 0, 155, 194, null, null],
|
||||
[0, "장각", null, 0, null, 87, 25, 86, 0, 140, 184, null, null],
|
||||
[0, "장기", null, 0, null, 74, 35, 76, 0, 170, 223, null, null],
|
||||
[0, "장의", null, 17, "남피", 79, 72, 75, 1, 153, 215, null, null],
|
||||
[0, "장훈", null, 7, "수춘", 72, 68, 41, 1, 156, 206, null, null],
|
||||
[0, "장굉", null, 23, "여강", 24, 22, 86, 1, 153, 212, null, null],
|
||||
[0, "장합", null, 17, "평원", 89, 89, 73, 1, 167, 231, null, null],
|
||||
[0, "조홍", null, 0, null, 66, 72, 28, 0, 156, 184, null, null],
|
||||
[0, "장제1", null, 1, "홍농", 70, 66, 55, 1, 144, 196, null, null],
|
||||
[0, "장수", null, 1, "홍농", 80, 73, 64, 1, 154, 207, null, null],
|
||||
[0, "장소 자포", null, 23, "여강", 33, 3, 84, 1, 156, 236, null, null],
|
||||
[0, "장송", null, 0, null, 25, 6, 88, 0, 170, 212, null, null],
|
||||
[0, "장임", null, 6, "성도", 84, 84, 78, 1, 169, 214, null, null],
|
||||
[0, "장제", null, 0, null, 67, 43, 80, 0, 236, 280, null, null],
|
||||
[0, "장막", null, 3, "복양", 52, 53, 71, 1, 155, 195, null, null],
|
||||
[0, "조범", null, 0, null, 28, 21, 32, 0, 168, 218, null, null],
|
||||
[0, "장비", null, 8, "하비", 94, 98, 30, 1, 165, 221, null, null],
|
||||
[0, "장포", null, 0, null, 78, 87, 48, 0, 198, 219, null, null],
|
||||
[0, "장보", null, 0, null, 83, 71, 81, 0, 148, 184, null, null],
|
||||
[0, "장만성", null, 0, null, 75, 81, 45, 0, 143, 184, null, null],
|
||||
[0, "장양", null, 5, "진양", 72, 70, 66, 12, 151, 195, null, null],
|
||||
[0, "장익", null, 0, null, 75, 74, 76, 0, 188, 264, null, null],
|
||||
[0, "장료", null, 3, "복양", 95, 92, 78, 1, 169, 222, null, null],
|
||||
[0, "장량", null, 0, null, 78, 80, 74, 0, 153, 184, null, null],
|
||||
[0, "조루", null, 0, null, 70, 43, 68, 0, 183, 219, null, null],
|
||||
[0, "장로", null, 14, "한중", 51, 26, 74, 12, 163, 216, null, null],
|
||||
[0, "진횡", null, 9, "건업", 64, 63, 26, 1, 161, 196, null, null],
|
||||
[0, "진응", null, 0, null, 62, 72, 45, 0, 172, 208, null, null],
|
||||
[0, "진기", null, 7, "수춘", 58, 65, 43, 1, 165, 198, null, null],
|
||||
[0, "진궁", null, 3, "복양", 84, 55, 89, 11, 154, 198, null, null, "조조…그자는 더 이상 내가 감당하기 어려워졌다"],
|
||||
[0, "진교", null, 0, null, 61, 27, 77, 0, 175, 237, null, null],
|
||||
[0, "진군", null, 8, "하비", 32, 14, 75, 11, 167, 236, null, null],
|
||||
[0, "진규", null, 8, "서주", 30, 4, 82, 1, 132, 206, null, null],
|
||||
[0, "진식", null, 0, null, 70, 71, 25, 0, 191, 230, null, null],
|
||||
[0, "진진", null, 0, null, 46, 46, 69, 0, 170, 235, null, null],
|
||||
[0, "진태", null, 0, null, 84, 77, 86, 0, 210, 260, null, null],
|
||||
[0, "진등", null, 8, "하비", 79, 64, 81, 1, 169, 208, null, null],
|
||||
[0, "진도", null, 0, null, 76, 71, 67, 0, 171, 237, null, null],
|
||||
[0, "진무", null, 0, null, 76, 87, 43, 0, 176, 215, null, null],
|
||||
[0, "진란", null, 7, "수춘", 66, 69, 40, 1, 157, 204, null, null],
|
||||
[0, "진림", null, 17, "남피", 25, 9, 75, 1, 160, 217, null, null],
|
||||
[0, "정욱", null, 2, "진류", 70, 49, 90, 1, 141, 220, null, null],
|
||||
[0, "정원지", null, 0, null, 69, 74, 17, 0, 145, 184, null, null],
|
||||
[0, "정은", null, 10, "서량", 69, 73, 40, 1, 184, 220, null, null],
|
||||
[0, "정원", null, 0, null, 70, 76, 35, 0, 137, 189, null, null],
|
||||
[0, "정보", null, 23, "여강", 84, 79, 79, 1, 151, 216, null, null],
|
||||
[0, "정봉승연", null, 0, null, 81, 80, 75, 0, 190, 271, null, null],
|
||||
[0, "전위", null, 0, null, 72, 95, 35, 0, 160, 197, null, null],
|
||||
[0, "전해", null, 4, "북평", 68, 65, 58, 1, 154, 199, null, null],
|
||||
[0, "전주", null, 0, null, 66, 66, 73, 0, 169, 214, null, null],
|
||||
[0, "전풍", null, 17, "업", 72, 29, 93, 11, 162, 200, null, null],
|
||||
[0, "전예", null, 4, "계", 80, 72, 80, 1, 171, 252, null, null],
|
||||
[0, "동윤", null, 0, null, 47, 28, 79, 0, 192, 246, null, null],
|
||||
[0, "동화", null, 0, null, 57, 34, 75, 0, 168, 219, null, null],
|
||||
[0, "등애", null, 0, null, 93, 87, 89, 0, 197, 264, null, null],
|
||||
[0, "도겸", null, 0, null, 51, 27, 64, 0, 132, 194, null, null],
|
||||
[0, "등현", null, 0, null, 61, 73, 45, 0, 188, 248, null, null],
|
||||
[0, "등지", null, 0, null, 73, 52, 81, 0, 178, 251, null, null],
|
||||
[0, "동습", null, 0, null, 72, 85, 50, 0, 169, 215, null, null],
|
||||
[0, "동승", null, 1, "장안", 56, 53, 65, 1, 154, 200, null, null],
|
||||
[0, "동소", null, 5, "하내", 34, 25, 80, 11, 156, 236, null, null],
|
||||
[0, "동탁", null, 0, null, 85, 87, 73, 0, 139, 192, null, null],
|
||||
[0, "동도나", null, 0, null, 68, 73, 31, 0, 189, 225, null, null],
|
||||
[0, "동민", null, 0, null, 49, 60, 25, 0, 149, 192, null, null],
|
||||
[0, "등무", null, 0, null, 61, 75, 29, 0, 147, 184, null, null],
|
||||
[0, "두기", null, 0, null, 66, 32, 75, 0, 163, 224, null, null],
|
||||
[0, "두예", null, 0, null, 84, 30, 85, 0, 222, 284, null, null],
|
||||
[0, "배원소", null, 0, null, 49, 65, 25, 0, 169, 200, null, null],
|
||||
[0, "마완", null, 0, null, 71, 74, 16, 0, 170, 211, null, null],
|
||||
[0, "마휴", null, 0, null, 68, 73, 48, 0, 178, 212, null, null],
|
||||
[0, "파재", null, 0, null, 74, 74, 55, 0, 145, 184, null, null],
|
||||
[0, "마준", null, 0, null, 38, 52, 31, 0, 196, 230, null, null],
|
||||
[0, "마속", null, 0, null, 61, 67, 87, 0, 190, 228, null, null],
|
||||
[0, "마대", null, 0, null, 79, 85, 62, 0, 183, 246, null, null],
|
||||
[0, "마충", null, 0, null, 78, 72, 68, 0, 187, 249, null, null],
|
||||
[0, "마초", null, 0, "서량", 93, 97, 44, 0, 176, 222, null, null],
|
||||
[0, "마철", null, 0, null, 70, 62, 58, 0, 179, 212, null, null],
|
||||
[0, "마등", null, 10, "서량", 82, 80, 52, 12, 149, 212, null, null],
|
||||
[0, "마량", null, 0, null, 53, 23, 88, 0, 187, 222, null, null],
|
||||
[0, "반준", null, 0, null, 61, 18, 83, 0, 174, 239, null, null],
|
||||
[0, "반장", null, 0, null, 76, 80, 74, 0, 177, 234, null, null],
|
||||
[0, "번조", null, 0, null, 66, 73, 31, 0, 149, 192, null, null],
|
||||
[0, "번능", null, 0, null, 69, 61, 36, 0, 158, 195, null, null],
|
||||
[0, "반봉", null, 0, null, 56, 77, 4, 0, 155, 190, null, null],
|
||||
[0, "비의", null, 0, null, 77, 30, 83, 0, 193, 253, null, null],
|
||||
[0, "비연", null, 0, null, 60, 66, 21, 0, 196, 238, null, null],
|
||||
[0, "비시", null, 0, null, 18, 28, 65, 0, 176, 240, null, null],
|
||||
[0, "미축", null, 8, "하비", 33, 29, 78, 1, 165, 220, null, null],
|
||||
[0, "미방", null, 8, "하비", 54, 61, 32, 1, 169, 226, null, null],
|
||||
[0, "비요", null, 0, null, 70, 66, 73, 0, 192, 228, null, null],
|
||||
[0, "무안국", null, 19, "북해", 70, 83, 34, 1, 156, 193, null, null],
|
||||
[0, "풍습", null, 0, null, 72, 71, 39, 0, 182, 222, null, null],
|
||||
[0, "부사인", null, 0, null, 46, 63, 37, 0, 182, 225, null, null],
|
||||
[0, "부첨", null, 0, null, 73, 85, 76, 0, 216, 263, null, null],
|
||||
[0, "부손", null, 0, null, 29, 13, 70, 0, 162, 230, null, null],
|
||||
[0, "문앙", null, 0, null, 76, 93, 65, 0, 238, 291, null, null],
|
||||
[0, "문흠", null, 0, null, 74, 83, 43, 0, 200, 258, null, null],
|
||||
[0, "문추", null, 17, "남피", 89, 93, 47, 1, 169, 200, null, null],
|
||||
[0, "문빙", null, 0, null, 80, 82, 69, 0, 178, 237, null, null],
|
||||
[0, "변희", null, 0, null, 56, 70, 62, 0, 169, 200, null, null],
|
||||
[0, "망아장", null, 0, null, 45, 79, 3, 0, 191, 225, null, null],
|
||||
[0, "봉기", null, 17, "남피", 43, 21, 84, 1, 153, 202, null, null],
|
||||
[0, "방희", null, 0, null, 60, 38, 69, 0, 152, 192, null, null],
|
||||
[0, "법정", null, 0, null, 82, 47, 94, 0, 176, 220, null, null],
|
||||
[0, "방통", null, 0, null, 85, 34, 97, 0, 178, 213, null, null],
|
||||
[0, "방덕", null, 10, "서량", 89, 94, 75, 11, 170, 219, null, null],
|
||||
[0, "포륭", null, 0, null, 58, 76, 49, 0, 174, 208, null, null],
|
||||
[0, "목순", null, 0, null, 52, 77, 11, 0, 157, 190, null, null],
|
||||
[0, "목록대왕", null, 0, null, 65, 71, 39, 0, 184, 225, null, null],
|
||||
[0, "보즐", null, 0, null, 72, 51, 84, 0, 177, 247, null, null],
|
||||
[0, "만총", null, 0, null, 84, 64, 82, 0, 170, 242, null, null],
|
||||
[0, "모개", null, 0, null, 63, 39, 60, 0, 161, 216, null, null],
|
||||
[0, "맹획", null, 0, null, 87, 87, 42, 0, 186, 245, null, null],
|
||||
[0, "맹달", null, 0, null, 75, 73, 75, 0, 172, 228, null, null],
|
||||
[0, "맹우", null, 0, null, 50, 68, 22, 0, 190, 243, null, null],
|
||||
[0, "유섭", null, 7, "완", 62, 79, 24, 1, 158, 190, null, null],
|
||||
[0, "양부2", null, 0, null, 62, 68, 68, 0, 167, 212, null, null],
|
||||
[0, "옹개", null, 0, null, 73, 75, 57, 0, 188, 225, null, null],
|
||||
[0, "양의", null, 0, null, 68, 40, 82, 0, 190, 235, null, null],
|
||||
[0, "양호", null, 0, null, 90, 64, 84, 0, 221, 278, null, null],
|
||||
[0, "양홍", null, 7, "완", 33, 15, 77, 1, 152, 199, null, null],
|
||||
[0, "양앙", null, 0, null, 68, 72, 38, 0, 172, 215, null, null],
|
||||
[0, "양추1", null, 0, null, 52, 65, 40, 0, 159, 199, null, null],
|
||||
[0, "양수", null, 0, null, 25, 4, 83, 0, 175, 219, null, null],
|
||||
[0, "양추2", null, 0, null, 67, 64, 60, 0, 172, 238, null, null],
|
||||
[0, "양송", null, 0, null, 1, 5, 28, 0, 167, 215, null, null],
|
||||
[0, "양임", null, 14, "한중", 70, 78, 56, 1, 170, 215, null, null],
|
||||
[0, "양조", null, 0, null, 53, 61, 36, 0, 223, 286, null, null],
|
||||
[0, "양백", null, 14, "한중", 44, 45, 19, 1, 171, 214, null, null],
|
||||
[0, "양부1", null, 0, null, 67, 50, 83, 0, 178, 239, null, null],
|
||||
[0, "양봉1", null, 0, null, 66, 65, 31, 0, 153, 197, null, null],
|
||||
[0, "양봉2", null, 0, null, 54, 62, 53, 0, 191, 252, null, null],
|
||||
[0, "뇌동", null, 0, null, 69, 78, 51, 0, 172, 218, null, null],
|
||||
[0, "뇌박", null, 7, "수춘", 62, 70, 36, 1, 157, 206, null, null],
|
||||
[0, "이이", null, 0, null, 63, 77, 19, 0, 187, 222, null, null],
|
||||
[0, "이회", null, 0, null, 79, 65, 79, 0, 175, 231, null, null],
|
||||
[0, "이각", null, 1, "장안", 69, 72, 24, 12, 148, 198, null, null],
|
||||
[0, "육항", null, 0, null, 91, 63, 87, 0, 226, 274, null, null],
|
||||
[0, "육적", null, 0, null, 20, 7, 72, 0, 187, 219, null, null],
|
||||
[0, "육손", null, 0, null, 94, 69, 95, 0, 183, 245, null, null],
|
||||
[0, "이엄", null, 0, null, 83, 84, 76, 0, 172, 234, null, null],
|
||||
[0, "이유", null, 0, null, 61, 26, 93, 0, 150, 192, null, null, "장안으로 천도를 하는것이 어떻겠습니까?"],
|
||||
[0, "이숙", null, 0, null, 46, 69, 61, 0, 156, 192, null, null],
|
||||
[0, "이통", null, 0, "여남", 73, 83, 62, 0, 168, 209, null, null],
|
||||
[0, "이전", null, 0, null, 78, 77, 79, 0, 181, 216, null, null],
|
||||
[0, "이풍", null, 7, "수춘", 69, 74, 50, 1, 158, 199, null, null],
|
||||
[0, "유언", null, 0, null, 53, 38, 80, 0, 132, 194, null, null],
|
||||
[0, "유괴", null, 6, "성도", 71, 73, 67, 1, 165, 214, null, null],
|
||||
[0, "유기", null, 13, "양양", 52, 12, 63, 1, 174, 210, null, null],
|
||||
[0, "유우", null, 0, null, 55, 33, 70, 0, 145, 193, null, null],
|
||||
[0, "유훈", null, 7, "수춘", 47, 63, 35, 1, 163, 216, null, null],
|
||||
[0, "유현", null, 0, null, 54, 66, 51, 0, 188, 252, null, null],
|
||||
[0, "유순", null, 0, null, 63, 46, 41, 0, 172, 255, null, null],
|
||||
[0, "유장", null, 6, "성도", 31, 5, 34, 12, 162, 219, null, null],
|
||||
[0, "유심", null, 0, null, 60, 62, 70, 0, 238, 264, null, null],
|
||||
[0, "유선", null, 0, null, 3, 5, 9, 0, 208, 270, null, null],
|
||||
[0, "유종", null, 0, null, 24, 22, 65, 0, 195, 220, null, null],
|
||||
[0, "유도", null, 0, null, 25, 23, 26, 0, 168, 214, null, null],
|
||||
[0, "유파", null, 0, null, 29, 22, 75, 0, 186, 222, null, null],
|
||||
[0, "유반", null, 0, null, 69, 76, 47, 0, 168, 210, null, null],
|
||||
[0, "유비", null, 8, "하비", 81, 77, 80, 12, 161, 223, null, null],
|
||||
[0, "유표1", null, 13, "양양", 48, 31, 72, 1, 142, 208, null, null, "강하 팔준에서 제일가는 이 유표에게로 오시오!"],
|
||||
[0, "유복", null, 0, null, 64, 49, 74, 0, 164, 208, null, null],
|
||||
[0, "유벽", null, 24, "여남", 70, 72, 47, 12, 145, 201, null, null],
|
||||
[0, "유봉", null, 0, null, 75, 79, 44, 0, 188, 220, null, null],
|
||||
[0, "유엽", null, 0, null, 56, 32, 92, 0, 176, 235, null, null],
|
||||
[0, "유요", null, 9, "건업", 62, 67, 46, 12, 156, 198, null, null],
|
||||
[0, "여위황", null, 17, "남피", 58, 63, 29, 1, 159, 200, null, null],
|
||||
[0, "요화", null, 0, null, 73, 76, 69, 0, 168, 264, null, null],
|
||||
[0, "양흥", null, 10, "서량", 61, 65, 19, 1, 169, 211, null, null],
|
||||
[0, "양습", null, 0, null, 73, 40, 74, 0, 168, 230, null, null],
|
||||
[0, "양서", null, 0, null, 56, 62, 66, 0, 198, 260, null, null],
|
||||
[0, "능조", null, 0, null, 75, 81, 42, 0, 168, 203, null, null],
|
||||
[0, "능통", null, 0, null, 77, 89, 60, 0, 189, 217, null, null],
|
||||
[0, "여개", null, 0, null, 53, 30, 67, 0, 194, 227, null, null],
|
||||
[0, "여건", null, 2, "진류", 57, 70, 62, 1, 173, 238, null, null],
|
||||
[0, "여광", null, 0, null, 56, 70, 13, 0, 162, 207, null, null],
|
||||
[0, "여상", null, 0, null, 54, 71, 12, 0, 164, 207, null, null],
|
||||
[0, "여대", null, 0, null, 75, 71, 70, 0, 161, 256, null, null],
|
||||
[0, "여범", null, 0, null, 73, 63, 75, 0, 169, 228, null, null],
|
||||
[0, "여포", null, 3, "복양", 97, 100, 26, 12, 156, 198, null, null, "크하하하 다 부숴 버리겠다!"],
|
||||
[0, "여몽", null, 0, null, 82, 81, 20, 0, 178, 219, null, null],
|
||||
[0, "윤직", null, 0, null, 46, 42, 73, 0, 196, 237, null, null],
|
||||
[0, "냉포", null, 0, null, 71, 82, 61, 0, 176, 214, null, null],
|
||||
[0, "진취", null, 13, "강하", 56, 63, 45, 1, 174, 208, null, null],
|
||||
[0, "노숙", null, 0, null, 84, 56, 92, 0, 172, 217, null, null],
|
||||
[0, "노식", null, 0, null, 86, 63, 82, 0, 139, 192, null, null],
|
||||
[0, "범강", null, 0, null, 49, 50, 36, 0, 174, 222, null, null],
|
||||
[0, "장달", null, 0, null, 43, 56, 34, 0, 175, 222, null, null],
|
||||
[0, "규람", null, 0, null, 55, 70, 58, 0, 165, 204, null, null],
|
||||
[0, "대원", null, 0, null, 38, 32, 68, 0, 166, 204, null, null],
|
||||
[0, "왕기", null, 0, null, 77, 72, 86, 0, 190, 261, null, null],
|
||||
[0, "왕상", null, 0, null, 23, 19, 68, 0, 180, 268, null, null],
|
||||
[0, "왕돈", null, 0, null, 60, 66, 46, 0, 198, 256, null, null],
|
||||
[0, "악침", null, 0, null, 73, 75, 43, 0, 196, 257, null, null],
|
||||
[0, "하제", null, 0, null, 83, 70, 82, 0, 171, 227, null, null],
|
||||
[0, "환범", null, 0, null, 40, 11, 78, 0, 199, 249, null, null],
|
||||
[0, "견초", null, 17, "남피", 72, 79, 73, 1, 168, 244, null, null],
|
||||
[0, "강경", null, 0, null, 69, 64, 54, 0, 178, 212, null, null],
|
||||
[0, "오찬", null, 0, null, 68, 41, 78, 0, 181, 245, null, null],
|
||||
[0, "호질", null, 0, null, 73, 46, 74, 0, 192, 250, null, null],
|
||||
[0, "오질", null, 0, null, 16, 29, 68, 0, 177, 230, null, null],
|
||||
[0, "호준", null, 0, null, 79, 76, 62, 0, 200, 256, null, null],
|
||||
[0, "고담", null, 0, null, 53, 23, 77, 0, 203, 244, null, null],
|
||||
[0, "최림", null, 0, null, 32, 22, 66, 0, 187, 244, null, null],
|
||||
[0, "사마망", null, 0, null, 78, 58, 83, 0, 205, 271, null, null],
|
||||
[0, "주이", null, 0, null, 70, 73, 61, 0, 201, 257, null, null],
|
||||
[0, "전단", null, 0, null, 67, 64, 39, 0, 204, 261, null, null],
|
||||
[0, "조우", null, 0, null, 50, 43, 37, 0, 199, 278, null, null],
|
||||
[0, "조상", null, 0, null, 39, 33, 30, 0, 207, 249, null, null],
|
||||
[0, "조방", null, 0, null, 20, 14, 27, 0, 232, 274, null, null],
|
||||
[0, "태사향", null, 0, null, 57, 66, 48, 0, 189, 246, null, null],
|
||||
[0, "장휴", null, 0, null, 63, 27, 74, 0, 204, 244, null, null],
|
||||
[0, "장호", null, 0, null, 75, 74, 51, 0, 195, 240, null, null],
|
||||
[0, "조광", null, 0, null, 67, 71, 53, 0, 210, 263, null, null],
|
||||
[0, "장집", null, 0, null, 76, 29, 80, 0, 196, 254, null, null],
|
||||
[0, "장승", null, 0, null, 77, 70, 75, 0, 178, 244, null, null],
|
||||
[0, "장소", null, 0, null, 23, 56, 31, 0, 202, 264, null, null],
|
||||
[0, "조통", null, 0, null, 68, 67, 61, 0, 209, 260, null, null],
|
||||
[0, "진표", null, 0, null, 66, 42, 74, 0, 207, 237, null, null],
|
||||
[0, "정무", null, 0, null, 56, 35, 75, 0, 201, 265, null, null],
|
||||
[0, "정봉", null, 0, null, 66, 66, 53, 0, 198, 266, null, null],
|
||||
[0, "전만", null, 0, null, 51, 73, 39, 0, 181, 235, null, null],
|
||||
[0, "마균", null, 0, null, 22, 17, 71, 0, 200, 259, null, null],
|
||||
[0, "방회", null, 0, null, 69, 75, 49, 0, 205, 272, null, null],
|
||||
[0, "서구", null, 0, null, 69, 42, 70, 0, 149, 208, null, null],
|
||||
[0, "육개", null, 0, null, 65, 61, 79, 0, 198, 269, null, null],
|
||||
[0, "유찬", null, 0, null, 78, 81, 64, 0, 172, 255, null, null],
|
||||
[0, "유소", null, 0, null, 68, 53, 74, 0, 195, 264, null, null],
|
||||
[0, "요립", null, 0, null, 32, 15, 76, 0, 181, 250, null, null],
|
||||
[0, "여거", null, 0, null, 78, 69, 71, 0, 196, 256, null, null],
|
||||
[0, "제갈첨", null, 0, null, 70, 57, 76, 0, 227, 263, null, null],
|
||||
[0, "손휴", null, 0, null, 57, 42, 78, 0, 235, 264, null, null],
|
||||
[0, "극정", null, 0, null, 42, 23, 68, 0, 208, 278, null, null],
|
||||
[0, "호분", null, 0, null, 71, 76, 50, 0, 222, 288, null, null],
|
||||
[0, "손량", null, 0, null, 32, 26, 70, 0, 243, 260, null, null],
|
||||
[0, "마막", null, 0, null, 34, 12, 44, 0, 221, 266, null, null],
|
||||
[0, "손호", null, 0, null, 41, 73, 71, 0, 242, 284, null, null],
|
||||
[0, "조모", null, 0, null, 54, 61, 59, 0, 241, 260, null, null],
|
||||
[0, "조환", null, 0, null, 30, 28, 30, 0, 246, 302, null, null],
|
||||
[0, "사마유", null, 0, null, 71, 68, 75, 0, 248, 283, null, null],
|
||||
[0, "제갈상", null, 0, null, 64, 79, 62, 0, 246, 263, null, null],
|
||||
[0, "손화", null, 0, null, 31, 39, 68, 0, 224, 253, null, null],
|
||||
[0, "염우", null, 0, null, 52, 61, 51, 0, 209, 264, null, null],
|
||||
[0, "왕경", null, 0, null, 67, 63, 71, 0, 206, 260, null, null],
|
||||
[0, "왕혼", null, 0, null, 74, 71, 63, 0, 223, 297, null, null],
|
||||
[0, "곽혁", null, 0, null, 33, 29, 70, 0, 187, 225, null, null],
|
||||
[0, "가범", null, 0, null, 60, 62, 73, 0, 202, 237, null, null],
|
||||
[0, "서질", null, 0, null, 68, 83, 24, 0, 207, 253, null, null],
|
||||
[0, "손준", null, 0, null, 64, 70, 65, 0, 219, 256, null, null],
|
||||
[0, "손침", null, 0, null, 58, 55, 65, 0, 231, 258, null, null],
|
||||
[0, "우사", null, 0, null, 61, 34, 74, 0, 217, 273, null, null],
|
||||
[0, "진건", null, 0, null, 72, 71, 65, 0, 214, 292, null, null],
|
||||
[0, "동궐", null, 0, null, 65, 59, 76, 0, 204, 271, null, null],
|
||||
[0, "당자", null, 0, null, 61, 71, 50, 0, 196, 265, null, null],
|
||||
[0, "등충", null, 0, null, 76, 86, 69, 0, 230, 264, null, null],
|
||||
[0, "황숭", null, 0, null, 67, 63, 72, 0, 208, 263, null, null],
|
||||
[0, "화핵", null, 0, null, 20, 24, 71, 0, 217, 278, null, null],
|
||||
[0, "허의", null, 0, null, 51, 74, 15, 0, 213, 263, null, null],
|
||||
[0, "송겸", null, 0, null, 66, 78, 42, 0, 175, 227, null, null],
|
||||
[0, "호제", null, 0, null, 57, 39, 68, 0, 207, 264, null, null],
|
||||
[0, "나헌", null, 0, null, 85, 70, 77, 0, 218, 270, null, null],
|
||||
[0, "아들노숙", null, 0, null, 73, 53, 74, 0, 217, 272, null, null],
|
||||
[0, "구성", null, 0, null, 63, 70, 38, 0, 157, 187, null, null],
|
||||
[0, "어부라", null, 0, null, 72, 68, 21, 0, 150, 195, null, null],
|
||||
[0, "아하소과", null, 0, null, 61, 76, 13, 0, 204, 253, null, null],
|
||||
[0, "가비능", null, 0, null, 77, 69, 52, 0, 172, 235, null, null],
|
||||
[0, "구력거", null, 0, null, 80, 68, 56, 0, 152, 193, null, null],
|
||||
[0, "강단", null, 0, null, 65, 83, 48, 0, 168, 234, null, null],
|
||||
[0, "황란", null, 0, null, 63, 75, 44, 0, 200, 264, null, null],
|
||||
[0, "호주천", null, 0, null, 70, 71, 40, 0, 169, 230, null, null],
|
||||
[0, "종리목", null, 0, null, 80, 72, 73, 0, 214, 269, null, null],
|
||||
[0, "제갈교", null, 0, null, 62, 27, 75, 0, 204, 228, null, null],
|
||||
[0, "답돈", null, 0, null, 84, 82, 61, 0, 158, 207, null, null],
|
||||
[0, "반임", null, 0, null, 66, 79, 38, 0, 168, 225, null, null],
|
||||
[0, "보도근", null, 0, null, 64, 73, 50, 0, 170, 233, null, null],
|
||||
[0, "미당대왕", null, 0, null, 59, 69, 21, 0, 202, 260, null, null],
|
||||
[0, "유표(흉노)", null, 0, null, 73, 74, 48, 0, 173, 252, null, null],
|
||||
[0, "루반", null, 0, null, 65, 76, 39, 0, 178, 207, null, null],
|
||||
[0, "손관", null, 0, null, 72, 78, 51, 0, 166, 217, null, null],
|
||||
[0, "곽석", null, 0, null, 59, 71, 48, 0, 158, 187, null, null],
|
||||
[0, "주조", null, 0, null, 63, 68, 26, 0, 152, 187, null, null],
|
||||
[0, "장거", null, 0, null, 79, 75, 56, 0, 150, 188, null, null],
|
||||
[0, "장순", null, 0, null, 73, 81, 60, 0, 152, 188, null, null],
|
||||
[0, "해니", null, 0, null, 62, 78, 35, 0, 191, 233, null, null],
|
||||
[0, "토안", null, 0, null, 53, 82, 30, 0, 191, 233, null, null],
|
||||
[0, "유주", null, 0, null, 67, 78, 42, 0, 172, 233, null, null],
|
||||
[0, "비잔", null, 0, null, 60, 71, 51, 0, 181, 216, null, null],
|
||||
[0, "월길", null, 0, null, 81, 88, 26, 0, 190, 227, null, null],
|
||||
[0, "아단", null, 0, null, 72, 65, 78, 0, 186, 259, null, null],
|
||||
[0, "철리길", null, 0, null, 77, 70, 52, 0, 179, 259, null, null],
|
||||
[0, "주삭", null, 0, null, 54, 60, 50, 0, 174, 253, null, null],
|
||||
[0, "왕이", null, 0, null, 87, 59, 77, 0, 171, 230, null, null],
|
||||
[0, "왕원희", null, 0, null, 53, 37, 73, 0, 217, 268, null, null],
|
||||
[0, "곽씨", null, 0, null, 42, 4, 55, 0, 184, 235, null, null],
|
||||
[0, "하후씨", null, 0, null, 34, 13, 42, 0, 188, 243, null, null],
|
||||
[0, "하후령녀", null, 0, null, 22, 33, 60, 0, 207, 257, null, null],
|
||||
[0, "하씨", null, 0, null, 51, 14, 67, 0, 150, 189, null, null],
|
||||
[0, "관은병", null, 0, null, 87, 84, 75, 0, 204, 271, null, null],
|
||||
[0, "감씨", null, 8, "서주", 14, 10, 44, 1, 175, 210, null, null],
|
||||
[0, "황월영", null, 0, null, 62, 33, 90, 0, 186, 235, null, null],
|
||||
[0, "채염", null, 0, null, 53, 14, 80, 0, 168, 224, null, null],
|
||||
[0, "채씨", null, 13, "양양", 38, 7, 70, 1, 159, 212, null, null],
|
||||
[0, "소교", null, 0, null, 40, 3, 70, 0, 178, 212, null, null],
|
||||
[0, "서씨", null, 0, null, 48, 16, 80, 0, 183, 232, null, null],
|
||||
[0, "신헌영", null, 0, null, 48, 28, 84, 0, 191, 269, null, null],
|
||||
[0, "견씨", null, 0, null, 42, 18, 74, 0, 182, 221, null, null],
|
||||
[0, "추씨", null, 1, "홍농", 4, 9, 66, 1, 165, 200, null, null],
|
||||
[0, "조절", null, 0, null, 32, 33, 79, 0, 190, 260, null, null],
|
||||
[0, "손씨", null, 0, null, 32, 14, 55, 0, 188, 250, null, null],
|
||||
[0, "손상향", null, 0, null, 77, 82, 55, 0, 193, 222, null, null],
|
||||
[0, "손노육", null, 0, null, 52, 10, 66, 0, 204, 253, null, null],
|
||||
[0, "손노반", null, 0, null, 17, 15, 80, 0, 202, 258, null, null],
|
||||
[0, "대교", null, 0, null, 33, 30, 66, 0, 177, 215, null, null],
|
||||
[0, "장씨", null, 0, null, 55, 35, 66, 0, 207, 237, null, null],
|
||||
[0, "장춘화", null, 0, null, 66, 29, 77, 0, 179, 234, null, null],
|
||||
[0, "초선", null, 0, null, 42, 18, 81, 0, 176, 230, null, null],
|
||||
[0, "번씨", null, 0, null, 6, 12, 50, 0, 183, 225, null, null],
|
||||
[0, "미씨", null, 0, null, 13, 6, 72, 0, 176, 208, null, null],
|
||||
[0, "변씨", null, 0, null, 30, 23, 55, 0, 160, 230, null, null],
|
||||
[0, "보련사", null, 0, null, 45, 30, 77, 0, 198, 248, null, null],
|
||||
[0, "유씨", null, 17, "업", 8, 8, 44, 1, 164, 215, null, null],
|
||||
[0, "화만", null, 0, null, 85, 92, 40, 0, 191, 233, null, null],
|
||||
[0, "포삼낭", null, 0, null, 87, 83, 36, 0, 191, 240, null, null],
|
||||
[0, "성렴", null, 3, "복양", 66, 72, 43, 1, 168, 199, null, null],
|
||||
[0, "위월", null, 3, "복양", 68, 76, 39, 1, 170, 199, null, null],
|
||||
[0, "진의록", null, 3, "복양", 46, 43, 17, 1, 168, 199, null, null],
|
||||
[0, "채옹", null, 0, null, 27, 16, 81, 0, 133, 192, null, null],
|
||||
[0, "지양군", null, 0, null, 13, 8, 55, 0, 103, 192, null, null],
|
||||
[0, "위소", null, 0, null, 21, 23, 65, 0, 204, 273, null, null],
|
||||
[0, "위강", null, 0, null, 58, 35, 56, 0, 168, 213, null, null],
|
||||
[0, "원요", null, 0, null, 28, 41, 50, 0, 177, 206, null, null],
|
||||
[0, "왕기2", null, 0, null, 69, 67, 61, 0, 217, 281, null, null],
|
||||
[0, "원환", null, 0, null, 30, 17, 72, 0, 165, 225, null, null],
|
||||
[0, "우전", null, 0, null, 64, 76, 40, 0, 204, 258, null, null],
|
||||
[0, "왕광", null, 0, null, 33, 65, 55, 0, 150, 190, null, null],
|
||||
[0, "왕업", null, 0, null, 33, 6, 66, 0, 220, 280, null, null],
|
||||
[0, "왕융", null, 0, null, 40, 57, 68, 0, 234, 305, null, null],
|
||||
[0, "왕충", null, 0, null, 46, 39, 57, 0, 152, 214, null, null],
|
||||
[0, "위풍", null, 0, null, 52, 23, 72, 0, 182, 219, null, null],
|
||||
[0, "구건", null, 0, null, 39, 52, 65, 0, 239, 272, null, null],
|
||||
[0, "구본", null, 0, null, 50, 41, 67, 0, 232, 269, null, null],
|
||||
[0, "동백", null, 0, null, 12, 7, 52, 0, 173, 192, null, null],
|
||||
[0, "마운", null, 0, null, 78, 88, 53, 0, 183, 225, null, null],
|
||||
[0, "사섭", null, 18, "교지", 53, 31, 76, 12, 137, 226, null, null],
|
||||
[0, "사지", null, 18, "교지", 58, 51, 52, 1, 163, 227, null, null],
|
||||
[0, "사휘", null, 18, "남해", 69, 64, 44, 1, 165, 227, null, null],
|
||||
[0, "사일", null, 18, "남해", 48, 36, 67, 1, 153, 230, null, null],
|
||||
[0, "사광", null, 18, "교지", 58, 45, 64, 1, 175, 235, null, null],
|
||||
[0, "후음", null, 0, null, 76, 72, 45, 0, 184, 219, null, null],
|
||||
[0, "공주", null, 0, null, 26, 16, 68, 0, 151, 191, null, null],
|
||||
[0, "고담륭", null, 0, null, 35, 24, 76, 0, 190, 251, null, null],
|
||||
[0, "국연", null, 0, null, 50, 19, 77, 0, 160, 219, null, null],
|
||||
[0, "오언", null, 0, null, 74, 72, 77, 0, 235, 297, null, null],
|
||||
[0, "오강", null, 0, null, 48, 38, 65, 0, 216, 275, null, null],
|
||||
[0, "최염", null, 0, null, 18, 55, 77, 0, 162, 216, null, null],
|
||||
[0, "좌혁", null, 0, null, 62, 66, 51, 0, 232, 280, null, null],
|
||||
[0, "차주", null, 0, null, 49, 49, 40, 0, 164, 199, null, null],
|
||||
[0, "주태", null, 0, null, 71, 64, 71, 0, 207, 261, null, null],
|
||||
[0, "북궁백옥", null, 0, null, 66, 72, 49, 0, 146, 187, null, null],
|
||||
[0, "변장", null, 0, null, 69, 56, 47, 0, 133, 187, null, null],
|
||||
[0, "순의", null, 0, null, 16, 13, 72, 0, 207, 274, null, null],
|
||||
[0, "서진순욱", null, 0, null, 8, 19, 77, 0, 225, 289, null, null],
|
||||
[0, "사마준", null, 0, null, 85, 82, 80, 0, 232, 286, null, null],
|
||||
[0, "종육", null, 0, null, 32, 12, 71, 0, 223, 263, null, null],
|
||||
[0, "초이", null, 0, null, 62, 69, 63, 0, 219, 266, null, null],
|
||||
[0, "제갈정", null, 0, null, 58, 63, 66, 0, 246, 300, null, null],
|
||||
[0, "보협", null, 0, null, 62, 49, 53, 0, 216, 264, null, null],
|
||||
[0, "복양흥", null, 0, null, 32, 70, 65, 0, 224, 264, null, null],
|
||||
[0, "보천", null, 0, null, 71, 60, 71, 0, 222, 272, null, null],
|
||||
[0, "유대연주", null, 0, null, 53, 32, 55, 0, 148, 192, null, null],
|
||||
[0, "유대", null, 2, "진류", 63, 58, 30, 1, 155, 202, null, null],
|
||||
[0, "유승", null, 0, null, 45, 65, 33, 0, 215, 258, null, null],
|
||||
[0, "여영기", null, 0, null, 79, 88, 37, 0, 182, 198, null, null],
|
||||
[0, "독발수기능", null, 0, null, 89, 93, 77, 0, 232, 279, null, null],
|
||||
[0, "진수", null, 0, null, 12, 22, 68, 0, 233, 297, null, null],
|
||||
[0, "마륭", null, 0, null, 84, 75, 76, 0, 231, 296, null, null],
|
||||
[0, "도황", null, 0, null, 76, 68, 72, 0, 232, 290, null, null],
|
||||
[0, "진온", null, 0, null, 60, 38, 48, 0, 167, 192, null, null],
|
||||
[0, "교모", null, 0, null, 54, 45, 69, 0, 137, 190, null, null],
|
||||
[0, "교국로", null, 0, null, 38, 17, 55, 0, 150, 220, null, null],
|
||||
[0, "포신", null, 0, null, 77, 67, 81, 0, 152, 192, null, null],
|
||||
[0, "포훈", null, 0, null, 41, 35, 75, 0, 180, 226, null, null],
|
||||
[0, "포도", null, 0, null, 57, 70, 43, 0, 159, 190, null, null],
|
||||
[0, "위자", null, 0, null, 58, 38, 62, 0, 150, 190, null, null],
|
||||
[0, "유화", null, 17, "남피", 21, 16, 59, 1, 165, 216, null, null],
|
||||
[0, "이이자", null, 4, "북평", 32, 18, 48, 1, 157, 199, null, null],
|
||||
[0, "선우보", null, 0, null, 74, 69, 65, 0, 161, 229, null, null],
|
||||
[0, "원유", null, 0, null, 57, 39, 73, 0, 150, 193, null, null],
|
||||
[0, "사마휘", null, 0, null, 21, 8, 92, 0, 173, 208, "은둔", null, "좋지~좋아"],
|
||||
[0, "화타", null, 0, null, 8, 7, 68, 0, 173, 207, "은둔", "의술"],
|
||||
[0, "관로", null, 0, null, 13, 9, 72, 0, 200, 233, "은둔", null],
|
||||
[0, "예형", null, 0, null, 13, 4, 90, 0, 173, 199, null, null],
|
||||
[0, "원혁", null, 7, "수춘", 18, 18, 18, 1, 180, 246, null, null],
|
||||
[0, "헌제", null, 1, "장안", 23, 17, 66, 1, 184, 234, null, null, "한 황실은 이대로 끝나는 것인가…"],
|
||||
[0, "황완", null, 0, null, 40, 34, 71, 0, 141, 192, null, null],
|
||||
[0, "마일제", null, 0, "수춘", 43, 25, 70, 0, 130, 194, null, null],
|
||||
[0, "유총", null, 22, "여남", 82, 45, 60, 12, 151, 197, null, null],
|
||||
[0, "낙준", null, 22, "여남", 51, 37, 66, 11, 152, 197, null, null],
|
||||
[0, "낙통", null, 0, null, 76, 57, 73, 0, 193, 228, null, null],
|
||||
[0, "허소", null, 9, "건업", 46, 11, 82, 11, 150, 196, null, null, "태사자는 배신의 상이요! 쓰면 배신을 할것이오"],
|
||||
[0, "허사", null, 3, "복양", 24, 20, 60, 1, 163, 199, null, null],
|
||||
[0, "장초", null, 3, "복양", 61, 32, 60, 1, 168, 196, null, null],
|
||||
[0, "양표", null, 1, "장안", 24, 20, 60, 1, 142, 225, null, null],
|
||||
[0, "왕필", null, 2, "진류", 53, 44, 59, 1, 170, 228, null, null],
|
||||
[0, "사환", null, 2, "진류", 70, 71, 45, 1, 173, 209, null, null],
|
||||
[0, "정비", null, 2, "진류", 34, 30, 57, 1, 158, 214, null, null],
|
||||
[0, "두씨", null, 3, "복양", 26, 41, 52, 1, 172, 236, null, null],
|
||||
[0, "누규", null, 13, "양양", 58, 40, 87, 1, 143, 212, null, null],
|
||||
[0, "단외", null, 1, "장안", 55, 48, 55, 1, 148, 209, null, null],
|
||||
[0, "원사", null, 7, "수춘", 65, 43, 78, 1, 159, 210, null, null],
|
||||
[0, "사의관", null, 7, "수춘", 22, 12, 67, 1, 168, 199, null, null],
|
||||
[0, "송건", null, 0, null, 66, 77, 40, 0, 164, 214, null, null],
|
||||
[0, "곽원", null, 17, "업", 66, 73, 36, 1, 162, 205, null, null],
|
||||
[0, "소유", null, 17, "업", 51, 59, 48, 1, 167, 210, null, null],
|
||||
[0, "주한", null, 17, "남피", 49, 51, 26, 1, 162, 204, null, null],
|
||||
[0, "뇌서", null, 0, null, 68, 65, 34, 0, 170, 212, null, null],
|
||||
[0, "장홍", null, 3, "복양", 72, 81, 65, 1, 164, 196, null, null],
|
||||
[0, "유위대", null, 4, "북평", 53, 44, 62, 1, 152, 199, null, null],
|
||||
[0, "문칙", null, 4, "계", 59, 53, 32, 1, 164, 199, null, null],
|
||||
[0, "습진", null, 0, null, 64, 68, 51, 0, 189, 221, null, null],
|
||||
[0, "습정", null, 0, null, 49, 29, 70, 0, 181, 229, null, null],
|
||||
[0, "이리", null, 1, "장안", 65, 72, 37, 1, 171, 198, null, null]
|
||||
],
|
||||
"general_ex":[
|
||||
],
|
||||
"cities":[
|
||||
|
||||
],
|
||||
"history":[
|
||||
"<C>●</>195년 1월:<L><b>【역사모드3-2】</b>군욱축록</>",
|
||||
"<C>●</>195년 1월:<L><b>【이벤트】</b></>천하는 분열하고 군웅할거가 도래하다"
|
||||
],
|
||||
"initialEvents":[
|
||||
[
|
||||
true,
|
||||
["ChangeCity", "free", {
|
||||
"pop":"70%",
|
||||
"agri":"70%",
|
||||
"comm":"70%",
|
||||
"secu":"70%",
|
||||
"trust":80
|
||||
}],
|
||||
["ChangeCity", "occupied", {
|
||||
"pop":"70%",
|
||||
"agri":"70%",
|
||||
"comm":"70%",
|
||||
"secu":"70%",
|
||||
"trust":80,
|
||||
"def":"70%",
|
||||
"wall":"70%"
|
||||
}]
|
||||
]
|
||||
],
|
||||
"events":[
|
||||
[
|
||||
"destroy_nation", 1000,
|
||||
["and",
|
||||
["Date", ">=", 195, 1],
|
||||
["RemainNation", "==", 1]
|
||||
],
|
||||
["BlockScoutAction"],
|
||||
["DeleteEvent"]
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,764 @@
|
||||
{
|
||||
"title":"【역사모드4-2】 황제 원술",
|
||||
"startYear":194,
|
||||
"life":1,
|
||||
"fiction":0,
|
||||
"const":{
|
||||
"defaultMaxGeneral":600
|
||||
},
|
||||
"nation":[
|
||||
["이각", "#A9A9A9", 5000, 5000, "바람앞의 등불 이각", 1000, "도적", 5, ["장안", "함곡"]],
|
||||
["조조", "#0000FF", 10000, 40000, "승천하는 조조", 1000, "병가", 3, ["진류", "정도", "복양", "초", "낙양", "사곡", "허창", "사수", "호로"]],
|
||||
["여포", "#A9A9A9", 10000, 10000, "서주를 뺏은 여포", 1000, "병가", 1, ["서주", "하비", "광릉"]],
|
||||
["공손찬", "#FF00FF", 10000, 10000, "히키코모리 공손찬", 1000, "병가", 3, ["북평", "역경"]],
|
||||
["장수", "#7B68EE", 10000, 10000, "동탁의 잔당 장수", 1000, "병가", 3, ["완"]],
|
||||
["유장", "#483D8B", 10000, 30000, "익주목 유장", 1000, "유가", 4, ["성도", "자동", "면죽", "강주"]],
|
||||
["중", "#FFC0CB", 15000, 40000, "중나라 황제 원술", 1000, "병가", 7, ["수춘", "여강", "합비"]],
|
||||
["유비", "#008000", 10000, 10000, "예주자사 좌장군 유비", 1000, "유가", 3, ["패"]],
|
||||
["마등", "#808000", 10000, 10000, "서량태수 마등", 1000, "병가", 3, ["서량"]],
|
||||
["공손도", "#A0522D", 10000, 10000, "요동의 왕 공손도", 1000, "덕가", 1, ["안평"]],
|
||||
["엄백호", "#800080", 10000, 10000, "엄백호의 잔당세력", 1000, "도적", 1, ["산월"]],
|
||||
["유표", "#E0FFFF", 10000, 10000, "형주자사 유표", 1000, "유가", 4, ["양양", "장판", "강하"]],
|
||||
["장로", "#20B2AA", 10000, 10000, "오두미도국 장로", 1000, "오두미도", 2, ["한중", "양평"]],
|
||||
["장연", "#87CEEB", 10000, 10000, "흑산적 장연", 1000, "유가", 2, ["진양"]],
|
||||
["화흠", "#6495ED", 10000, 10000, "예장태수 화흠", 1000, "유가", 1, ["시상"]],
|
||||
["원소", "#FFFF00", 10000, 10000, "대장군 원소", 1000, "병가", 5, ["남피", "업", "계교", "평원", "북해"]],
|
||||
["사섭", "#800000", 10000, 10000, "교주자사 사섭", 1000, "유가", 3, ["교지", "남해"]],
|
||||
["한수", "#FFFFFF", 10000, 10000, "서량의 모반자 한수", 1000, "병가", 1, ["천수"]],
|
||||
["손책", "#FF0000", 10000, 10000, "강동을 제패한 손책", 1000, "병가", 2, ["건업", "오", "회계"]],
|
||||
["유벽", "#FFD700", 10000, 10000, "여남 황건적", 1000, "도적", 1, ["여남"]]
|
||||
],
|
||||
"diplomacy":[
|
||||
[7, 2, 1, 36],
|
||||
[7, 8, 1, 36],
|
||||
[7, 19, 1, 36],
|
||||
[3, 8, 7, 48],
|
||||
[2, 8, 7, 48],
|
||||
[16, 2, 7, 48],
|
||||
[13, 6, 1, 36]
|
||||
],
|
||||
"general":[
|
||||
[0, "아회남", null, 0, null, 65, 74, 26, 0, 190, 225, null, null],
|
||||
[0, "이적", null, 0, null, 29, 24, 84, 0, 162, 273, null, null],
|
||||
[0, "윤상", null, 0, null, 51, 44, 62, 0, 194, 260, null, null],
|
||||
[0, "윤묵", null, 0, null, 26, 15, 66, 0, 183, 239, null, null],
|
||||
[0, "우금1", null, 2, "진류", 83, 78, 74, 1, 159, 221, null, null],
|
||||
[0, "위관", null, 0, null, 69, 46, 79, 0, 220, 291, null, null],
|
||||
[0, "원윤", null, 7, "수춘", 15, 14, 39, 1, 150, 199, null, null],
|
||||
[0, "원희", null, 16, "업", 66, 51, 63, 1, 176, 207, null, null],
|
||||
[0, "염행", null, 18, "천수", 76, 84, 66, 1, 159, 215, null, null],
|
||||
[0, "염유", null, 0, null, 71, 57, 71, 0, 168, 227, null, null],
|
||||
[0, "원술", null, 7, "수춘", 44, 65, 65, 12, 155, 199, null, null, "이몸은 이제부터 중의 황제다!"],
|
||||
[0, "원상", null, 0, null, 63, 75, 42, 0, 179, 207, null, null],
|
||||
[0, "원소", null, 16, "업", 81, 69, 70, 12, 154, 202, null, null, "사세 삼공의 이 원소가 천하를 지배하겠다!"],
|
||||
[0, "염상", null, 7, "수춘", 30, 27, 71, 11, 158, 199, null, null],
|
||||
[0, "원담", null, 16, "북해", 60, 69, 30, 1, 173, 205, null, null],
|
||||
[0, "염포", null, 13, "한중", 29, 25, 82, 11, 167, 231, null, null],
|
||||
[0, "왕위", null, 12, "양양", 60, 70, 62, 1, 163, 222, null, null],
|
||||
[0, "왕윤", null, 0, null, 26, 6, 68, 0, 137, 192, null, null],
|
||||
[0, "왕항", null, 0, null, 76, 60, 61, 0, 184, 254, null, null],
|
||||
[0, "왕찬", null, 12, "양양", 15, 3, 79, 1, 177, 217, null, null],
|
||||
[0, "왕수", null, 16, "북해", 54, 27, 77, 1, 168, 218, null, null],
|
||||
[0, "왕숙", null, 0, null, 36, 22, 76, 0, 195, 256, null, null],
|
||||
[0, "왕준", null, 0, null, 81, 73, 77, 0, 206, 286, null, null],
|
||||
[0, "왕쌍", null, 0, null, 67, 88, 19, 0, 195, 228, null, null],
|
||||
[0, "왕창", null, 0, null, 74, 64, 79, 0, 188, 259, null, null],
|
||||
[0, "왕평", null, 0, null, 83, 79, 76, 0, 192, 248, null, null],
|
||||
[0, "왕보", null, 0, null, 62, 41, 79, 0, 171, 219, null, null],
|
||||
[0, "왕문", null, 16, "남피", 65, 64, 31, 1, 162, 205, null, null],
|
||||
[0, "왕릉", null, 0, null, 74, 64, 71, 0, 172, 251, null, null],
|
||||
[0, "왕루", null, 0, null, 28, 30, 79, 0, 173, 211, null, null],
|
||||
[0, "왕랑", null, 0, null, 47, 35, 79, 0, 167, 228, null, null],
|
||||
[0, "온회", null, 0, null, 62, 36, 74, 0, 178, 228, null, null],
|
||||
[0, "하안", null, 0, null, 15, 30, 72, 0, 190, 249, null, null],
|
||||
[0, "괴월", null, 12, "양양", 47, 27, 82, 1, 157, 214, null, null],
|
||||
[0, "괴량", null, 12, "양양", 68, 33, 88, 11, 155, 204, null, null],
|
||||
[0, "가화", null, 0, null, 49, 65, 41, 0, 176, 222, null, null],
|
||||
[0, "가규", null, 0, null, 78, 61, 84, 0, 177, 231, null, null],
|
||||
[0, "하의", null, 0, null, 56, 69, 36, 0, 161, 196, null, null],
|
||||
[0, "화흠", null, 15, "시상", 33, 33, 82, 12, 157, 231, null, null],
|
||||
[0, "가후", null, 5, "완", 88, 48, 97, 11, 147, 223, null, null],
|
||||
[0, "곽가", null, 2, "진류", 88, 15, 96, 11, 170, 207, null, null],
|
||||
[0, "악환", null, 0, null, 65, 82, 42, 0, 196, 251, null, null],
|
||||
[0, "곽사", null, 1, "장안", 64, 76, 13, 1, 146, 197, null, null],
|
||||
[0, "악취", null, 0, null, 53, 66, 61, 0, 157, 195, null, null],
|
||||
[0, "곽준", null, 0, null, 73, 66, 73, 0, 178, 217, null, null],
|
||||
[0, "학소", null, 0, null, 86, 79, 78, 0, 185, 229, null, null],
|
||||
[0, "악진", null, 2, "진류", 80, 84, 56, 1, 159, 218, null, null],
|
||||
[0, "곽도", null, 16, "업", 52, 50, 82, 1, 157, 205, null, null],
|
||||
[0, "학맹", null, 0, null, 62, 67, 42, 0, 156, 196, null, null],
|
||||
[0, "곽유지", null, 0, null, 28, 9, 69, 0, 190, 259, null, null],
|
||||
[0, "곽익", null, 0, null, 73, 69, 69, 0, 207, 270, null, null],
|
||||
[0, "곽회", null, 0, null, 87, 78, 81, 0, 187, 255, null, null],
|
||||
[0, "하후위", null, 0, null, 71, 73, 49, 0, 204, 253, null, null],
|
||||
[0, "하후연", null, 2, "낙양", 90, 91, 68, 1, 162, 219, null, null],
|
||||
[0, "하후은", null, 2, "복양", 63, 72, 53, 1, 180, 208, null, null],
|
||||
[0, "하후화", null, 0, null, 48, 51, 71, 0, 207, 265, null, null],
|
||||
[0, "하후혜", null, 0, null, 50, 44, 76, 0, 206, 242, null, null],
|
||||
[0, "하후현", null, 0, null, 54, 39, 76, 0, 208, 254, null, null],
|
||||
[0, "하후상", null, 0, null, 79, 72, 75, 0, 181, 225, null, null],
|
||||
[0, "하후덕", null, 0, null, 69, 73, 32, 0, 178, 218, null, null],
|
||||
[0, "하후돈", null, 2, "진류", 92, 90, 63, 1, 156, 220, null, null],
|
||||
[0, "하후패", null, 0, null, 79, 84, 77, 0, 202, 262, null, null],
|
||||
[0, "하후무", null, 0, null, 20, 7, 4, 0, 201, 259, null, null],
|
||||
[0, "가충", null, 0, null, 54, 31, 89, 0, 217, 282, null, null],
|
||||
[0, "하진", null, 0, null, 39, 40, 6, 0, 135, 189, null, null],
|
||||
[0, "화웅", null, 0, null, 88, 92, 61, 0, 155, 190, null, null],
|
||||
[0, "한윤", null, 7, "수춘", 26, 29, 65, 1, 155, 197, null, null],
|
||||
[0, "관우", null, 8, "소패", 97, 97, 79, 1, 162, 219, null, null, "술이 식기 전에 돌아오겠소"],
|
||||
[0, "환계", null, 19, "건업", 25, 26, 68, 1, 156, 221, null, null],
|
||||
[0, "관해", null, 0, null, 70, 82, 10, 0, 160, 193, null, null],
|
||||
[0, "관구검", null, 0, null, 78, 74, 55, 0, 202, 255, null, null],
|
||||
[0, "한현", null, 12, "양양", 22, 33, 62, 1, 163, 220, null, null],
|
||||
[0, "한호", null, 2, "진류", 69, 72, 84, 1, 164, 224, null, null],
|
||||
[0, "관흥", null, 0, null, 76, 86, 67, 0, 199, 234, null, null],
|
||||
[0, "관색", null, 0, null, 74, 88, 56, 0, 200, 239, null, null],
|
||||
[0, "한수", null, 18, "천수", 81, 75, 82, 12, 142, 215, null, null],
|
||||
[0, "한숭", null, 12, "강하", 25, 15, 71, 1, 154, 210, null, null],
|
||||
[0, "관정", null, 4, "북평", 36, 52, 73, 11, 158, 199, null, null],
|
||||
[0, "한섬", null, 7, "수춘", 69, 66, 36, 1, 159, 197, null, null],
|
||||
[0, "감택", null, 0, null, 43, 49, 83, 0, 182, 243, null, null],
|
||||
[0, "한충", null, 0, null, 64, 67, 18, 0, 151, 184, null, null],
|
||||
[0, "한당", null, 19, "건업", 76, 85, 61, 1, 156, 227, null, null],
|
||||
[0, "한덕", null, 0, null, 63, 76, 25, 0, 171, 228, null, null],
|
||||
[0, "감녕", null, 12, "강하", 93, 94, 76, 1, 174, 219, null, null],
|
||||
[0, "한복", null, 0, null, 28, 3, 27, 0, 149, 193, null, null],
|
||||
[0, "관평", null, 0, null, 80, 82, 72, 0, 186, 219, null, null],
|
||||
[0, "간옹", null, 8, "소패", 20, 32, 75, 1, 164, 225, null, null],
|
||||
[0, "안량", null, 16, "업", 88, 94, 21, 1, 160, 200, null, null],
|
||||
[0, "위연", null, 0, null, 85, 92, 72, 0, 175, 234, null, null],
|
||||
[0, "국의", null, 0, null, 82, 78, 51, 0, 146, 197, null, null],
|
||||
[0, "희지재", null, 0, null, 63, 7, 88, 0, 157, 196, null, null],
|
||||
[0, "위속", null, 3, "서주", 67, 78, 31, 1, 156, 200, null, null],
|
||||
[0, "위유", null, 0, null, 46, 29, 74, 0, 151, 193, null, null],
|
||||
[0, "우금2", null, 2, "낙양", 72, 79, 38, 1, 173, 226, null, null],
|
||||
[0, "우보", null, 0, null, 38, 60, 21, 0, 159, 192, null, null],
|
||||
[0, "강유", null, 0, null, 92, 89, 90, 0, 202, 264, null, null],
|
||||
[0, "공지", null, 0, null, 43, 53, 62, 0, 178, 197, null, null],
|
||||
[0, "교유", null, 0, null, 64, 69, 38, 0, 143, 195, null, null],
|
||||
[0, "공도", null, 20, "여남", 59, 70, 23, 1, 164, 200, null, null],
|
||||
[0, "허공", null, 0, null, 44, 66, 64, 0, 155, 200, null, null],
|
||||
[0, "허정", null, 17, "남해", 23, 5, 68, 1, 152, 222, null, null],
|
||||
[0, "허저", null, 2, "낙양", 74, 96, 36, 1, 169, 226, null, null],
|
||||
[0, "허유", null, 16, "업", 36, 21, 80, 1, 155, 204, null, null],
|
||||
[0, "기령", null, 7, "수춘", 78, 83, 51, 1, 155, 199, null, null],
|
||||
[0, "김의", null, 0, null, 32, 38, 65, 0, 177, 218, null, null],
|
||||
[0, "김배삼결", null, 0, null, 64, 76, 19, 0, 192, 225, null, null],
|
||||
[0, "김선", null, 12, "양양", 52, 68, 13, 1, 155, 220, null, null],
|
||||
[0, "우번", null, 19, "회계", 43, 46, 86, 1, 164, 233, null, null],
|
||||
[0, "형도영", null, 0, null, 69, 80, 41, 0, 174, 208, null, null],
|
||||
[0, "엄안", null, 6, "자동", 82, 83, 72, 1, 151, 222, null, null],
|
||||
[0, "곽마", null, 0, null, 68, 71, 40, 0, 239, 281, null, null],
|
||||
[0, "엄준", null, 0, null, 28, 2, 71, 0, 169, 246, null, null],
|
||||
[0, "엄정", null, 0, null, 50, 55, 39, 0, 151, 189, null, null],
|
||||
[0, "엄백호", null, 11, "산월", 67, 70, 23, 12, 142, 198, null, null, "이 몸은 동오의 덕왕 엄백호라고 하오!"],
|
||||
[0, "엄여", null, 0, null, 61, 78, 44, 0, 145, 196, null, null],
|
||||
[0, "오의", null, 0, null, 79, 73, 70, 0, 165, 237, null, null],
|
||||
[0, "황개", null, 19, "건업", 80, 83, 70, 1, 154, 218, null, null],
|
||||
[0, "고간", null, 16, "업", 74, 56, 48, 1, 168, 206, null, null],
|
||||
[0, "황권", null, 6, "성도", 75, 59, 82, 11, 167, 240, null, null],
|
||||
[0, "고유", null, 16, "업", 57, 43, 72, 1, 174, 263, null, null],
|
||||
[0, "고순", null, 3, "하비", 85, 86, 60, 1, 162, 198, null, null],
|
||||
[0, "고상", null, 0, null, 68, 68, 53, 0, 194, 252, null, null],
|
||||
[0, "고승", null, 0, null, 67, 73, 41, 0, 145, 184, null, null],
|
||||
[0, "후성", null, 3, "하비", 74, 75, 68, 1, 158, 199, null, null],
|
||||
[0, "후선", null, 18, "천수", 63, 66, 34, 1, 175, 228, null, null],
|
||||
[0, "황조", null, 12, "강하", 74, 66, 55, 1, 148, 208, null, null],
|
||||
[0, "공손월", null, 0, null, 74, 72, 47, 0, 160, 192, null, null],
|
||||
[0, "공손연", null, 0, null, 65, 70, 60, 0, 205, 238, null, null],
|
||||
[0, "공손공", null, 10, "안평", 39, 17, 69, 1, 174, 240, null, null],
|
||||
[0, "공손강", null, 10, "안평", 72, 66, 65, 1, 172, 211, null, null],
|
||||
[0, "공손찬", null, 4, "북평", 84, 83, 75, 12, 152, 199, null, null],
|
||||
[0, "공손속", null, 4, "북평", 63, 68, 57, 1, 176, 199, null, null],
|
||||
[0, "공손도", null, 10, "안평", 67, 71, 66, 12, 154, 204, null, null],
|
||||
[0, "공손범", null, 4, "북평", 73, 69, 66, 1, 158, 199, null, null],
|
||||
[0, "황충", null, 12, "양양", 90, 93, 65, 1, 154, 220, null, null],
|
||||
[0, "고정", null, 6, "자동", 55, 63, 43, 1, 190, 251, null, null],
|
||||
[0, "고패", null, 0, null, 66, 61, 69, 0, 170, 212, null, null],
|
||||
[0, "황보숭", null, 0, null, 92, 61, 73, 0, 132, 195, null, null],
|
||||
[0, "공융", null, 2, "허창", 30, 5, 73, 1, 153, 208, null, null],
|
||||
[0, "고람", null, 16, "평원", 76, 82, 68, 1, 159, 201, null, null],
|
||||
[0, "오경", null, 0, null, 69, 66, 52, 0, 151, 211, null, null],
|
||||
[0, "호거아", null, 5, "완", 70, 82, 41, 1, 164, 206, null, null],
|
||||
[0, "호진", null, 0, null, 65, 74, 12, 0, 148, 190, null, null],
|
||||
[0, "올돌골", null, 0, null, 79, 86, 1, 0, 186, 225, null, null],
|
||||
[0, "호반", null, 0, null, 55, 55, 59, 0, 179, 233, null, null],
|
||||
[0, "오반", null, 6, "성도", 74, 71, 59, 1, 171, 234, null, null],
|
||||
[0, "고옹", null, 0, null, 43, 18, 80, 0, 168, 243, null, null],
|
||||
[0, "오란", null, 6, "성도", 68, 80, 35, 1, 170, 218, null, null],
|
||||
[0, "채화", null, 12, "장판", 39, 50, 1, 1, 166, 208, null, null],
|
||||
[0, "채중", null, 12, "장판", 40, 54, 2, 1, 168, 208, null, null],
|
||||
[0, "채모", null, 12, "양양", 70, 70, 77, 1, 160, 215, null, null],
|
||||
[0, "착융", null, 0, "건업", 60, 69, 38, 0, 161, 196, null, null],
|
||||
[0, "사마의", null, 0, null, 98, 63, 98, 0, 179, 251, null, null],
|
||||
[0, "사마염", null, 0, null, 69, 59, 77, 0, 238, 290, null, null],
|
||||
[0, "사마사", null, 0, null, 79, 64, 88, 0, 208, 255, null, null],
|
||||
[0, "사마소", null, 0, null, 76, 57, 87, 0, 211, 265, null, null],
|
||||
[0, "사마부", null, 0, null, 70, 37, 77, 0, 180, 272, null, null],
|
||||
[0, "사마랑", null, 0, null, 20, 21, 72, 0, 171, 217, null, null],
|
||||
[0, "사마가", null, 0, null, 66, 84, 25, 0, 167, 222, null, null],
|
||||
[0, "주흔", null, 0, null, 65, 62, 73, 0, 159, 196, null, null],
|
||||
[0, "주앙", null, 0, null, 74, 65, 65, 0, 162, 195, null, null],
|
||||
[0, "주지", null, 0, null, 65, 85, 41, 0, 233, 295, null, null],
|
||||
[0, "주창", null, 0, null, 68, 84, 42, 0, 164, 219, null, null],
|
||||
[0, "주태유평", null, 19, "건업", 84, 91, 48, 1, 171, 225, null, null],
|
||||
[0, "주방", null, 0, null, 52, 41, 80, 0, 200, 240, null, null],
|
||||
[0, "주유", null, 19, "건업", 95, 71, 96, 11, 175, 210, null, null],
|
||||
[0, "주환", null, 0, null, 84, 82, 77, 0, 177, 238, null, null],
|
||||
[0, "주거", null, 0, null, 77, 57, 72, 0, 190, 246, null, null],
|
||||
[0, "축융", null, 0, null, 74, 85, 29, 0, 182, 230, null, null],
|
||||
[0, "주준", null, 0, null, 89, 65, 70, 0, 149, 195, null, null],
|
||||
[0, "주연", null, 0, null, 79, 69, 71, 0, 182, 249, null, null],
|
||||
[0, "주치", null, 0, null, 71, 56, 72, 0, 156, 224, null, null],
|
||||
[0, "주포", null, 0, null, 68, 73, 34, 0, 191, 225, null, null],
|
||||
[0, "주령", null, 2, "진류", 71, 73, 67, 1, 170, 236, null, null],
|
||||
[0, "순욱", null, 2, "진류", 62, 14, 95, 1, 163, 212, null, null],
|
||||
[0, "순우경", null, 16, "남피", 72, 66, 29, 1, 146, 200, null, null],
|
||||
[0, "순심", null, 16, "업", 34, 25, 78, 1, 164, 208, null, null],
|
||||
[0, "순유", null, 2, "진류", 73, 26, 94, 1, 157, 214, null, null],
|
||||
[0, "장완", null, 0, null, 78, 34, 84, 0, 188, 246, null, null],
|
||||
[0, "종회", null, 0, null, 82, 51, 91, 0, 225, 264, null, null],
|
||||
[0, "장간", null, 0, null, 19, 6, 66, 0, 175, 208, null, null],
|
||||
[0, "장의거", null, 16, "업", 71, 58, 59, 1, 159, 205, null, null],
|
||||
[0, "장흠", null, 19, "오", 78, 84, 54, 1, 159, 208, null, null],
|
||||
[0, "장제3", null, 0, null, 48, 43, 85, 0, 188, 249, null, null],
|
||||
[0, "초주", null, 0, null, 16, 3, 67, 0, 201, 270, null, null],
|
||||
[0, "초촉", null, 16, "업", 65, 76, 33, 1, 159, 208, null, null],
|
||||
[0, "상총", null, 0, null, 76, 61, 77, 0, 195, 240, null, null],
|
||||
[0, "종요", null, 2, "낙양", 70, 24, 77, 1, 151, 230, null, null],
|
||||
[0, "상랑", null, 0, null, 28, 18, 74, 0, 167, 247, null, null],
|
||||
[0, "서영", null, 0, null, 80, 76, 62, 0, 147, 192, null, null],
|
||||
[0, "제갈각", null, 0, null, 72, 47, 90, 0, 203, 253, null, null],
|
||||
[0, "제갈근", null, 0, null, 75, 34, 81, 0, 174, 241, null, null],
|
||||
[0, "제갈균", null, 0, null, 29, 25, 52, 0, 185, 252, null, null],
|
||||
[0, "제갈탄", null, 0, null, 72, 72, 76, 0, 206, 258, null, null],
|
||||
[0, "제갈량", null, 0, null, 98, 38, 100, 0, 184, 234, null, null],
|
||||
[0, "서황", null, 2, "낙양", 91, 90, 78, 1, 165, 228, null, null],
|
||||
[0, "서서", null, 0, null, 87, 64, 93, 0, 160, 300, null, null],
|
||||
[0, "서성", null, 0, null, 86, 81, 78, 0, 178, 232, null, null],
|
||||
[0, "서막", null, 0, null, 55, 32, 68, 0, 171, 249, null, null],
|
||||
[0, "신의", null, 0, null, 55, 59, 60, 0, 190, 252, null, null],
|
||||
[0, "신탐", null, 0, null, 56, 62, 54, 0, 188, 254, null, null],
|
||||
[0, "심배", null, 16, "업", 80, 60, 83, 1, 156, 204, null, null],
|
||||
[0, "신비", null, 16, "남피", 39, 24, 79, 1, 171, 240, null, null],
|
||||
[0, "신평", null, 16, "남피", 69, 43, 77, 1, 165, 204, null, null],
|
||||
[0, "진복", null, 0, null, 31, 7, 73, 0, 160, 226, null, null],
|
||||
[0, "진랑", null, 0, null, 58, 70, 35, 0, 192, 234, null, null],
|
||||
[0, "휴고", null, 14, "진양", 63, 71, 38, 1, 151, 199, null, null],
|
||||
[0, "추단", null, 0, null, 61, 64, 34, 0, 144, 193, null, null],
|
||||
[0, "성의", null, 18, "천수", 74, 72, 41, 1, 168, 211, null, null],
|
||||
[0, "성공영", null, 18, "천수", 73, 71, 80, 11, 172, 220, null, null],
|
||||
[0, "설종", null, 0, null, 34, 16, 73, 0, 187, 243, null, null],
|
||||
[0, "선경", null, 4, "북평", 71, 68, 43, 1, 156, 199, null, null],
|
||||
[0, "전종", null, 0, null, 78, 72, 75, 0, 183, 249, null, null],
|
||||
[0, "조예", null, 0, null, 72, 52, 82, 0, 205, 239, null, null],
|
||||
[0, "조휴", null, 2, "진류", 74, 75, 62, 1, 174, 228, null, null],
|
||||
[0, "송헌", null, 3, "하비", 68, 77, 38, 1, 157, 200, null, null],
|
||||
[0, "조앙", null, 2, "진류", 74, 67, 69, 1, 161, 197, null, null],
|
||||
[0, "조홍 자렴", null, 2, "진류", 79, 81, 44, 1, 169, 232, null, null, "좌절감이 사나이를 키우는 것이다!"],
|
||||
[0, "조순", null, 2, "진류", 75, 71, 63, 1, 170, 210, null, null],
|
||||
[0, "조창", null, 0, null, 82, 90, 40, 0, 190, 223, null, null],
|
||||
[0, "조식", null, 0, null, 33, 22, 81, 0, 192, 232, null, null],
|
||||
[0, "조진", null, 0, null, 81, 74, 70, 0, 185, 231, null, null],
|
||||
[0, "조인", null, 2, "진류", 89, 86, 62, 1, 168, 223, null, null],
|
||||
[0, "조성", null, 0, null, 54, 74, 38, 0, 163, 198, null, null],
|
||||
[0, "조조", null, 2, "진류", 99, 72, 91, 12, 151, 220, null, null, "내가 천하 사람들을 저버릴지언정, 천하 사람들이 나를 저버리게 하지는 않을 것이다."],
|
||||
[0, "조충", null, 0, null, 31, 21, 85, 0, 196, 207, null, null],
|
||||
[0, "장패", null, 0, null, 78, 75, 53, 0, 165, 231, null, null],
|
||||
[0, "조비", null, 0, null, 70, 71, 83, 0, 187, 226, null, null],
|
||||
[0, "조표", null, 0, null, 66, 69, 32, 0, 151, 196, null, null],
|
||||
[0, "저수", null, 16, "업", 78, 35, 90, 1, 156, 201, null, null],
|
||||
[0, "소비", null, 0, null, 69, 63, 66, 0, 172, 221, null, null],
|
||||
[0, "조무", null, 0, null, 70, 71, 66, 0, 155, 193, null, null],
|
||||
[0, "손환", null, 0, null, 82, 73, 76, 0, 166, 228, null, null],
|
||||
[0, "손견", null, 0, null, 94, 90, 79, 0, 156, 192, null, null, "내가 옥새를 가지고 있다면 나는 칼과 화살을 맞아 죽을 것이다."],
|
||||
[0, "손건", null, 0, null, 34, 33, 79, 0, 165, 215, null, null],
|
||||
[0, "손권", null, 19, "건업", 79, 67, 80, 1, 182, 252, null, null],
|
||||
[0, "손교", null, 0, null, 77, 72, 67, 0, 181, 219, null, null],
|
||||
[0, "손책", null, 19, "건업", 96, 92, 74, 12, 175, 200, null, null],
|
||||
[0, "손소", null, 0, null, 80, 79, 76, 0, 188, 241, null, null],
|
||||
[0, "손정", null, 19, "오", 66, 53, 73, 1, 160, 206, null, null],
|
||||
[0, "손중", null, 0, null, 62, 71, 36, 0, 154, 184, null, null],
|
||||
[0, "손등", null, 0, null, 62, 37, 79, 0, 209, 241, null, null],
|
||||
[0, "손유", null, 0, null, 77, 70, 68, 0, 177, 215, null, null],
|
||||
[0, "손익", null, 0, null, 67, 78, 27, 0, 184, 204, null, null],
|
||||
[0, "손례", null, 0, null, 78, 81, 67, 0, 180, 250, null, null],
|
||||
[0, "태사자", null, 19, "건업", 90, 93, 69, 1, 166, 206, null, null],
|
||||
[0, "대래동주", null, 0, null, 51, 53, 54, 0, 195, 249, null, null],
|
||||
[0, "대릉", null, 0, null, 70, 75, 39, 0, 199, 258, null, null],
|
||||
[0, "타사대왕", null, 0, null, 62, 59, 70, 0, 186, 225, null, null],
|
||||
[0, "담웅", null, 0, null, 58, 73, 14, 0, 188, 221, null, null],
|
||||
[0, "장윤", null, 12, "양양", 72, 67, 42, 1, 163, 208, null, null],
|
||||
[0, "조운", null, 4, "북평", 96, 96, 77, 1, 168, 229, null, null],
|
||||
[0, "장위", null, 13, "한중", 74, 66, 45, 1, 172, 228, null, null, "쌀 다섯말만 가지고 오시오~ 모두 고쳐드리리다."],
|
||||
[0, "장영", null, 0, null, 75, 74, 41, 0, 154, 196, null, null],
|
||||
[0, "장연", null, 14, "진양", 76, 81, 57, 12, 153, 210, null, null],
|
||||
[0, "장횡", null, 0, null, 59, 70, 23, 0, 178, 211, null, null],
|
||||
[0, "장온", null, 0, null, 32, 10, 72, 0, 193, 231, null, null],
|
||||
[0, "장개", null, 0, null, 34, 66, 8, 0, 155, 194, null, null],
|
||||
[0, "장각", null, 0, null, 87, 25, 86, 0, 140, 184, null, null],
|
||||
[0, "장기", null, 0, null, 74, 35, 76, 0, 170, 223, null, null],
|
||||
[0, "장의", null, 16, "남피", 79, 72, 75, 1, 153, 215, null, null],
|
||||
[0, "장훈", null, 7, "수춘", 72, 68, 41, 1, 156, 206, null, null],
|
||||
[0, "장굉", null, 19, "건업", 24, 22, 86, 1, 153, 212, null, null],
|
||||
[0, "장합", null, 16, "평원", 89, 89, 73, 1, 167, 231, null, null],
|
||||
[0, "조홍", null, 0, null, 66, 72, 28, 0, 156, 184, null, null],
|
||||
[0, "장제1", null, 1, "홍농", 70, 66, 55, 1, 144, 196, null, null],
|
||||
[0, "장수", null, 5, "완", 80, 73, 64, 12, 154, 207, null, null],
|
||||
[0, "장소 자포", null, 19, "건업", 33, 3, 84, 1, 156, 236, null, null],
|
||||
[0, "장송", null, 0, null, 25, 6, 88, 0, 170, 212, null, null],
|
||||
[0, "장임", null, 6, "성도", 84, 84, 78, 1, 169, 214, null, null],
|
||||
[0, "장제", null, 0, null, 67, 43, 80, 0, 236, 280, null, null],
|
||||
[0, "장막", null, 0, null, 52, 53, 71, 0, 155, 195, null, null],
|
||||
[0, "조범", null, 0, null, 28, 21, 32, 0, 168, 218, null, null],
|
||||
[0, "장비", null, 8, "소패", 94, 98, 30, 1, 165, 221, null, null],
|
||||
[0, "장포", null, 0, null, 78, 87, 48, 0, 198, 219, null, null],
|
||||
[0, "장보", null, 0, null, 83, 71, 81, 0, 148, 184, null, null],
|
||||
[0, "장만성", null, 0, null, 75, 81, 45, 0, 143, 184, null, null],
|
||||
[0, "장양", null, 0, null, 72, 70, 66, 0, 151, 195, null, null],
|
||||
[0, "장익", null, 0, null, 75, 74, 76, 0, 188, 264, null, null],
|
||||
[0, "장료", null, 3, "서주", 95, 92, 78, 1, 169, 222, null, null],
|
||||
[0, "장량", null, 0, null, 78, 80, 74, 0, 153, 184, null, null],
|
||||
[0, "조루", null, 0, null, 70, 43, 68, 0, 183, 219, null, null],
|
||||
[0, "장로", null, 13, "한중", 51, 26, 74, 12, 163, 216, null, null],
|
||||
[0, "진횡", null, 0, null, 64, 63, 26, 0, 161, 196, null, null],
|
||||
[0, "진응", null, 0, null, 62, 72, 45, 0, 172, 208, null, null],
|
||||
[0, "진기", null, 7, "수춘", 58, 65, 43, 1, 165, 198, null, null],
|
||||
[0, "진궁", null, 3, "하비", 84, 55, 89, 11, 154, 198, null, null],
|
||||
[0, "진교", null, 0, null, 61, 27, 77, 0, 175, 237, null, null],
|
||||
[0, "진군", null, 2, "진류", 32, 14, 75, 1, 167, 236, null, null],
|
||||
[0, "진규", null, 3, "서주", 30, 4, 82, 1, 132, 206, null, null],
|
||||
[0, "진식", null, 0, null, 70, 71, 25, 0, 191, 230, null, null],
|
||||
[0, "진진", null, 16, "업", 46, 46, 69, 1, 170, 235, null, null],
|
||||
[0, "진태", null, 0, null, 84, 77, 86, 0, 210, 260, null, null],
|
||||
[0, "진등", null, 3, "하비", 79, 64, 81, 1, 169, 208, null, null],
|
||||
[0, "진도", null, 0, null, 76, 71, 67, 0, 171, 237, null, null],
|
||||
[0, "진무", null, 0, null, 76, 87, 43, 0, 176, 215, null, null],
|
||||
[0, "진란", null, 7, "여강", 66, 69, 40, 1, 157, 204, null, null],
|
||||
[0, "진림", null, 16, "남피", 25, 9, 75, 1, 160, 217, null, null],
|
||||
[0, "정욱", null, 2, "진류", 70, 49, 90, 1, 141, 220, null, null],
|
||||
[0, "정원지", null, 0, null, 69, 74, 17, 0, 145, 184, null, null],
|
||||
[0, "정은", null, 9, "서량", 69, 73, 40, 1, 184, 220, null, null],
|
||||
[0, "정원", null, 0, null, 70, 76, 35, 0, 137, 189, null, null],
|
||||
[0, "정보", null, 19, "건업", 84, 79, 79, 1, 151, 216, null, null],
|
||||
[0, "정봉승연", null, 0, null, 81, 80, 75, 0, 190, 271, null, null],
|
||||
[0, "전위", null, 0, null, 72, 95, 35, 0, 160, 197, null, null],
|
||||
[0, "전해", null, 4, "북평", 68, 65, 58, 1, 154, 199, null, null],
|
||||
[0, "전주", null, 0, null, 66, 66, 73, 0, 169, 214, null, null],
|
||||
[0, "전풍", null, 16, "남피", 72, 29, 93, 11, 162, 200, null, null],
|
||||
[0, "전예", null, 4, "역경", 80, 72, 80, 1, 171, 252, null, null],
|
||||
[0, "동윤", null, 0, null, 47, 28, 79, 0, 192, 246, null, null],
|
||||
[0, "동화", null, 0, null, 57, 34, 75, 0, 168, 219, null, null],
|
||||
[0, "등애", null, 0, null, 93, 87, 89, 0, 197, 264, null, null],
|
||||
[0, "도겸", null, 0, null, 51, 27, 64, 0, 132, 194, null, null],
|
||||
[0, "등현", null, 0, null, 61, 73, 45, 0, 188, 248, null, null],
|
||||
[0, "등지", null, 0, null, 73, 52, 81, 0, 178, 251, null, null],
|
||||
[0, "동습", null, 0, null, 72, 85, 50, 0, 169, 215, null, null],
|
||||
[0, "동승", null, 2, "허창", 56, 53, 65, 1, 154, 200, null, null],
|
||||
[0, "동소", null, 0, null, 34, 25, 80, 0, 156, 236, null, null],
|
||||
[0, "동탁", null, 0, null, 85, 87, 73, 0, 139, 192, null, null, "크하하, 천하는 이제 나의 것이다!"],
|
||||
[0, "동도나", null, 0, null, 68, 73, 31, 0, 189, 225, null, null],
|
||||
[0, "동민", null, 0, null, 49, 60, 25, 0, 149, 192, null, null],
|
||||
[0, "등무", null, 0, null, 61, 75, 29, 0, 147, 184, null, null],
|
||||
[0, "두기", null, 0, null, 66, 32, 75, 0, 163, 224, null, null],
|
||||
[0, "두예", null, 0, null, 84, 30, 85, 0, 222, 284, null, null],
|
||||
[0, "배원소", null, 0, null, 49, 65, 25, 0, 169, 200, null, null],
|
||||
[0, "마완", null, 0, null, 71, 74, 16, 0, 170, 211, null, null],
|
||||
[0, "마휴", null, 9, "서량", 68, 73, 48, 1, 178, 212, null, null],
|
||||
[0, "파재", null, 0, null, 74, 74, 55, 0, 145, 184, null, null],
|
||||
[0, "마준", null, 0, null, 38, 52, 31, 0, 196, 230, null, null],
|
||||
[0, "마속", null, 0, null, 61, 67, 87, 0, 190, 228, null, null],
|
||||
[0, "마대", null, 0, null, 79, 85, 62, 0, 183, 246, null, null],
|
||||
[0, "마충", null, 0, null, 78, 72, 68, 0, 187, 249, null, null],
|
||||
[0, "마초", null, 9, "서량", 93, 97, 44, 1, 176, 222, null, null],
|
||||
[0, "마철", null, 9, "서량", 70, 62, 58, 1, 179, 212, null, null],
|
||||
[0, "마등", null, 9, "서량", 82, 80, 52, 12, 149, 212, null, null],
|
||||
[0, "마량", null, 0, null, 53, 23, 88, 0, 187, 222, null, null],
|
||||
[0, "반준", null, 0, null, 61, 18, 83, 0, 174, 239, null, null],
|
||||
[0, "반장", null, 0, null, 76, 80, 74, 0, 177, 234, null, null],
|
||||
[0, "번조", null, 0, null, 66, 73, 31, 0, 149, 192, null, null],
|
||||
[0, "번능", null, 0, null, 69, 61, 36, 0, 158, 195, null, null],
|
||||
[0, "반봉", null, 0, null, 56, 77, 4, 0, 155, 190, null, null],
|
||||
[0, "비의", null, 0, null, 77, 30, 83, 0, 193, 253, null, null],
|
||||
[0, "비연", null, 0, null, 60, 66, 21, 0, 196, 238, null, null],
|
||||
[0, "비시", null, 0, null, 18, 28, 65, 0, 176, 240, null, null],
|
||||
[0, "미축", null, 8, "소패", 33, 29, 78, 1, 165, 220, null, null],
|
||||
[0, "미방", null, 8, "소패", 54, 61, 32, 1, 169, 226, null, null],
|
||||
[0, "비요", null, 0, null, 70, 66, 73, 0, 192, 228, null, null],
|
||||
[0, "무안국", null, 0, null, 70, 83, 34, 0, 156, 193, null, null],
|
||||
[0, "풍습", null, 0, null, 72, 71, 39, 0, 182, 222, null, null],
|
||||
[0, "부사인", null, 0, null, 46, 63, 37, 0, 182, 225, null, null],
|
||||
[0, "부첨", null, 0, null, 73, 85, 76, 0, 216, 263, null, null],
|
||||
[0, "부손", null, 0, null, 29, 13, 70, 0, 162, 230, null, null],
|
||||
[0, "문앙", null, 0, null, 76, 93, 65, 0, 238, 291, null, null],
|
||||
[0, "문흠", null, 0, null, 74, 83, 43, 0, 200, 258, null, null],
|
||||
[0, "문추", null, 16, "남피", 89, 93, 47, 1, 169, 200, null, null],
|
||||
[0, "문빙", null, 0, null, 80, 82, 69, 0, 178, 237, null, null],
|
||||
[0, "변희", null, 0, null, 56, 70, 62, 0, 169, 200, null, null],
|
||||
[0, "망아장", null, 0, null, 45, 79, 3, 0, 191, 225, null, null],
|
||||
[0, "봉기", null, 16, "남피", 43, 21, 84, 1, 153, 202, null, null],
|
||||
[0, "방희", null, 0, null, 60, 38, 69, 0, 152, 192, null, null],
|
||||
[0, "법정", null, 0, null, 82, 47, 94, 0, 176, 220, null, null],
|
||||
[0, "방통", null, 0, null, 85, 34, 97, 0, 178, 213, null, null],
|
||||
[0, "방덕", null, 9, "서량", 89, 94, 75, 11, 170, 219, null, null],
|
||||
[0, "포륭", null, 0, null, 58, 76, 49, 0, 174, 208, null, null],
|
||||
[0, "목순", null, 0, null, 52, 77, 11, 0, 157, 190, null, null],
|
||||
[0, "목록대왕", null, 0, null, 65, 71, 39, 0, 184, 225, null, null],
|
||||
[0, "보즐", null, 0, null, 72, 51, 84, 0, 177, 247, null, null],
|
||||
[0, "만총", null, 0, null, 84, 64, 82, 0, 170, 242, null, null],
|
||||
[0, "모개", null, 0, null, 63, 39, 60, 0, 161, 216, null, null],
|
||||
[0, "맹획", null, 0, null, 87, 87, 42, 0, 186, 245, null, null],
|
||||
[0, "맹달", null, 0, null, 75, 73, 75, 0, 172, 228, null, null],
|
||||
[0, "맹우", null, 0, null, 50, 68, 22, 0, 190, 243, null, null],
|
||||
[0, "유섭", null, 0, null, 62, 79, 24, 0, 158, 190, null, null],
|
||||
[0, "양부2", null, 0, null, 62, 68, 68, 0, 167, 212, null, null],
|
||||
[0, "옹개", null, 0, null, 73, 75, 57, 0, 188, 225, null, null],
|
||||
[0, "양의", null, 0, null, 68, 40, 82, 0, 190, 235, null, null],
|
||||
[0, "양호", null, 0, null, 90, 64, 84, 0, 221, 278, null, null],
|
||||
[0, "양홍", null, 7, "수춘", 33, 15, 77, 1, 152, 199, null, null],
|
||||
[0, "양앙", null, 13, "한중", 68, 72, 38, 1, 172, 215, null, null],
|
||||
[0, "양추1", null, 0, null, 52, 65, 40, 0, 159, 199, null, null],
|
||||
[0, "양수", null, 0, null, 25, 4, 83, 0, 175, 219, null, null],
|
||||
[0, "양추2", null, 0, null, 67, 64, 60, 0, 172, 238, null, null],
|
||||
[0, "양송", null, 0, null, 1, 5, 28, 0, 167, 215, null, null],
|
||||
[0, "양임", null, 13, "한중", 70, 78, 56, 1, 170, 215, null, null],
|
||||
[0, "양조", null, 0, null, 53, 61, 36, 0, 223, 286, null, null],
|
||||
[0, "양백", null, 13, "한중", 44, 45, 19, 1, 171, 214, null, null],
|
||||
[0, "양부1", null, 0, null, 67, 50, 83, 0, 178, 239, null, null],
|
||||
[0, "양봉1", null, 0, null, 66, 65, 31, 0, 153, 197, null, null],
|
||||
[0, "양봉2", null, 0, null, 54, 62, 53, 0, 191, 252, null, null],
|
||||
[0, "뇌동", null, 0, null, 69, 78, 51, 0, 172, 218, null, null],
|
||||
[0, "뇌박", null, 7, "수춘", 62, 70, 36, 1, 157, 206, null, null],
|
||||
[0, "이이", null, 0, null, 63, 77, 19, 0, 187, 222, null, null],
|
||||
[0, "이회", null, 0, null, 79, 65, 79, 0, 175, 231, null, null],
|
||||
[0, "이각", null, 1, "장안", 69, 72, 24, 12, 148, 198, null, null],
|
||||
[0, "육항", null, 0, null, 91, 63, 87, 0, 226, 274, null, null],
|
||||
[0, "육적", null, 0, null, 20, 7, 72, 0, 187, 219, null, null],
|
||||
[0, "육손", null, 0, null, 94, 69, 95, 0, 183, 245, null, null],
|
||||
[0, "이엄", null, 0, null, 83, 84, 76, 0, 172, 234, null, null],
|
||||
[0, "이유", null, 0, null, 61, 26, 93, 0, 150, 192, null, null, "장안으로 천도를 하는것이 어떻겠습니까?"],
|
||||
[0, "이숙", null, 0, null, 46, 69, 61, 0, 156, 192, null, null],
|
||||
[0, "이통", null, 2, "초", 73, 83, 62, 1, 168, 209, null, null],
|
||||
[0, "이전", null, 2, "초", 78, 77, 79, 1, 181, 216, null, null],
|
||||
[0, "이풍", null, 7, "수춘", 69, 74, 50, 1, 158, 199, null, null],
|
||||
[0, "유언", null, 0, null, 53, 38, 80, 0, 132, 194, null, null],
|
||||
[0, "유괴", null, 6, "성도", 71, 73, 67, 1, 165, 214, null, null],
|
||||
[0, "유기", null, 12, "양양", 52, 12, 63, 1, 174, 210, null, null],
|
||||
[0, "유우", null, 0, null, 55, 33, 70, 0, 145, 193, null, null],
|
||||
[0, "유훈", null, 7, "여강", 47, 63, 35, 1, 163, 216, null, null],
|
||||
[0, "유현", null, 0, null, 54, 66, 51, 0, 188, 252, null, null],
|
||||
[0, "유순", null, 0, null, 63, 46, 41, 0, 172, 255, null, null],
|
||||
[0, "유장", null, 6, "성도", 31, 5, 34, 12, 162, 219, null, null],
|
||||
[0, "유심", null, 0, null, 60, 62, 70, 0, 238, 264, null, null],
|
||||
[0, "유선", null, 0, null, 3, 5, 9, 0, 208, 270, null, null],
|
||||
[0, "유종", null, 0, null, 24, 22, 65, 0, 195, 220, null, null],
|
||||
[0, "유도", null, 0, null, 25, 23, 26, 0, 168, 214, null, null],
|
||||
[0, "유파", null, 0, null, 29, 22, 75, 0, 186, 222, null, null],
|
||||
[0, "유반", null, 0, null, 69, 76, 47, 0, 168, 210, null, null],
|
||||
[0, "유비", null, 8, "하비", 81, 77, 80, 12, 161, 223, null, null],
|
||||
[0, "유표1", null, 12, "양양", 48, 31, 72, 12, 142, 208, null, null, "강하 팔준에서 제일가는 이 유표에게로 오시오!"],
|
||||
[0, "유복", null, 0, null, 64, 49, 74, 0, 164, 208, null, null],
|
||||
[0, "유벽", null, 20, "여남", 70, 72, 47, 12, 145, 201, null, null],
|
||||
[0, "유봉", null, 0, null, 75, 79, 44, 0, 188, 220, null, null],
|
||||
[0, "유엽", null, 7, "여강", 56, 32, 92, 1, 176, 235, null, null],
|
||||
[0, "유요", null, 0, null, 62, 67, 46, 0, 156, 198, null, null],
|
||||
[0, "여위황", null, 16, "남피", 58, 63, 29, 1, 159, 200, null, null],
|
||||
[0, "요화", null, 0, null, 73, 76, 69, 0, 168, 264, null, null],
|
||||
[0, "양흥", null, 9, "서량", 61, 65, 19, 1, 169, 211, null, null],
|
||||
[0, "양습", null, 0, null, 73, 40, 74, 0, 168, 230, null, null],
|
||||
[0, "양서", null, 0, null, 56, 62, 66, 0, 198, 260, null, null],
|
||||
[0, "능조", null, 0, null, 75, 81, 42, 0, 168, 203, null, null],
|
||||
[0, "능통", null, 0, null, 77, 89, 60, 0, 189, 217, null, null],
|
||||
[0, "여개", null, 0, null, 53, 30, 67, 0, 194, 227, null, null],
|
||||
[0, "여건", null, 2, "진류", 57, 70, 62, 1, 173, 238, null, null],
|
||||
[0, "여광", null, 16, "북해", 56, 70, 13, 1, 162, 207, null, null],
|
||||
[0, "여상", null, 16, "북해", 54, 71, 12, 1, 164, 207, null, null],
|
||||
[0, "여대", null, 0, null, 75, 71, 70, 0, 161, 256, null, null],
|
||||
[0, "여범", null, 19, "건업", 73, 63, 75, 1, 169, 228, null, null],
|
||||
[0, "여포", null, 3, "하비", 97, 100, 26, 12, 156, 198, null, null, "크하하하 다 부숴 버리겠다!"],
|
||||
[0, "여몽", null, 0, null, 82, 81, 20, 0, 178, 219, null, null],
|
||||
[0, "윤직", null, 0, null, 46, 42, 73, 0, 196, 237, null, null],
|
||||
[0, "냉포", null, 0, null, 71, 82, 61, 0, 176, 214, null, null],
|
||||
[0, "진취", null, 0, null, 56, 65, 48, 0, 174, 208, null, null],
|
||||
[0, "노숙", null, 0, null, 84, 56, 92, 0, 172, 217, null, null],
|
||||
[0, "노식", null, 0, null, 86, 63, 82, 0, 139, 192, null, null],
|
||||
[0, "범강", null, 0, null, 49, 50, 36, 0, 174, 222, null, null],
|
||||
[0, "장달", null, 0, null, 43, 56, 34, 0, 175, 222, null, null],
|
||||
[0, "규람", null, 0, null, 55, 70, 58, 0, 165, 204, null, null],
|
||||
[0, "대원", null, 0, null, 38, 32, 68, 0, 166, 204, null, null],
|
||||
[0, "왕기", null, 0, null, 77, 72, 86, 0, 190, 261, null, null],
|
||||
[0, "왕상", null, 0, null, 23, 19, 68, 0, 180, 268, null, null],
|
||||
[0, "왕돈", null, 0, null, 60, 66, 46, 0, 198, 256, null, null],
|
||||
[0, "악침", null, 0, null, 73, 75, 43, 0, 196, 257, null, null],
|
||||
[0, "하제", null, 19, "회계", 83, 70, 82, 1, 171, 227, null, null],
|
||||
[0, "환범", null, 0, null, 40, 11, 78, 0, 199, 249, null, null],
|
||||
[0, "견초", null, 16, "업", 72, 79, 73, 1, 168, 244, null, null],
|
||||
[0, "고당륭", null, 0, null, 35, 24, 76, 0, 190, 244, null, null],
|
||||
[0, "오찬", null, 0, null, 68, 41, 78, 0, 181, 245, null, null],
|
||||
[0, "호질", null, 0, null, 73, 46, 74, 0, 192, 250, null, null],
|
||||
[0, "오질", null, 0, null, 16, 29, 68, 0, 177, 230, null, null],
|
||||
[0, "호준", null, 0, null, 79, 76, 62, 0, 200, 256, null, null],
|
||||
[0, "고담", null, 0, null, 53, 23, 77, 0, 203, 244, null, null],
|
||||
[0, "최림", null, 0, null, 32, 22, 66, 0, 187, 244, null, null],
|
||||
[0, "사마망", null, 0, null, 78, 58, 83, 0, 205, 271, null, null],
|
||||
[0, "주이", null, 0, null, 70, 73, 61, 0, 201, 257, null, null],
|
||||
[0, "전단", null, 0, null, 67, 64, 39, 0, 204, 261, null, null],
|
||||
[0, "조우", null, 0, null, 50, 43, 37, 0, 199, 278, null, null],
|
||||
[0, "조상", null, 0, null, 39, 33, 30, 0, 207, 249, null, null],
|
||||
[0, "조방", null, 0, null, 20, 14, 27, 0, 232, 274, null, null],
|
||||
[0, "태사향", null, 0, null, 57, 66, 48, 0, 189, 246, null, null],
|
||||
[0, "장휴", null, 0, null, 63, 27, 74, 0, 204, 244, null, null],
|
||||
[0, "장호", null, 0, null, 75, 74, 51, 0, 195, 240, null, null],
|
||||
[0, "조광", null, 0, null, 67, 71, 53, 0, 210, 263, null, null],
|
||||
[0, "장집", null, 0, null, 76, 29, 80, 0, 196, 254, null, null],
|
||||
[0, "장승", null, 0, null, 77, 70, 75, 0, 178, 244, null, null],
|
||||
[0, "장소", null, 0, null, 23, 56, 31, 0, 202, 264, null, null],
|
||||
[0, "조통", null, 0, null, 68, 67, 61, 0, 209, 260, null, null],
|
||||
[0, "진표", null, 0, null, 66, 42, 74, 0, 207, 237, null, null],
|
||||
[0, "정무", null, 0, null, 56, 35, 75, 0, 201, 265, null, null],
|
||||
[0, "정봉", null, 0, null, 66, 66, 53, 0, 198, 266, null, null],
|
||||
[0, "전만", null, 2, "허창", 51, 73, 39, 1, 181, 235, null, null],
|
||||
[0, "마균", null, 0, null, 22, 17, 71, 0, 200, 259, null, null],
|
||||
[0, "방회", null, 0, null, 69, 75, 49, 0, 205, 272, null, null],
|
||||
[0, "서구", null, 0, null, 69, 42, 70, 0, 149, 208, null, null],
|
||||
[0, "육개", null, 0, null, 65, 61, 79, 0, 198, 269, null, null],
|
||||
[0, "유찬", null, 0, null, 78, 81, 64, 0, 172, 255, null, null],
|
||||
[0, "유소", null, 0, null, 68, 53, 74, 0, 195, 264, null, null],
|
||||
[0, "요립", null, 0, null, 32, 15, 76, 0, 181, 250, null, null],
|
||||
[0, "여거", null, 0, null, 78, 69, 71, 0, 196, 256, null, null],
|
||||
[0, "제갈첨", null, 0, null, 70, 57, 76, 0, 227, 263, null, null],
|
||||
[0, "손휴", null, 0, null, 57, 42, 78, 0, 235, 264, null, null],
|
||||
[0, "극정", null, 0, null, 42, 23, 68, 0, 208, 278, null, null],
|
||||
[0, "호분", null, 0, null, 71, 76, 50, 0, 222, 288, null, null],
|
||||
[0, "손량", null, 0, null, 32, 26, 70, 0, 243, 260, null, null],
|
||||
[0, "마막", null, 0, null, 34, 12, 44, 0, 221, 266, null, null],
|
||||
[0, "손호", null, 0, null, 41, 73, 71, 0, 242, 284, null, null],
|
||||
[0, "조모", null, 0, null, 54, 61, 59, 0, 241, 260, null, null],
|
||||
[0, "조환", null, 0, null, 30, 28, 30, 0, 246, 302, null, null],
|
||||
[0, "사마유", null, 0, null, 71, 68, 75, 0, 248, 283, null, null],
|
||||
[0, "제갈상", null, 0, null, 64, 79, 62, 0, 246, 263, null, null],
|
||||
[0, "손화", null, 0, null, 31, 39, 68, 0, 224, 253, null, null],
|
||||
[0, "염우", null, 0, null, 52, 61, 51, 0, 209, 264, null, null],
|
||||
[0, "왕경", null, 0, null, 67, 63, 71, 0, 206, 260, null, null],
|
||||
[0, "왕혼", null, 0, null, 74, 71, 63, 0, 223, 297, null, null],
|
||||
[0, "곽혁", null, 0, null, 33, 29, 70, 0, 187, 225, null, null],
|
||||
[0, "가범", null, 0, null, 60, 62, 73, 0, 202, 237, null, null],
|
||||
[0, "서질", null, 0, null, 68, 83, 24, 0, 207, 253, null, null],
|
||||
[0, "손준", null, 0, null, 64, 70, 65, 0, 219, 256, null, null],
|
||||
[0, "손침", null, 0, null, 58, 55, 65, 0, 231, 258, null, null],
|
||||
[0, "우사", null, 0, null, 61, 34, 74, 0, 217, 273, null, null],
|
||||
[0, "진건", null, 0, null, 72, 71, 65, 0, 214, 292, null, null],
|
||||
[0, "동궐", null, 0, null, 65, 59, 76, 0, 204, 271, null, null],
|
||||
[0, "당자", null, 0, null, 61, 71, 50, 0, 196, 265, null, null],
|
||||
[0, "등충", null, 0, null, 76, 86, 69, 0, 230, 264, null, null],
|
||||
[0, "황숭", null, 0, null, 67, 63, 72, 0, 208, 263, null, null],
|
||||
[0, "화핵", null, 0, null, 20, 24, 71, 0, 217, 278, null, null],
|
||||
[0, "허의", null, 0, null, 51, 74, 15, 0, 213, 263, null, null],
|
||||
[0, "송겸", null, 0, null, 66, 78, 42, 0, 175, 227, null, null],
|
||||
[0, "호제", null, 0, null, 57, 39, 68, 0, 207, 264, null, null],
|
||||
[0, "나헌", null, 0, null, 85, 70, 77, 0, 218, 270, null, null],
|
||||
[0, "아들노숙", null, 0, null, 73, 53, 74, 0, 217, 272, null, null],
|
||||
[0, "구성", null, 0, null, 63, 70, 38, 0, 157, 187, null, null],
|
||||
[0, "어부라", null, 0, null, 72, 68, 21, 0, 150, 195, null, null],
|
||||
[0, "아하소과", null, 0, null, 61, 76, 13, 0, 204, 253, null, null],
|
||||
[0, "가비능", null, 0, null, 77, 69, 52, 0, 172, 235, null, null],
|
||||
[0, "구력거", null, 0, null, 80, 68, 56, 0, 152, 193, null, null],
|
||||
[0, "강단", null, 0, null, 65, 83, 48, 0, 168, 234, null, null],
|
||||
[0, "황란", null, 0, null, 63, 75, 44, 0, 200, 264, null, null],
|
||||
[0, "호주천", null, 0, null, 70, 71, 40, 0, 169, 230, null, null],
|
||||
[0, "종리목", null, 0, null, 80, 72, 73, 0, 214, 269, null, null],
|
||||
[0, "제갈교", null, 0, null, 62, 27, 75, 0, 204, 228, null, null],
|
||||
[0, "답돈", null, 0, null, 84, 82, 61, 0, 158, 207, null, null],
|
||||
[0, "반임", null, 0, null, 66, 79, 38, 0, 168, 225, null, null],
|
||||
[0, "보도근", null, 0, null, 64, 73, 50, 0, 170, 233, null, null],
|
||||
[0, "미당대왕", null, 0, null, 59, 69, 21, 0, 202, 260, null, null],
|
||||
[0, "유표(흉노)", null, 0, null, 73, 74, 48, 0, 173, 252, null, null],
|
||||
[0, "루반", null, 0, null, 65, 76, 39, 0, 178, 207, null, null],
|
||||
[0, "손관", null, 0, null, 72, 78, 51, 0, 166, 217, null, null],
|
||||
[0, "곽석", null, 0, null, 59, 71, 48, 0, 158, 187, null, null],
|
||||
[0, "주조", null, 0, null, 63, 68, 26, 0, 152, 187, null, null],
|
||||
[0, "장거", null, 0, null, 79, 75, 56, 0, 150, 188, null, null],
|
||||
[0, "장순", null, 0, null, 73, 81, 60, 0, 152, 188, null, null],
|
||||
[0, "해니", null, 0, null, 62, 78, 35, 0, 191, 233, null, null],
|
||||
[0, "토안", null, 0, null, 53, 82, 30, 0, 191, 233, null, null],
|
||||
[0, "유주", null, 0, null, 67, 78, 42, 0, 172, 233, null, null],
|
||||
[0, "비잔", null, 0, null, 60, 71, 51, 0, 181, 216, null, null],
|
||||
[0, "월길", null, 0, null, 81, 88, 26, 0, 190, 227, null, null],
|
||||
[0, "아단", null, 0, null, 72, 65, 78, 0, 186, 259, null, null],
|
||||
[0, "철리길", null, 0, null, 77, 70, 52, 0, 179, 259, null, null],
|
||||
[0, "주삭", null, 0, null, 54, 60, 50, 0, 174, 253, null, null],
|
||||
[0, "왕이", null, 0, null, 87, 59, 77, 0, 171, 230, null, null],
|
||||
[0, "왕원희", null, 0, null, 53, 37, 73, 0, 217, 268, null, null],
|
||||
[0, "곽씨", null, 0, null, 42, 4, 55, 0, 184, 235, null, null],
|
||||
[0, "하후씨", null, 0, null, 34, 13, 42, 0, 188, 243, null, null],
|
||||
[0, "하후령녀", null, 0, null, 22, 33, 60, 0, 207, 257, null, null],
|
||||
[0, "하씨", null, 0, null, 51, 14, 67, 0, 150, 189, null, null],
|
||||
[0, "관은병", null, 0, null, 87, 84, 75, 0, 204, 271, null, null],
|
||||
[0, "감씨", null, 8, "소패", 14, 10, 44, 1, 175, 210, null, null],
|
||||
[0, "황월영", null, 0, null, 62, 33, 90, 0, 186, 235, null, null],
|
||||
[0, "채염", null, 0, null, 53, 14, 80, 0, 168, 224, null, null],
|
||||
[0, "채씨", null, 12, "양양", 38, 7, 70, 1, 159, 212, null, null],
|
||||
[0, "소교", null, 0, null, 40, 3, 70, 0, 178, 212, null, null],
|
||||
[0, "서씨", null, 0, null, 48, 16, 80, 0, 183, 232, null, null],
|
||||
[0, "신헌영", null, 0, null, 48, 28, 84, 0, 191, 269, null, null],
|
||||
[0, "견씨", null, 16, "업", 42, 18, 74, 1, 182, 221, null, null],
|
||||
[0, "추씨", null, 5, "완", 4, 9, 66, 1, 165, 200, null, null],
|
||||
[0, "조절", null, 0, null, 32, 33, 79, 0, 190, 260, null, null],
|
||||
[0, "손씨", null, 0, null, 32, 14, 55, 0, 188, 250, null, null],
|
||||
[0, "손상향", null, 0, null, 77, 82, 55, 0, 193, 222, null, null],
|
||||
[0, "손노육", null, 0, null, 52, 10, 66, 0, 204, 253, null, null],
|
||||
[0, "손노반", null, 0, null, 17, 15, 80, 0, 202, 258, null, null],
|
||||
[0, "대교", null, 0, null, 33, 30, 66, 0, 177, 215, null, null],
|
||||
[0, "장씨", null, 0, null, 55, 35, 66, 0, 207, 237, null, null],
|
||||
[0, "장춘화", null, 0, null, 66, 29, 77, 0, 179, 234, null, null],
|
||||
[0, "초선", null, 3, "하비", 42, 18, 81, 1, 176, 230, null, null],
|
||||
[0, "번씨", null, 0, null, 6, 12, 50, 0, 183, 225, null, null],
|
||||
[0, "미씨", null, 8, "소패", 13, 6, 72, 1, 176, 208, null, null],
|
||||
[0, "변씨", null, 2, "허창", 30, 23, 55, 1, 160, 230, null, null],
|
||||
[0, "보련사", null, 0, null, 45, 30, 77, 0, 198, 248, null, null],
|
||||
[0, "유씨", null, 16, "업", 8, 8, 44, 1, 164, 215, null, null],
|
||||
[0, "화만", null, 0, null, 85, 92, 40, 0, 191, 233, null, null],
|
||||
[0, "포삼낭", null, 0, null, 87, 83, 36, 0, 191, 240, null, null],
|
||||
[0, "성렴", null, 3, "서주", 66, 72, 43, 1, 168, 199, null, null],
|
||||
[0, "위월", null, 3, "서주", 68, 76, 39, 1, 170, 199, null, null],
|
||||
[0, "진의록", null, 3, "하비", 46, 43, 17, 1, 168, 199, null, null],
|
||||
[0, "채옹", null, 0, null, 27, 16, 81, 0, 133, 192, null, null],
|
||||
[0, "지양군", null, 0, null, 13, 8, 55, 0, 103, 192, null, null],
|
||||
[0, "위소", null, 0, null, 21, 23, 65, 0, 204, 273, null, null],
|
||||
[0, "위강", null, 0, null, 58, 35, 56, 0, 168, 213, null, null],
|
||||
[0, "원요", null, 0, null, 28, 41, 50, 0, 177, 206, null, null],
|
||||
[0, "왕기2", null, 0, null, 69, 67, 61, 0, 217, 281, null, null],
|
||||
[0, "원환", null, 0, null, 30, 17, 72, 0, 165, 225, null, null],
|
||||
[0, "우전", null, 0, null, 64, 76, 40, 0, 204, 258, null, null],
|
||||
[0, "왕광", null, 0, null, 33, 65, 55, 0, 150, 190, null, null],
|
||||
[0, "왕업", null, 0, null, 33, 6, 66, 0, 220, 280, null, null],
|
||||
[0, "왕융", null, 0, null, 40, 57, 68, 0, 234, 305, null, null],
|
||||
[0, "왕충", null, 2, "진류", 46, 39, 57, 1, 152, 214, null, null],
|
||||
[0, "위풍", null, 0, null, 52, 23, 72, 0, 182, 219, null, null],
|
||||
[0, "구건", null, 0, null, 39, 52, 65, 0, 239, 272, null, null],
|
||||
[0, "구본", null, 0, null, 50, 41, 67, 0, 232, 269, null, null],
|
||||
[0, "동백", null, 0, null, 12, 7, 52, 0, 173, 192, null, null],
|
||||
[0, "마운", null, 0, null, 78, 88, 53, 0, 183, 225, null, null],
|
||||
[0, "사섭", null, 17, "교지", 53, 31, 76, 12, 137, 226, null, null],
|
||||
[0, "사지", null, 17, "교지", 58, 51, 52, 1, 163, 227, null, null],
|
||||
[0, "사휘", null, 17, "남해", 69, 64, 44, 1, 165, 227, null, null],
|
||||
[0, "사일", null, 17, "남해", 48, 36, 67, 1, 153, 230, null, null],
|
||||
[0, "사광", null, 17, "교지", 58, 45, 64, 1, 175, 235, null, null],
|
||||
[0, "후음", null, 0, null, 76, 72, 45, 0, 184, 219, null, null],
|
||||
[0, "공주", null, 0, null, 26, 16, 68, 0, 151, 191, null, null],
|
||||
[0, "고담륭", null, 0, null, 35, 24, 76, 0, 190, 251, null, null],
|
||||
[0, "국연", null, 0, null, 50, 19, 77, 0, 160, 219, null, null],
|
||||
[0, "오언", null, 0, null, 74, 72, 77, 0, 235, 297, null, null],
|
||||
[0, "오강", null, 0, null, 48, 38, 65, 0, 216, 275, null, null],
|
||||
[0, "최염", null, 16, "업", 18, 55, 77, 1, 162, 216, null, null],
|
||||
[0, "좌혁", null, 0, null, 62, 66, 51, 0, 232, 280, null, null],
|
||||
[0, "차주", null, 2, "정도", 49, 49, 40, 1, 164, 199, null, null],
|
||||
[0, "주태", null, 0, null, 71, 64, 71, 0, 207, 261, null, null],
|
||||
[0, "북궁백옥", null, 0, null, 66, 72, 49, 0, 146, 187, null, null],
|
||||
[0, "변장", null, 0, null, 69, 56, 47, 0, 133, 187, null, null],
|
||||
[0, "순의", null, 0, null, 16, 13, 72, 0, 207, 274, null, null],
|
||||
[0, "서진순욱", null, 0, null, 8, 19, 77, 0, 225, 289, null, null],
|
||||
[0, "사마준", null, 0, null, 85, 82, 80, 0, 232, 286, null, null],
|
||||
[0, "종육", null, 0, null, 32, 12, 71, 0, 223, 263, null, null],
|
||||
[0, "초이", null, 0, null, 62, 69, 63, 0, 219, 266, null, null],
|
||||
[0, "제갈정", null, 0, null, 58, 63, 66, 0, 246, 300, null, null],
|
||||
[0, "보협", null, 0, null, 62, 49, 53, 0, 216, 264, null, null],
|
||||
[0, "복양흥", null, 0, null, 32, 70, 65, 0, 224, 264, null, null],
|
||||
[0, "보천", null, 0, null, 71, 60, 71, 0, 222, 272, null, null],
|
||||
[0, "유대연주", null, 0, null, 53, 32, 55, 0, 148, 192, null, null],
|
||||
[0, "유대", null, 2, "진류", 63, 58, 30, 1, 155, 202, null, null],
|
||||
[0, "유승", null, 0, null, 45, 65, 33, 0, 215, 258, null, null],
|
||||
[0, "여영기", null, 3, "하비", 79, 88, 37, 1, 182, 198, null, null],
|
||||
[0, "독발수기능", null, 0, null, 89, 93, 77, 0, 232, 279, null, null],
|
||||
[0, "진수", null, 0, null, 12, 22, 68, 0, 233, 297, null, null],
|
||||
[0, "마륭", null, 0, null, 84, 75, 76, 0, 231, 296, null, null],
|
||||
[0, "도황", null, 0, null, 76, 68, 72, 0, 232, 290, null, null],
|
||||
[0, "진온", null, 0, null, 60, 38, 48, 0, 167, 192, null, null],
|
||||
[0, "교모", null, 0, null, 54, 45, 69, 0, 137, 190, null, null],
|
||||
[0, "교국로", null, 19, "건업", 38, 17, 55, 1, 150, 220, null, null],
|
||||
[0, "포신", null, 0, null, 77, 67, 81, 0, 152, 192, null, null],
|
||||
[0, "포훈", null, 2, "복양", 41, 35, 75, 1, 180, 226, null, null],
|
||||
[0, "포도", null, 0, null, 57, 70, 43, 0, 159, 190, null, null],
|
||||
[0, "위자", null, 0, null, 58, 38, 62, 0, 150, 190, null, null],
|
||||
[0, "유화", null, 16, "남피", 21, 16, 59, 1, 165, 216, null, null],
|
||||
[0, "이이자", null, 4, "북평", 32, 18, 48, 1, 157, 199, null, null],
|
||||
[0, "선우보", null, 0, null, 74, 69, 65, 0, 161, 229, null, null],
|
||||
[0, "원유", null, 0, null, 57, 39, 73, 0, 150, 193, null, null],
|
||||
[0, "사마휘", null, 0, null, 21, 8, 92, 0, 173, 208, "은둔", null, "좋지~좋아"],
|
||||
[0, "화타", null, 0, null, 8, 7, 68, 0, 173, 207, "은둔", "의술"],
|
||||
[0, "관로", null, 0, null, 13, 9, 72, 0, 200, 233, "은둔", null],
|
||||
[0, "예형", null, 0, null, 13, 4, 90, 0, 173, 199, null, null],
|
||||
[0, "원혁", null, 7, "수춘", 52, 18, 18, 1, 180, 246, null, null],
|
||||
[0, "헌제", null, 2, "낙양", 23, 17, 66, 1, 184, 234, null, null, "….."],
|
||||
[0, "황완", null, 0, null, 40, 34, 71, 0, 141, 192, null, null],
|
||||
[0, "마일제", null, 0, null, 43, 25, 70, 0, 130, 194, null, null],
|
||||
[0, "유총", null, 0, null, 82, 45, 60, 0, 151, 197, null, null],
|
||||
[0, "낙준", null, 0, null, 51, 37, 66, 0, 152, 197, null, null],
|
||||
[0, "낙통", null, 0, null, 76, 57, 73, 0, 193, 228, null, null],
|
||||
[0, "허소", null, 0, null, 46, 11, 82, 0, 150, 196, null, null],
|
||||
[0, "허사", null, 3, "하비", 24, 20, 60, 1, 163, 199, null, null],
|
||||
[0, "장초", null, 0, null, 61, 32, 60, 0, 168, 196, null, null],
|
||||
[0, "양표", null, 2, "낙양", 24, 20, 60, 1, 142, 225, null, null],
|
||||
[0, "왕필", null, 2, "낙양", 53, 44, 59, 1, 170, 228, null, null],
|
||||
[0, "사환", null, 2, "진류", 70, 71, 45, 1, 173, 209, null, null],
|
||||
[0, "정비", null, 2, "진류", 34, 30, 57, 1, 158, 214, null, null],
|
||||
[0, "두씨", null, 3, "하비", 26, 41, 52, 1, 172, 236, null, null],
|
||||
[0, "누규", null, 12, "양양", 58, 40, 87, 1, 143, 212, null, null],
|
||||
[0, "단외", null, 1, "장안", 55, 48, 55, 1, 148, 209, null, null],
|
||||
[0, "원사", null, 2, "초", 65, 43, 78, 1, 159, 210, null, null],
|
||||
[0, "사의관", null, 7, "수춘", 22, 12, 67, 1, 168, 199, null, null],
|
||||
[0, "송건", null, 0, null, 66, 77, 40, 0, 164, 214, null, null],
|
||||
[0, "곽원", null, 16, "업", 66, 73, 36, 1, 162, 205, null, null],
|
||||
[0, "소유", null, 16, "업", 51, 59, 48, 1, 167, 210, null, null],
|
||||
[0, "주한", null, 16, "남피", 49, 51, 26, 1, 162, 204, null, null],
|
||||
[0, "뇌서", null, 0, null, 68, 65, 34, 0, 170, 212, null, null],
|
||||
[0, "장홍", null, 3, "복양", 72, 81, 65, 1, 164, 196, null, null],
|
||||
[0, "유위대", null, 4, "북평", 53, 44, 62, 1, 152, 199, null, null],
|
||||
[0, "문칙", null, 4, "계", 59, 53, 32, 1, 164, 199, null, null],
|
||||
[0, "습진", null, 0, null, 64, 68, 51, 0, 189, 221, null, null],
|
||||
[0, "습정", null, 0, null, 49, 29, 70, 0, 181, 229, null, null],
|
||||
[0, "이리", null, 1, "장안", 65, 72, 37, 1, 171, 198, null, null]
|
||||
],
|
||||
"general_ex":[
|
||||
|
||||
],
|
||||
"cities":[
|
||||
|
||||
],
|
||||
"history":[
|
||||
"<C>●</>197년 1월:<L><b>【역사모드4-2】</b>황제 원술</>",
|
||||
"<C>●</>197년 1월:<L><b>【이벤트】</b></>원술이 황제가 되고 반원술 연합이 결성되다"
|
||||
],
|
||||
"initialEvents":[
|
||||
[
|
||||
true,
|
||||
["ChangeCity", "free", {
|
||||
"pop":"70%",
|
||||
"agri":"70%",
|
||||
"comm":"70%",
|
||||
"secu":"70%",
|
||||
"trust":80
|
||||
}],
|
||||
["ChangeCity", "occupied", {
|
||||
"pop":"70%",
|
||||
"agri":"70%",
|
||||
"comm":"70%",
|
||||
"secu":"70%",
|
||||
"trust":80,
|
||||
"def":"70%",
|
||||
"wall":"70%"
|
||||
}]
|
||||
]
|
||||
],
|
||||
"events":[
|
||||
[
|
||||
"destroy_nation", 1000,
|
||||
["and",
|
||||
["Date", ">=", 183, 1],
|
||||
["RemainNation", "==", 1]
|
||||
],
|
||||
["BlockScoutAction"],
|
||||
["DeleteEvent"]
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"title": "【가상모드28】 강서유서 소설 월드",
|
||||
"title": "【가상모드28】 강서유서 월드",
|
||||
"startYear": 180,
|
||||
"history": [
|
||||
"<C>●</>180년 1월:<L><b>【가상모드28】</b>강서유서 소설 월드</>",
|
||||
"<C>●</>180년 1월:<L><b>【이벤트】</b></>강서유서의 소설 캐릭터들이 날뛰는곳"
|
||||
"<C>●</>180년 1월:<L><b>【가상모드28】</b>강서유서 월드</>",
|
||||
"<C>●</>180년 1월:<L><b>【이벤트】</b></>강서유서의 세계 속 캐릭터들이 날뛰는곳"
|
||||
],
|
||||
"stat": {
|
||||
"total": 210,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"title": "【가상모드28-b】 강서유서 소설 월드(비세력)",
|
||||
"title": "【가상모드28-b】 강서유서 월드(비세력)",
|
||||
"startYear": 180,
|
||||
"history": [
|
||||
"<C>●</>180년 1월:<L><b>【가상모드28】</b>강서유서 소설 월드</>",
|
||||
"<C>●</>180년 1월:<L><b>【이벤트】</b></>강서유서의 소설 캐릭터들이 날뛰는곳"
|
||||
"<C>●</>180년 1월:<L><b>【가상모드28】</b>강서유서 월드</>",
|
||||
"<C>●</>180년 1월:<L><b>【이벤트】</b></>강서유서의 세계 속 캐릭터들이 날뛰는곳"
|
||||
],
|
||||
"stat": {
|
||||
"total": 210,
|
||||
@@ -18,10 +18,10 @@
|
||||
"general":[
|
||||
[1, "키타노 유키코", null, 0, null, 95, 31, 95, 0, 160, 300, "대의", "저격", "모든것은 우리들의 꿈을 이루기 위해…"],
|
||||
[1, "키타노 시오리", null, 0, null, 88, 96, 89, 0, 160, 300, "대의", "무쌍", "언니를 지켜보일거야"],
|
||||
[1, "이치가와 아이리", null, 0, null, 87, 73, 94, 0, 160, 300, "대의", null, "유키코를 지킬거야. 내 속죄를 위해"],
|
||||
[1, "이즈카에 에리카", null, 0, null, 77, 93, 72, 0, 160, 300, "의협", "저격"],
|
||||
[1, "이치가와 아이리", null, 0, null, 87, 73, 94, 0, 160, 300, "대의", null, "모두와 함께 세계에 혁명을…."],
|
||||
[1, "이즈카에 에리카", null, 0, null, 77, 93, 72, 0, 160, 300, "의협", "저격", "모두와 함께 그녀를 지키며 혁명을!"],
|
||||
[1, "타에구치 유카리", null, 0, null, 71, 66, 80, 0, 160, 300, "할거", null, "아버지와 어머니를 지키기 위해 미래에서 왓어 난"],
|
||||
[1, "오모타 아미유", null, 0, null, 64, 51, 44, 0, 160, 300, "대의", null],
|
||||
[1, "오모타 아미유", null, 0, null, 64, 51, 44, 0, 160, 300, "유지", null],
|
||||
[1, "소카이 요시나", null, 0, null, 79, 62, 80, 0, 160, 300, "의협", null],
|
||||
[1, "소미타키 코토키", null, 0, null, 86, 79, 55, 0, 160, 300, "재간", null],
|
||||
[1, "타카이시 유키", null, 0, null, 83, 64, 72, 0, 160, 300, "재간", null],
|
||||
@@ -29,11 +29,11 @@
|
||||
[1, "요리시마 사치코", null, 0, null, 51, 81, 70, 0, 160, 300, null, "저격"],
|
||||
[0, "큐어 스플릿", null, 0, null, 80, 90, 56, 0, 160, 300, null, "무쌍"],
|
||||
[0, "큐어 솔크", null, 0, null, 93, 97, 67, 0, 160, 300, null, null, "아버지를 이겨보일거야."],
|
||||
[0, "큐어 샤인", null, 0, null, 89, 75, 99, 0, 160, 300, "패권", "신산", "디지털 월드와 인간세계 모두를 위해"],
|
||||
[0, "큐어 샤인", null, 0, null, 89, 75, 99, 0, 160, 300, "패권", "신산", "아빠를 막겠어. 디지털 월드와 현실세계의 평화를 위해... 내가 선택했으니까"],
|
||||
[0, "큐어 러브", null, 0, null, 77, 86, 54, 0, 160, 300, null, null, "사랑을 추구하는 전사 큐어 러브"],
|
||||
[0, "큐어 프로즌", null, 0, null, 64, 88, 72, 0, 160, 300, null, null],
|
||||
[0, "큐어 바이올렛", null, 0, null, 49, 89, 77, 0, 160, 300, null, "상재"],
|
||||
[0, "큐어 드림", null, 0, null, 97, 99, 71, 0, 160, 300, null, "무쌍"],
|
||||
[0, "큐어 프로즌", null, 0, null, 64, 88, 72, 0, 160, 300, null, null, "얼음처럼 차가운 성격, 큐어 프로즌"],
|
||||
[0, "큐어 바이올렛", null, 0, null, 49, 89, 77, 0, 160, 300, null, "상재", "바이올렛은 조화의 상징 큐어 바이올렛"],
|
||||
[0, "큐어 드림", null, 0, null, 97, 99, 71, 0, 160, 300, null, "무쌍", "희망이 가진 위대한 힘, 큐어 드림!"],
|
||||
[0, "큐어 뮤즈", null, 0, null, 71, 95, 64, 0, 160, 300, null, null],
|
||||
[0, "큐어 플리오네", null, 0, null, 89, 102, 86, 0, 148, 300, null, null],
|
||||
[0, "큐어 리큐브", null, 0, null, 67, 88, 82, 0, 152, 300, null, null],
|
||||
@@ -41,13 +41,13 @@
|
||||
[0, "큐어 뮤직", null, 0, null, 79, 84, 60, 0, 160, 300, null, null],
|
||||
[0, "큐어 포르테", null, 0, null, 72, 77, 55, 0, 160, 300, null, null],
|
||||
[0, "큐어 리스", null, 0, null, 89, 80, 67, 0, 160, 300, null, null],
|
||||
[0, "분비", null, 0, null, 85, 94, 90, 0, 148, 300, null, null],
|
||||
[0, "시비렛타", null, 0, null, 82, 73, 90, 0, 127, 300, null, null],
|
||||
[0, "히토", null, 0, null, 64, 88, 80, 0, 115, 300, null, null],
|
||||
[2, "모요코모 미에카", null, 0, null, 91, 60, 97, 0, 141, 300, "할거", null, "모든것은 분비님이 부활시키려는 그자를 막기 위해서야! 그자는 우리가 죽을뻔한 선택받은 아이들의 적이니까!"],
|
||||
[2, "노자미 미노코", null, 0, null, 80, 77, 80, 0, 143, 300, null, null, "도체 요시미츠님(분비님)은 뭔 생각이신거야…"],
|
||||
[2, "노자미 미에키", null, 0, null, 73, 70, 55, 0, 160, 300, null, null],
|
||||
[999, "리아", null, 0, null, 99, 34, 100, 0, 132, 300, null, "집중", "이 세계를 부디 안정시켜줄 사람이 필요해…."],
|
||||
[0, "분비", null, 0, null, 85, 94, 90, 0, 148, 300, null, null, "세상은 말이지, 강한자가 약한자를 지배하는 법이 당연시 되어 있어"],
|
||||
[0, "시비렛타", null, 0, null, 82, 73, 90, 0, 127, 300, null, null, "싫어….! 이대로 죽고싶진 않단 말이야!"],
|
||||
[0, "히토", null, 0, null, 64, 88, 80, 0, 115, 300, null, null, "이젠 내가 나설 차례인가?"],
|
||||
[2, "모요코모 미에카", null, 0, null, 91, 60, 97, 0, 141, 300, "할거", null, "우리가 이럴수 있는 이유는 디지몬의 독재자 그를 막기 위해서…"],
|
||||
[2, "노자미 미노코", null, 0, null, 80, 77, 80, 0, 143, 300, null, null, "도대체 그분은 무슨 생각을 하는거지?"],
|
||||
[2, "노자미 미에키", null, 0, null, 73, 70, 55, 0, 160, 300, null, null, "우리들은 디지털 월드와 현실세계를 잇는 아이들"],
|
||||
[0, "리아", null, 0, null, 99, 34, 100, 0, 132, 300, null, "집중", "어느새, 두세계의 융합이 코앞으로 다가와 버렸어"],
|
||||
[0, "코구라 아치모토", null, 0, null, 51, 44, 114, 0, 160, 300, null, "발명", "크흐흐…다크 프리큐어여 모습을 드러내라…!"],
|
||||
[0, "다크 프리큐어즈", null, 0, null, 84, 97, 92, 0, 160, 300, null, null, "모든건… 그분의 부활을 위해."],
|
||||
[0, "레톨프", null, 0, null, 87, 88, 64, 0, 160, 300, null, null],
|
||||
@@ -56,7 +56,7 @@
|
||||
[2, "아야토케 나이미", null, 0, null, 76, 44, 88, 0, 151, 300, null, null],
|
||||
[2, "히마즈라 유게키", null, 0, null, 54, 80, 12, 0, 149, 300, null, null],
|
||||
[2, "하루사키 유나", null, 0, null, 77, 44, 91, 0, 150, 300, null, null],
|
||||
[0, "야가미 타이치", null, 0, null, 89, 88, 67, 0, 153, 300, null, null],
|
||||
[0, "야가미 타이치", null, 0, null, 89, 88, 67, 0, 153, 300, null, null, "반드시 돌아올게. 감기, 빨리 나아야해."],
|
||||
[0, "야가미 소라", null, 0, null, 62, 37, 75, 0, 153, 300, null, null],
|
||||
[0, "야가미 유메", null, 0, null, 80, 55, 78, 0, 160, 300, null, null],
|
||||
[0, "이시다 야마토", null, 0, null, 72, 77, 60, 0, 153, 300, null, null],
|
||||
@@ -70,14 +70,15 @@
|
||||
[2, "야카기 츠에루", null, 0, null, 83, 80, 51, 0, 160, 300, null, null, "할머니의 업보… 제가 신 갚아나갈 거에요"],
|
||||
[0, "사이온지 카나타", null, 0, null, 81, 79, 85, 0, 160, 300, null, null, "내가 먼 말할게 좋아해 미유,"],
|
||||
[0, "사이온지 미유", null, 0, null, 78, 64, 82, 0, 160, 300, null, null, "미우… 방금 뭐라고?.....루? 루란 말이야?"],
|
||||
[0, "사이온지 미우", null, 0, null, 85, 91, 33, 0, 160, 300, null, "견고", "크하하… 모두 박살나 버려라, 이딴 세상!"],
|
||||
[0, "사이온지 미우", null, 0, null, 85, 91, 33, 0, 160, 300, null, "견고", "엄마도, 아빠도 우리들도 모두, 모든것은.... 이곳에서 시작됬어"],
|
||||
[0, "오카자키 나기사", null, 0, null, 77, 17, 91, 0, 160, 300, null, null, "경단~ 경단~ 경단~경단 경단 가족~"],
|
||||
[0, "오카자키 토모야", null, 0, null, 79, 82, 51, 0, 160, 300, null, null],
|
||||
[0, "오카자키 우시오", null, 0, null, 67, 77, 68, 0, 160, 300, null, null, "미우를 구하기 위해..! 난 싸울거야 친구니까!"],
|
||||
[0, "오카자키 우시오", null, 0, null, 67, 77, 68, 0, 160, 300, null, null, "이곳은 마을의 마음이니까"],
|
||||
[0, "최산해", null, 0, null, 82, 88, 33, 0, 160, 300, null, null],
|
||||
[0, "모토미야 유지", null, 0, null, 86, 31, 88, 0, 160, 300, null, "상재", "허허…. 이나라가 어찌되련지…"],
|
||||
[0, "유에", null, 0, null, 56, 14, 80, 0, 160, 300, null, null, "백성을 우선시하는게 통치의 근본."],
|
||||
[0, "에이", null, 0, null, 64, 33, 97, 0, 160, 300, null, null, "유에는 소중한 친구이자 주군,"],
|
||||
[0, "견휘", null, 0, null, 85, 44, 92, 0, 160, 300, null, "집중"],
|
||||
[0, "고현", null, 0, null, 52, 37, 81, 0, 160, 300, null, null],
|
||||
[5, "고순", null, 0, null, 88, 86, 66, 0, 160, 300, null, "기병"],
|
||||
[5, "장료", null, 0, null, 85, 92, 78, 0, 160, 300, null, "무쌍"],
|
||||
@@ -96,7 +97,7 @@
|
||||
[3, "유희", null, 0, null, 64, 75, 62, 0, 160, 300, null, null],
|
||||
[3, "나지현", null, 0, null, 64, 33, 71, 0, 160, 300, null, null],
|
||||
[3, "아사쿠라 나오", null, 0, null, 81, 63, 83, 0, 160, 300, null, "저격", "모든건 우리를 싸우게 만들고 서로 죽이게 한 어른들에 한 복수,"],
|
||||
[3, "아오이 타쿠마", null, 0, null, 77, 89, 55, 0, 160, 300, null, null],
|
||||
[3, "아오이 타쿠마", null, 0, null, 77, 89, 55, 0, 160, 300, null, null, "웃기지마!"],
|
||||
[0, "우라베 슈고", null, 0, null, 53, 68, 61, 0, 160, 300, null, null],
|
||||
[0, "카사이 오사무", null, 0, null, 66, 54, 39, 0, 160, 300, null, null],
|
||||
[0, "카케이 쿄코", null, 0, null, 77, 55, 81, 0, 160, 300, null, null],
|
||||
@@ -109,20 +110,20 @@
|
||||
[0, "야기 아야네", null, 0, null, 78, 83, 55, 0, 160, 300, null, "저격"],
|
||||
[0, "야자와 아이", null, 0, null, 67, 89, 57, 0, 160, 300, null, "무쌍"],
|
||||
[0, "유키 카나", null, 0, null, 79, 24, 88, 0, 160, 300, null, "통찰"],
|
||||
[0, "노다 샤토미", null, 0, null, 61, 55, 69, 0, 160, 300, null, null],
|
||||
[0, "노다 샤토미", null, 0, null, 61, 55, 69, 0, 160, 300, null, null, "적어도 내일까진 살수 있을거라 생각했어…."],
|
||||
[0, "사사가와 류헤이", null, 0, null, 66, 58, 66, 0, 160, 300, null, null],
|
||||
[0, "키리야마 카즈오", null, 0, null, 91, 92, 99, 0, 160, 300, "패권", "위압", "뒷면이 나오면…이 게임을 받아들이기로…!"],
|
||||
[0, "이치가와 쥬리", null, 0, null, 34, 32, 91, 0, 160, 300, null, "발명"],
|
||||
[0, "모토무라 아야네", null, 0, null, 77, 67, 55, 0, 163, 300, null, null],
|
||||
[0, "코토히키 카요코", null, 0, null, 78, 33, 78, 0, 160, 300, null, null],
|
||||
[0, "스기무라 히로키", null, 0, null, 67, 90, 45, 0, 160, 300, "유지", null],
|
||||
[0, "코토히키 카요코", null, 0, null, 78, 33, 78, 0, 160, 300, null, null, "나도 널 좋아해 히로키…."],
|
||||
[0, "스기무라 히로키", null, 0, null, 67, 90, 45, 0, 160, 300, "유지", null, "살고싶은 마음은 모두가 다 똑같아, 그런데 왜 자기 혼자만 살려고 하는거지?!"],
|
||||
[0, "민유희", null, 0, null, 89, 55, 81, 0, 163, 300, null, null],
|
||||
[0, "사카키 유코", null, 0, null, 45, 34, 60, 0, 160, 300, null, null],
|
||||
[0, "사카키 유코", null, 0, null, 45, 34, 60, 0, 160, 300, null, null, "그러니까 안돼…. 나혼자만 살아남아선 안되는거야…."],
|
||||
[0, "사쿠라이 사키", null, 0, null, 78, 77, 66, 0, 160, 300, null, "저격"],
|
||||
[0, "마키", null, 0, null, 72, 74, 56, 0, 160, 300, null, null],
|
||||
[0, "마이", null, 0, null, 70, 70, 60, 0, 160, 300, null, null],
|
||||
[0, "소마 미츠코", null, 0, null, 93, 66, 90, 0, 160, 300, null, null, "나는 뺏는쪽에 서서 살아가는 거잖아…?"],
|
||||
[0, "타카구치유이치로", null, 0, null, 77, 54, 79, 0, 160, 300, null, null],
|
||||
[0, "타카구치유이치로", null, 0, null, 77, 54, 79, 0, 160, 300, null, null, "미츠코는 다들 얘기하는 것만큼 나쁜 애가 아니라고… 설령 진짜로 나쁜 짓을 하고 다닌다 해도 그렇게라도 하지 않곤 못 견딜 이유가 있을거라고 분명 그건 미츠코가 나빠서 그러는게 아니라고 생각해"],
|
||||
[0, "스즈키 아야로", null, 0, null, 55, 44, 77, 0, 160, 300, null, null],
|
||||
[0, "아사기 유미", null, 0, null, 84, 56, 66, 0, 160, 300, null, null],
|
||||
[1, "아오조야 하나코", null, 0, null, 89, 66, 94, 0, 160, 300, null, null],
|
||||
@@ -131,7 +132,7 @@
|
||||
[0, "아카타 마야코", null, 0, null, 35, 40, 55, 0, 160, 300, null, null],
|
||||
[0, "아타키 류야", null, 0, null, 64, 12, 80, 0, 160, 300, null, null],
|
||||
[0, "우츠미 유키에", null, 0, null, 71, 44, 82, 0, 160, 300, null, null, "바보야…모두 바보야! 모두 살아돌아갈수도 있엇는데…."],
|
||||
[0, "아제라크", null, 0, null, 81, 89, 68, 0, 160, 300, null, null, "프리큐어…! 모조리 거미밥으로 만들어주마!"],
|
||||
[0, "아제라크", null, 0, null, 81, 89, 68, 0, 160, 300, null, null],
|
||||
[0, "악튜러스 멩스크", null, 0, null, 95, 71, 99, 0, 151, 300, "패권", "집중", "누구도 날 막을 수 없어. 네놈이건, 연합이건, 프로토스건, 그 누구도! 코프룰루를 가질 수 없다면 차라리 잿더미로 만들어 버릴테다!"],
|
||||
[0, "발레리안 멩스크", null, 0, null, 85, 63, 88, 0, 160, 300, null, null, "난 국민의 편이오. 아버지의 죄는 용서받을 수 없다는 걸 알고 있소. 권좌에서… 내려오셔야 한다는 것도."],
|
||||
[10, "호러스 워필드", null, 0, null, 95, 83, 82, 0, 160, 300, "대의", "인덕", "캐리건... 잘 들어라. 지금 왕복선 3엔 부상병들만 타고 있다. 다들 누군가의... 남편이자, 아버지야. 들은 그냥 보내다오. 알겠나? 독한 것. 넌 인간도 아니야. 넌 우릴 배신했어. 뭣 때문에? 그깟 복수 때문에?\n무고한 사람들을 얼마나 죽인 거야? 또 얼마나 더 죽일 셈이야?\n레이너가 널 보면 뭐라고 할까?"],
|
||||
@@ -148,11 +149,11 @@
|
||||
[0, "유리아 베넷", null, 0, null, 76, 41, 55, 0, 160, 300, null, null],
|
||||
[0, "루미 베레스", null, 0, null, 77, 33, 89, 0, 160, 300, null, null],
|
||||
[0, "타이커스", null, 0, null, 61, 85, 40, 0, 160, 300, null, null],
|
||||
[0, "제임스 레이너", null, 0, null, 99, 88, 72, 0, 160, 300, "대의", "돌격", "피닉스! 안돼!!"],
|
||||
[0, "맷 호너", null, 0, null, 87, 64, 95, 0, 160, 300, null, null],
|
||||
[0, "제임스 레이너", null, 0, null, 99, 88, 72, 0, 160, 300, "대의", "돌격", "아크튜러스, 이 나쁜 자식! 그러지 마!"],
|
||||
[0, "맷 호너", null, 0, null, 87, 64, 95, 0, 160, 300, null, null, "멩스크를 끌어내리는 건 시작일 뿐입니다. 우리 목표는 더 밝은 미래라고요. 모르시겠습니까? 우린 멩스크에게 맞선 과학자, 철학자, 자유사상가들을 해방했어요. 그것이 오늘의 가장 큰 승리입니다."],
|
||||
[0, "로리 스완", null, 0, null, 32, 55, 93, 0, 160, 300, null, "발명"],
|
||||
[0, "이곤 스텟먼", null, 0, null, 11, 7, 91, 0, 160, 300, null, "발명"],
|
||||
[0, "가브리엘 토시", null, 0, null, 85, 96, 55, 0, 160, 300, null, null],
|
||||
[0, "가브리엘 토시", null, 0, null, 85, 96, 55, 0, 160, 300, null, null, "순진하기 짝이 없군. 내일 새로운 멩스크가 나타나고 그 후에 또 비슷한 놈이 나타날 겁니다. 밝게 빛나는 미래 따위는...그저 허상일 뿐이지."],
|
||||
[0, "로사 모랄레스", null, 0, null, 51, 44, 67, 0, 160, 300, null, null],
|
||||
[0, "레베카 레이너", null, 0, null, 81, 93, 44, 0, 160, 300, null, null],
|
||||
[0, "케이트 록웰", null, 0, null, 43, 10, 78, 0, 160, 300, null, null],
|
||||
@@ -161,7 +162,7 @@
|
||||
[0, "알렌 세자르", null, 0, null, 85, 61, 82, 0, 160, 300, null, null],
|
||||
[0, "마이클 리버티", null, 0, null, 64, 33, 71, 0, 160, 300, null, null],
|
||||
[0, "시온 크루스", null, 0, null, 51, 14, 85, 0, 160, 300, null, null],
|
||||
[0, "사라 케리건", null, 0, null, 95, 95, 81, 0, 160, 300, null, "무쌍"],
|
||||
[0, "사라 케리건", null, 0, null, 95, 95, 81, 0, 160, 300, null, "무쌍", "지미, 갑옷을 걸친 기사 행세는 그만둬. 그게 어울릴 때도 있지만, 지금은... 지금은 아냐. 난 내가 뭘 해야 하는지 잘 아니까. 프로토스는 저그뿐만 아니라 행성을 통째로 파괴하고 말 거야. 난 알아. 왜냐하면... 그냥 알 수 있어. 난... 유령이잖아. 프로토스를 처리하고 나서, 저그는 그때 생각할 거야. 아크튜러스도 결국 정신을 차릴 테고. 난 알아."],
|
||||
[10, "드랄리스카", null, 0, null, 82, 93, 78, 0, 160, 300, null, null, "케리건님이 죽다니…근데 왜 내가 그 중임을 이어받아야 하는거야?"],
|
||||
[0, "에레니카", null, 0, null, 78, 38, 91, 0, 160, 300, null, null, "곧있으면 위하신 아몬님이 완전히 부활할거야 드랄리스카 언니 후후후…."],
|
||||
[0, "세페니아", null, 0, null, 75, 81, 51, 0, 160, 300, null, null, "에레니카 언니 뭔가 이상해"],
|
||||
@@ -170,24 +171,24 @@
|
||||
[0, "데히카", null, 0, null, 75, 91, 55, 0, 160, 300, null, null],
|
||||
[0, "스투코프 부제독", null, 0, null, 87, 71, 93, 0, 160, 300, null, null],
|
||||
[0, "니아드라", null, 0, null, 80, 83, 52, 0, 160, 300, null, null],
|
||||
[0, "아바투르", null, 0, null, 43, 33, 81, 0, 160, 300, null, "발명"],
|
||||
[0, "아바투르", null, 0, null, 43, 33, 81, 0, 160, 300, null, "발명", "아바투르. 군단 진화 및 유전자 조작. 처음엔 초월체, 이후 칼날 여왕을 섬겼음. 이제 당신을 섬김.."],
|
||||
[0, "다고쓰", null, 0, null, 81, 85, 80, 0, 160, 300, null, null, "휴우… 드랄리스카도 환생자라 다행이야, 죽을일은 없겠네… 근데 케리건이 죽을줄이야…"],
|
||||
[0, "초월체", null, 0, null, 95, 55, 99, 0, 160, 300, null, null],
|
||||
[0, "초월체", null, 0, null, 95, 55, 99, 0, 160, 300, null, null, "일어나라, 내 아이야, 일어나 네 생득의 영광을 보듬어라.\n나는 초월체, 군단의 영원한 의지이며 넌 날 섬기기 위해 창조되었다.\n군락을 섬겨라. 나는 리듬을 지배한다. 군락을 섬겨라. 나는 리듬을 지배한다. 군락을 섬겨라.난 너의 행동을 지배한다. 내 리듬이 어떠한가, 다고스?"],
|
||||
[0, "자츠", null, 0, null, 82, 55, 71, 0, 160, 300, null, null],
|
||||
[0, "곤", null, 0, null, 79, 56, 77, 0, 160, 300, null, null],
|
||||
[0, "카그", null, 0, null, 80, 55, 69, 0, 160, 300, null, null],
|
||||
[0, "아우자", null, 0, null, 64, 71, 86, 0, 160, 300, null, null],
|
||||
[0, "제라드 듀갈", null, 0, null, 87, 51, 80, 0, 160, 300, null, null],
|
||||
[0, "맘스틴 령", null, 0, null, 80, 51, 66, 0, 160, 300, null, null],
|
||||
[0, "스투코프 주니어", null, 0, null, 81, 56, 72, 0, 160, 300, null, null, "ued 2차원정로 우린 아몬과의 결전에 참가할거야."],
|
||||
[0, "레비나 스투코프", null, 0, null, 66, 31, 90, 0, 160, 300, null, null, "근데 우리 병사로 괜찮을까 오빠"],
|
||||
[0, "제라툴", null, 0, null, 85, 90, 82, 0, 160, 300, null, null, "젤나가 맙소사! 누가 이런 끔찍한 혼종을 만들었단 말인가"],
|
||||
[0, "테사다르", "`", 0, null, 82, 77, 86, 0, 160, 300, null, null, "난 죽음을 경험한 적이 없네"],
|
||||
[0, "피닉스", null, 0, null, 79, 80, 65, 0, 160, 300, null, null],
|
||||
[0, "알다리스", null, 0, null, 80, 51, 77, 0, 160, 300, null, null],
|
||||
[0, "제라드 듀갈", null, 0, null, 87, 51, 80, 0, 160, 300, null, null, "다시는 나를 볼 수 없을 거요, 헬레나.\n아이들에게 사랑한다고 전해 주시오. 아버지가 너희들의 미래를 지키다가 죽었다고도 말해 주시오.\n그럼, 안녕히...(Au revoir)."],
|
||||
[0, "맘스틴 대령", null, 0, null, 80, 51, 66, 0, 160, 300, null, null],
|
||||
[0, "스투코프 주니어", null, 0, null, 81, 56, 72, 0, 160, 300, null, null],
|
||||
[0, "레비나 스투코프", null, 0, null, 66, 31, 90, 0, 160, 300, null, null],
|
||||
[0, "제라툴", null, 0, null, 85, 90, 82, 0, 160, 300, null, null, "젤나가 맙소사! 누가 이런 혼종을 만들었단 말인가."],
|
||||
[0, "테사다르", "`", 0, null, 82, 77, 86, 0, 160, 300, null, null, "집행관, 초월체가 약해졌소. 하지만 우리도 심각한 피해를 입었군. 내가 간트리서를 초월체에게 충돌시키겠소. 간트리서의 선체에 암흑 기사단의 힘을 충분히 모은다면, 저 저주받은 흉물에게 죽음을 선사할 수 있을 거요. 우리를 기억해주오, 집행관이여. 오늘 우리가 한 일을 기억하시오. 아둔이 그대를 보살피시길"],
|
||||
[0, "피닉스", null, 0, null, 79, 80, 65, 0, 160, 300, null, null, "칼라가 나를 기다린다, 케리건. 비록 난 운명을 맞이할 준비가 되기는 했지만, 그렇게 쉬운 먹이가 되진 않겠다!"],
|
||||
[0, "알다리스", null, 0, null, 80, 51, 77, 0, 160, 300, null, null, "집행관, 태사다르, 소식이 늦어서 미안하군. 대의회는 그대들이 정신체를 쓰러뜨리는 것을 지켜보았소. 그리고, 그 용감한 행위가 헛되지 않았음을 깨달았지. 그대들을 심판하려 했으나, 그릇된 생각을 한 것은 우리였소. 그대들은 우리의 내면에 존재하는 위대함의 상징이니, 우리 모두의 희망을 걸겠소. 엔 타로 아둔! 용감한 아이어의 후예들이여!"],
|
||||
[0, "탈란다르", null, 0, null, 84, 66, 80, 0, 160, 300, null, null],
|
||||
[0, "아르타니스", null, 0, null, 80, 71, 84, 0, 160, 300, null, null, "모조리 용기병으로 만들어주마!"],
|
||||
[0, "라자갈", null, 0, null, 90, 75, 89, 0, 160, 300, null, null],
|
||||
[0, "아르타니스", null, 0, null, 80, 71, 84, 0, 160, 300, null, null, "모조리 용기병으로 만들어주마"],
|
||||
[0, "라자갈", null, 0, null, 90, 75, 89, 0, 160, 300, null, null, "고맙다, 제라툴... 네가 나를 놈의 추악한 손아귀에서 벗어나게 해주었다. 너는 늘 명예롭게 나를 섬겨주었지... 그러니 부탁하마... 내 부족을 돌봐 다오... 네 손에 미래를 맡기겠다"],
|
||||
[0, "우룬 제독", null, 0, null, 84, 80, 55, 0, 160, 300, null, null],
|
||||
[0, "셀렌디스", null, 0, null, 80, 80, 67, 0, 160, 300, null, null],
|
||||
[0, "보라준", null, 0, null, 87, 51, 91, 0, 160, 300, null, null],
|
||||
@@ -195,7 +196,7 @@
|
||||
[0, "말라쉬", null, 0, null, 80, 96, 65, 0, 160, 300, null, null],
|
||||
[0, "니온", null, 0, null, 62, 73, 43, 0, 160, 300, null, null],
|
||||
[0, "지나라", null, 0, null, 75, 90, 55, 0, 160, 300, null, null],
|
||||
[0, "아리샤", null, 0, null, 51, 81, 50, 0, 160, 300, null, null, "난 XX부르드의 울트라 표 아리샤다!"],
|
||||
[0, "아리샤", null, 0, null, 51, 81, 50, 0, 160, 300, null, null],
|
||||
[0, "아몬", null, 0, null, 97, 107, 85, 0, 160, 300, null, null, "나는 너희들의 시작이었으니 또한 끝이리라….!"],
|
||||
[0, "에밀 나루드", null, 0, null, 85, 92, 96, 0, 160, 300, null, null, "아몬께서 별빛으로 속삭이셨다. 그분은 돌아오신다고 하셨다. 그분은 파괴한다고 하셨다. 멸종. 모든 것이 종말을 맞이할 것이라고"],
|
||||
[0, "오로스", null, 0, null, 80, 79, 55, 0, 160, 300, null, null],
|
||||
@@ -206,14 +207,14 @@
|
||||
[0, "유당찬", null, 0, null, 83, 69, 55, 0, 160, 300, null, null],
|
||||
[0, "유시하", "`", 0, null, 67, 55, 81, 0, 160, 300, null, null],
|
||||
[0, "유미하", null, 0, null, 41, 34, 48, 0, 160, 300, null, null],
|
||||
[0, "유지은", null, 0, null, 55, 78, 64, 0, 160, 300, null, null, "나는 레플리카"],
|
||||
[0, "유혜선", null, 0, null, 78, 89, 55, 0, 160, 300, null, null, "나는"],
|
||||
[0, "유지은", null, 0, null, 55, 78, 64, 0, 160, 300, null, null, "나는 레플리카, 적의 기술을 베껴서 사용 가능하지."],
|
||||
[0, "유혜선", null, 0, null, 78, 89, 55, 0, 160, 300, null, null],
|
||||
[0, "최정윤", null, 0, null, 85, 71, 66, 0, 160, 300, null, null, "우리는 다미키즈, 다미의 친구들이자 다미에 의해 도움을 받은 아이들 거기서도 나는 캔디팝이야"],
|
||||
[0, "이선아", null, 0, null, 83, 88, 56, 0, 160, 300, null, null, "나는 마콧치, 마법나라 감사부문의 부부장이야."],
|
||||
[0, "나서희", null, 0, null, 81, 77, 44, 0, 160, 300, null, null, "다미를 지킬거야"],
|
||||
[0, "나서희", null, 0, null, 81, 77, 44, 0, 160, 300, null, null, "우리는 다미를 지킬거야"],
|
||||
[0, "스노우 화이트", null, 0, null, 71, 80, 66, 0, 160, 300, null, null, "이번에야 말로 친구들을 지켜 보이겠어. 소타도, 아카리도"],
|
||||
[0, "워터 스플렛", null, 0, null, 65, 80, 52, 0, 160, 300, null, null, "이번생에는 그런 셔플린 따위에게 지지 않을거야,"],
|
||||
[0, "스노우 슬러스", null, 0, null, 69, 75, 53, 0, 160, 300, null, null, "번생이랑 달라! 나도 강해졋다고!"],
|
||||
[0, "워터 스플렛", null, 0, null, 65, 80, 52, 0, 160, 300, null, null, "…. 이번에야말로…"],
|
||||
[0, "스노우 슬러스", null, 0, null, 69, 75, 53, 0, 160, 300, null, null, "저번생이랑 달라,… 나도 언니처럼 강해질수 있어"],
|
||||
[0, "리하나", null, 0, null, 79, 70, 65, 0, 160, 300, null, null, "프흐레 언니…"],
|
||||
[0, "블루베리", null, 0, null, 81, 88, 73, 0, 160, 300, null, null],
|
||||
[0, "스윔스윔", null, 0, null, 54, 90, 71, 0, 163, 300, null, null, "룰러가 그랫어 마법소녀는 본모습을 남에게 보여주면 안된다고"],
|
||||
@@ -221,22 +222,22 @@
|
||||
[0, "정혜림", null, 0, null, 91, 88, 80, 0, 160, 300, null, null, "우리 다미키즈는 다미언니에 의해 많은걸 받은 아이들이야,"],
|
||||
[0, "하나조노 아무", null, 0, null, 67, 53, 81, 0, 160, 300, null, null, "캐릭터 변신! 애뮬릿 다이아!"],
|
||||
[0, "하나조노 카렌", null, 0, null, 66, 51, 80, 0, 160, 300, null, null, "나는 하나조노 카렌, 전 이름은 하나무라 카렌이야."],
|
||||
[0, "하나조노 아리스", null, 0, null, 53, 14, 61, 0, 160, 300, null, null],
|
||||
[0, "하나조노 아리스", null, 0, null, 53, 14, 61, 0, 160, 300, null, null, "당신에겐 고민이 있나요? 학교생활, 그리고 친구, 가족과의 관계와 장래의 꿈에 대한 고민, 혼자서는 도저히 해결할수 없는 고민들이 있죠? 바로 그럴떄 내 이야기를 들어줄 누군가가 필요하지 않나요? 그게 누구냐고요? 만나고 싶으면 리루 리루 페어리루라 외치세요!"],
|
||||
[0, "린", null, 0, null, 38, 32, 69, 0, 160, 300, null, null, "난 용담꽃의 페어리루 린이야~"],
|
||||
[0, "하나조노 마츠아", null, 0, null, 44, 40, 55, 0, 160, 300, null, null],
|
||||
[0, "롭프", null, 0, null, 55, 47, 50, 0, 160, 300, null, null],
|
||||
[0, "이정선 황제", null, 0, null, 74, 44, 85, 0, 160, 300, null, null, "감히 우리 제국에 반기를 든자들! 용서하지 않는다! !"],
|
||||
[0, "이양선 공", null, 0, null, 77, 42, 79, 0, 160, 300, null, null, "형님이 리 화를 내시다니 내심 단하구나 그들은"],
|
||||
[0, "이양선 공", null, 0, null, 77, 42, 79, 0, 160, 300, null, null, "형님께서 이리 화를 내시다니, 그들도 강하군"],
|
||||
[0, "이을용 황태자", null, 0, null, 51, 33, 69, 0, 160, 300, null, null],
|
||||
[0, "카라시프 장군", null, 0, null, 75, 69, 71, 0, 160, 300, null, null],
|
||||
[0, "카몬 요네미", null, 0, null, 40, 8, 66, 0, 160, 300, null, null],
|
||||
[0, "키타노 타케시", null, 0, null, 43, 14, 75, 0, 160, 300, null, null, "우리가족의 행복과 안전을 우선시해야지"],
|
||||
[0, "키타노 유리아", null, 0, null, 32, 8, 69, 0, 160, 300, null, null],
|
||||
[0, "후타바 안즈", null, 0, null, 67, 44, 90, 0, 160, 300, null, null, "나도 하메르도 행복하길"],
|
||||
[0, "히나노", null, 0, null, 66, 32, 73, 0, 160, 300, null, null, "이번생에는 행복하고 싶어"],
|
||||
[0, "히나노", null, 0, null, 66, 32, 73, 0, 160, 300, null, null, "히힛"],
|
||||
[0, "나츠세 유우", null, 0, null, 66, 30, 70, 0, 160, 300, null, null],
|
||||
[0, "크로노스 신", null, 0, null, 50, 15, 90, 0, 160, 300, null, null],
|
||||
[0, "프흐레", null, 0, null, 69, 24, 100, 0, 160, 300, null, null, "마법나라가 바라는 마법소녀란 말이지…"],
|
||||
[0, "크로노스 신", null, 0, null, 33, 15, 90, 0, 160, 300, null, null],
|
||||
[0, "프흐레", null, 0, null, 69, 24, 100, 0, 160, 300, null, null, "마법나라'가 원하는 마법소녀다운 마법소녀란 건 말이지. 여차할 때 주저없이 자기 자신을 희생할 수 있는 성인이야. 제대로 돼먹은 정신머리로는 못 해먹을 짓거리지."],
|
||||
[0, "나나하라 슈야", null, 0, null, 73, 72, 62, 0, 160, 300, null, null, "전세계의 어른들에게 선전포고 한다!"],
|
||||
[0, "타에구치유이치로", null, 0, null, 51, 63, 44, 0, 160, 300, null, null],
|
||||
[0, "카와다 소교", null, 0, null, 49, 70, 85, 0, 160, 300, null, null],
|
||||
@@ -246,12 +247,12 @@
|
||||
[0, "큐어 리프렛", null, 0, null, 71, 89, 72, 0, 160, 300, null, null],
|
||||
[0, "큐어 실프", null, 0, null, 83, 92, 88, 0, 160, 300, null, null],
|
||||
[0, "큐어 아네모스", null, 0, null, 69, 80, 55, 0, 160, 300, null, null],
|
||||
[0, "카마디야", null, 0, null, 85, 93, 55, 0, 160, 300, null, null, "크흐흐… 감히 인간 따위가!! 나에게 덤비다니 용서못한다!!"],
|
||||
[0, "카마디야", null, 0, null, 85, 93, 55, 0, 160, 300, null, null, "이번엔 방심하지 않고 인간들을 처리하겟다!"],
|
||||
[0, "토토리몬", null, 0, null, 69, 88, 44, 0, 160, 300, null, null, "모든것은 카마디야님의 부활을 위해서…."],
|
||||
[0, "카와리노", null, 0, null, 73, 91, 80, 0, 160, 300, null, null, "나를 그곳에 꺼내주신 카마디야 님을 위해 난 일한다!"],
|
||||
[0, "가마오", null, 0, null, 41, 65, 13, 0, 160, 300, null, null, "이씨 너무 배고파…"],
|
||||
[0, "밀키 로즈", null, 0, null, 64, 82, 52, 0, 160, 300, null, null],
|
||||
[0, "옐로우 미치", null, 0, null, 61, 80, 80, 0, 160, 300, null, null, "난 마법나라 외교부문의 부문장 옐로우 미치야"],
|
||||
[0, "옐로우 미치", null, 0, null, 61, 80, 80, 0, 160, 300, null, null, "후후… 모든건 모두의 행복을 위해서"],
|
||||
[0, "마왕팜", null, 0, null, 50, 90, 70, 0, 160, 300, null, null, "무지개? 어째서…!"],
|
||||
[0, "그림하트", null, 0, null, 74, 63, 41, 0, 160, 300, null, null, "목을 쳐라!!"],
|
||||
[0, "루다", null, 0, null, 62, 66, 62, 0, 160, 300, null, null, "널 좋아해 미우."],
|
||||
@@ -272,7 +273,7 @@
|
||||
[0, "유해", null, 0, null, 85, 87, 64, 0, 160, 300, null, null],
|
||||
[0, "유채", null, 0, null, 64, 71, 23, 0, 160, 300, null, null],
|
||||
[0, "유창", null, 0, null, 59, 68, 33, 0, 160, 300, null, null],
|
||||
[0, "타카이시 타케루", null, 0, null, 80, 92, 65, 0, 160, 300, null, "무쌍"],
|
||||
[0, "타카이시 타케루", null, 0, null, 80, 92, 65, 0, 160, 300, null, null],
|
||||
[0, "타카이시 히카리", null, 0, null, 31, 11, 90, 0, 160, 300, null, "인덕"],
|
||||
[0, "타카이시 노조미", null, 0, null, 82, 66, 81, 0, 160, 300, null, null],
|
||||
[0, "타카이시 미츠루", null, 0, null, 85, 51, 71, 0, 160, 300, null, null, "이 나라가 왜이리 망가져 버린걸까"],
|
||||
@@ -280,7 +281,7 @@
|
||||
[0, "유상호", null, 0, null, 39, 56, 44, 0, 160, 300, null, null, "쯧. 런애가 내 딸이라니 수치야"],
|
||||
[0, "서유현", null, 0, null, 42, 44, 60, 0, 160, 300, null, null, "하… 내가 교육을 잘못했구나"],
|
||||
[0, "오준", null, 0, null, 69, 12, 84, 0, 160, 300, null, null, "이 지역은 이제부터 내것이다!"],
|
||||
[10, "도니 버밀리언", null, 0, null, 48, 14, 66, 0, 160, 300, null, null],
|
||||
[10, "도니 버밀리언", null, 0, null, 48, 14, 66, 0, 160, 300, null, null, "레이너, 당신은 양심도 없습니까?"],
|
||||
[0, "모한다르", null, 0, null, 68, 81, 45, 0, 160, 300, null, null],
|
||||
[0, "월리엄 와이트", null, 0, null, 55, 64, 88, 0, 160, 300, null, null, "아니 나를 별명으로 부르지 말라고!"],
|
||||
[0, "도미니크 헥터", null, 0, null, 57, 69, 41, 0, 160, 300, null, null],
|
||||
@@ -296,7 +297,7 @@
|
||||
[0, "사야코", null, 0, null, 69, 75, 60, 0, 160, 300, null, null],
|
||||
[0, "미요가타노 나코", null, 0, null, 77, 55, 79, 0, 160, 300, null, null, "fucking doll!"],
|
||||
[0, "첼너 마우스", null, 0, null, 51, 85, 2, 0, 160, 300, null, null, "체르나는 그런거 싫단 말이야! 내꺼라고!"],
|
||||
[0, "페치카", null, 0, null, 68, 8, 80, 0, 160, 300, null, null],
|
||||
[0, "페치카", null, 0, null, 68, 8, 80, 0, 160, 300, null, null, "마법소녀끼리 정정당당하게 싸우면…… 크랑테일은 당신 같은 사람에게 지지 않아."],
|
||||
[0, "기타노 유리코", null, 0, null, 14, 9, 64, 0, 160, 300, null, null, "유키코는 참 의젓한 아이야."],
|
||||
[0, "니얀다 카멘", null, 0, null, 86, 85, 41, 0, 160, 300, null, null, "살려달라고 외치면 그 누구든 구할거야, 설사 적이라 할지라도"],
|
||||
[0, "니얀다 카렌", null, 0, null, 73, 88, 60, 0, 160, 300, null, null],
|
||||
@@ -318,9 +319,9 @@
|
||||
[0, "모노미야 세이나", null, 0, null, 77, 83, 41, 0, 160, 300, null, null, "어떤 유혹에도 빛을 잃지않는 깨끗한 마음 큐어 다이아!"],
|
||||
[0, "모노야마 유이", null, 0, null, 69, 85, 60, 0, 160, 300, null, null, "사랑하는 사람을 지키기 위한 진실한 마음 큐어 실린!"],
|
||||
[0, "루시에르", null, 0, null, 84, 72, 80, 0, 160, 300, null, null, "내 딸들은 행복했으면 좋겟어"],
|
||||
[0, "큐어 루프", null, 0, null, 63, 81, 40, 0, 160, 300, null, null, "감히… 내동생을 어둠의 가면으로 죽였겟다! 복수할거야! 로스쿨"],
|
||||
[0, "가제크", null, 0, null, 55, 79, 51, 0, 160, 300, null, null, "어…언니 사…살려줘!!"],
|
||||
[0, "큐어 가젯", null, 0, null, 67, 81, 55, 0, 160, 300, null, null, "어머니… 어머니의 원수 꼭 갚겠어요.. 우리를 버린 로스쿨에게 복수할 겁니다"],
|
||||
[0, "큐어 루프", null, 0, null, 63, 81, 40, 0, 160, 300, null, null, "우리는 왜 이런 직장에 충성한 걸까"],
|
||||
[0, "가제크", null, 0, null, 55, 79, 51, 0, 160, 300, null, null],
|
||||
[0, "큐어 가젯", null, 0, null, 67, 81, 55, 0, 160, 300, null, null, "어머니… 어머니의 원수 꼭 갚겠어요.. 우리를 버린 그들에게 복수할 겁니다."],
|
||||
[0, "큐어 루비", null, 0, null, 73, 79, 59, 0, 160, 300, null, null, "언니 우리 같이 힘내자"],
|
||||
[0, "루미에르", null, 0, null, 80, 81, 64, 0, 160, 300, null, null],
|
||||
[0, "토모야마 리에리", null, 0, null, 92, 55, 91, 0, 160, 300, null, null],
|
||||
@@ -333,12 +334,12 @@
|
||||
[0, "에트리카 에슈", null, 0, null, 84, 53, 82, 0, 160, 300, null, null, "모든것은, 나를 이렇게 만든 세상에 복수하기 위해!"],
|
||||
[0, "유가을", null, 0, null, 87, 95, 60, 0, 160, 300, null, null],
|
||||
[0, "츠바이 유시", null, 0, null, 43, 63, 77, 0, 160, 300, null, null],
|
||||
[0, "크람베리", null, 0, null, 82, 93, 51, 0, 160, 300, null, null],
|
||||
[0, "파브", null, 0, null, 41, 10, 78, 0, 160, 300, null, null],
|
||||
[0, "파르", null, 0, null, 43, 10, 88, 0, 160, 300, null, null],
|
||||
[0, "크람베리", null, 0, null, 82, 93, 51, 0, 160, 300, null, null, "전 숲의 음악가 크람베리, 마법소녀를 하고 있습니다."],
|
||||
[0, "파브", null, 0, null, 41, 10, 78, 0, 160, 300, null, null, "지금부터 이 마을의 마법소녀를 반으로 줄여야 합니다퐁!"],
|
||||
[0, "파르", null, 0, null, 43, 10, 88, 0, 160, 300, null, null, "역시 스노우화이트는 내가 없으면 안되퐁…"],
|
||||
[0, "타이요 아카리", null, 0, null, 81, 92, 77, 0, 160, 300, null, null, "나 잊지 않을테니까, 후유나에 대해 절대로 잊지않을 테니까"],
|
||||
[0, "신자키 후유나", null, 0, null, 74, 79, 55, 0, 160, 300, null, null, "안녕 아카리, 아카리와 만나 행복했어"],
|
||||
[0, "크랑테일", null, 0, null, 77, 81, 55, 0, 160, 300, null, null],
|
||||
[0, "크랑테일", null, 0, null, 77, 81, 55, 0, 160, 300, null, null, "라즐리느와 페치카가 살려준 난...... 못 죽어"],
|
||||
[0, "타카이시 히모리", null, 0, null, 69, 37, 89, 0, 160, 300, null, null, "내 나라, 부모님의 나라 내가 지킬거야"],
|
||||
[0, "사카나기 아야나", null, 0, null, 73, 89, 53, 0, 160, 300, null, null, "난 스윔스윔, 잘부탁해"],
|
||||
[0, "사카나기 이즈카", null, 0, null, 77, 82, 60, 0, 160, 300, null, null, "난 사야코야"],
|
||||
@@ -359,8 +360,8 @@
|
||||
[0, "히노모토 아카리", null, 0, null, 70, 17, 81, 0, 160, 300, null, null],
|
||||
[0, "아오누마 키리하", null, 0, null, 84, 67, 85, 0, 160, 300, null, null],
|
||||
[0, "츠루기 젠지로", null, 0, null, 56, 69, 40, 0, 160, 300, null, null],
|
||||
[0, "향상성", null, 0, null, 82, 77, 90, 0, 160, 300, null, null],
|
||||
[0, "이그드라실", null, 0, null, 80, 80, 77, 0, 160, 300, null, null, "저는 디지털 월드의 평화를 바라는자,"],
|
||||
[0, "향상성", null, 0, null, 82, 77, 90, 0, 160, 300, null, null, "저는 디지털 월드의 평화를 바라는 자,"],
|
||||
[0, "이그드라실", null, 0, null, 80, 80, 77, 0, 160, 300, null, null, "어머니와 향상성을 이해할수가 없군요"],
|
||||
[0, "렌", null, 0, null, 89, 100, 42, 0, 160, 300, null, "무쌍"],
|
||||
[0, "네네네", null, 0, null, 63, 55, 89, 0, 160, 300, null, null],
|
||||
[0, "이소긴&야도간", null, 0, null, 58, 77, 39, 0, 160, 300, null, null],
|
||||
@@ -377,7 +378,8 @@
|
||||
[0, "큐어 리히트", null, 0, null, 89, 93, 74, 0, 160, 300, null, null],
|
||||
[0, "다크 드림", null, 0, null, 77, 94, 76, 0, 160, 300, null, null],
|
||||
[0, "큐어 프렌디", null, 0, null, 86, 83, 77, 0, 160, 300, null, null],
|
||||
[0, "큐어 릴리안", null, 0, null, 87, 82, 80, 0, 160, 300, null, null]
|
||||
[0, "큐어 릴리안", null, 0, null, 87, 82, 80, 0, 160, 300, null, null],
|
||||
[0, "마시로 리마", null, 0, null, 86, 79, 82, 0, 160, 300, null, null]
|
||||
],
|
||||
"general_ex":[
|
||||
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
{
|
||||
"title":"【공백지】 무한대흥 ",
|
||||
"startYear":180,
|
||||
"map":{
|
||||
"mapName":"miniche"
|
||||
},
|
||||
"history":[
|
||||
"<C>●</>180년 1월:<L><b>【이벤트】</b></>많은 제한치가 더 높게 풀려있습니다."
|
||||
],
|
||||
"stat": {
|
||||
"total": 200,
|
||||
"min": 20,
|
||||
"max": 100,
|
||||
"chiefMin": 20
|
||||
},
|
||||
"const": {
|
||||
"joinRuinedNPCProp":0,
|
||||
"npcBanMessageProb":0.005,
|
||||
"scenarioEffect": "event_MoreEffect",
|
||||
"availableInstantAction": {
|
||||
"instantRetreat": true
|
||||
},
|
||||
"defaultMaxGeneral": 800,
|
||||
"basePopIncreaseAmount": 10000,
|
||||
"maxTechLevel": 15,
|
||||
"upgradeLimit": 20,
|
||||
"initialAllowedTechLevel": 2,
|
||||
"expandCityPopIncreaseAmount": 150000,
|
||||
"expandCityDevelIncreaseAmount": 3000,
|
||||
"expandCityWallIncreaseAmount": 3000,
|
||||
"defaultCityWall": 3000,
|
||||
"maxResourceActionAmount": 30000,
|
||||
"resourceActionAmountGuide": [
|
||||
100, 200, 300, 400, 500, 600, 700, 800, 900, 1000,
|
||||
1200, 1500, 2000, 2500, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000,
|
||||
12000, 15000, 20000, 25000, 30000
|
||||
],
|
||||
"allItems": {
|
||||
"horse" : {
|
||||
"che_명마_01_노기" : 0, "che_명마_02_조랑" : 0, "che_명마_03_노새" : 0,
|
||||
"che_명마_04_나귀" : 0, "che_명마_05_갈색마" : 0, "che_명마_06_흑색마" : 0,
|
||||
|
||||
"che_명마_07_백마" : 4, "che_명마_07_기주마" : 4, "che_명마_07_오환마" : 4, "che_명마_07_백상" : 4,
|
||||
"che_명마_08_양주마" : 4, "che_명마_08_흉노마" : 4, "che_명마_09_과하마" : 4, "che_명마_09_의남백마" : 4,
|
||||
"che_명마_10_대완마" : 4, "che_명마_10_옥추마" : 4, "che_명마_11_서량마" : 4, "che_명마_11_화종마" : 4,
|
||||
"che_명마_12_사륜거" : 4, "che_명마_12_옥란백용구": 4, "che_명마_13_절영" : 4, "che_명마_13_적로" : 4,
|
||||
"che_명마_14_적란마" : 4, "che_명마_14_조황비전" : 4, "che_명마_15_한혈마" : 4, "che_명마_15_적토마" : 4
|
||||
},
|
||||
"weapon" : {
|
||||
"che_무기_01_단도" : 0, "che_무기_02_단궁" : 0, "che_무기_03_단극" : 0,
|
||||
"che_무기_04_목검" : 0, "che_무기_05_죽창" : 0, "che_무기_06_소부" : 0,
|
||||
|
||||
"che_무기_07_동추" : 4, "che_무기_07_철편" : 4, "che_무기_07_철쇄" : 4, "che_무기_07_맥궁" : 4,
|
||||
"che_무기_08_유성추" : 4, "che_무기_08_철질여골" : 4, "che_무기_09_쌍철극" : 4, "che_무기_09_동호비궁" : 4,
|
||||
"che_무기_10_삼첨도" : 4, "che_무기_10_대부" : 4, "che_무기_11_고정도" : 4, "che_무기_11_이광궁" : 4,
|
||||
"che_무기_12_철척사모" : 4, "che_무기_12_칠성검" : 4, "che_무기_13_사모" : 4, "che_무기_13_양유기궁" : 4,
|
||||
"che_무기_14_언월도" : 4, "che_무기_14_방천화극" : 4, "che_무기_15_청홍검" : 4, "che_무기_15_의천검" : 4
|
||||
},
|
||||
"book" : {
|
||||
"che_서적_01_효경전" : 0, "che_서적_02_회남자" : 0, "che_서적_03_변도론" : 0,
|
||||
"che_서적_04_건상역주" : 0, "che_서적_05_여씨춘추" : 0, "che_서적_06_사민월령" : 0,
|
||||
|
||||
"che_서적_07_위료자" : 4, "che_서적_07_사마법" : 4, "che_서적_07_한서" : 4, "che_서적_07_논어" : 4,
|
||||
"che_서적_08_전론" : 4, "che_서적_08_사기" : 4, "che_서적_09_장자" : 4, "che_서적_09_역경" : 4,
|
||||
"che_서적_10_시경" : 4, "che_서적_10_구국론" : 4, "che_서적_11_상군서" : 4, "che_서적_11_춘추전" : 4,
|
||||
"che_서적_12_산해경" : 4, "che_서적_12_맹덕신서" : 4, "che_서적_13_관자" : 4, "che_서적_13_병법24편" : 4,
|
||||
"che_서적_14_한비자" : 4, "che_서적_14_오자병법" : 4, "che_서적_15_노자" : 4, "che_서적_15_손자병법" : 4
|
||||
},
|
||||
"item": {
|
||||
"che_치료_환약": 0,
|
||||
"che_저격_수극": 0,
|
||||
"che_사기_탁주": 0,
|
||||
"che_훈련_청주": 0,
|
||||
"che_계략_이추": 0,
|
||||
"che_계략_향낭": 0,
|
||||
|
||||
"event_전투특기_격노": 0,
|
||||
"event_전투특기_견고": 0,
|
||||
"event_전투특기_공성": 0,
|
||||
"event_전투특기_궁병": 0,
|
||||
"event_전투특기_귀병": 0,
|
||||
"event_전투특기_기병": 0,
|
||||
"event_전투특기_돌격": 0,
|
||||
"event_전투특기_무쌍": 0,
|
||||
"event_전투특기_반계": 0,
|
||||
"event_전투특기_보병": 0,
|
||||
"event_전투특기_신산": 0,
|
||||
"event_전투특기_신중": 0,
|
||||
"event_전투특기_위압": 0,
|
||||
"event_전투특기_의술": 0,
|
||||
"event_전투특기_저격": 0,
|
||||
"event_전투특기_집중": 0,
|
||||
"event_전투특기_징병": 0,
|
||||
"event_전투특기_척사": 0,
|
||||
"event_전투특기_필살": 0,
|
||||
"event_전투특기_환술": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"initialEvents":[
|
||||
[
|
||||
true,
|
||||
["ChangeCity", ["cities", "낙양", "성도", "건업", "업", "장안", "허창", "양양"], {
|
||||
"pop_max": "+300000",
|
||||
"agri_max": "+6000",
|
||||
"comm_max": "+6000",
|
||||
"secu_max": "+5000",
|
||||
"def_max": "+6000",
|
||||
"wall_max": "+6000"
|
||||
}],
|
||||
["ChangeCity", ["cities", "시상", "수춘", "한중", "남피", "위례", "북평", "강릉", "완", "장사"], {
|
||||
"pop_max": "+250000",
|
||||
"agri_max": "+5000",
|
||||
"comm_max": "+5000",
|
||||
"secu_max": "+4000",
|
||||
"def_max": "+5000",
|
||||
"wall_max": "+5000"
|
||||
}],
|
||||
["ChangeCity", ["cities", "오", "하비", "복양", "웅진", "강주", "무도", "국내", "진류", "계양", "계림", "계", "무위", "제남", "남해", "덕양", "하내", "상용"], {
|
||||
"pop_max": "+200000",
|
||||
"agri_max": "+4000",
|
||||
"comm_max": "+4000",
|
||||
"secu_max": "+3000",
|
||||
"def_max": "+4000",
|
||||
"wall_max": "+4000"
|
||||
}],
|
||||
["ChangeCity", ["cities", "초", "운남", "대", "하동", "무릉", "교지", "단양", "영안", "북해", "건녕", "강하", "진양", "평원", "회계", "천수", "평양", "요동", "거록", "여강", "패", "자동", "광릉", "장가", "영릉", "월수", "건안", "신야", "상동", "안정", "여남", "홍농", "주제"], {
|
||||
"pop_max": "+150000",
|
||||
"agri_max": "+3000",
|
||||
"comm_max": "+3000",
|
||||
"secu_max": "+2000",
|
||||
"def_max": "+3000",
|
||||
"wall_max": "+3000"
|
||||
}],
|
||||
["ChangeCity", ["cities", "남만", "산월", "오환", "강", "왜", "흉노"], {
|
||||
"pop_max": "+100000",
|
||||
"agri_max": "+2000",
|
||||
"comm_max": "+2000",
|
||||
"secu_max": "+1000",
|
||||
"def_max": "+2000",
|
||||
"wall_max": "+2000"
|
||||
}],
|
||||
["ChangeCity", ["cities", "합비", "이릉", "오장원", "호로", "탐라", "적벽"], {
|
||||
"pop_max": "+100000",
|
||||
"agri_max": "+1000",
|
||||
"comm_max": "+1000",
|
||||
"secu_max": "+1000",
|
||||
"def_max": "+1000",
|
||||
"wall_max": "+1000"
|
||||
}],
|
||||
["ChangeCity", "free", {
|
||||
"pop":"50%"
|
||||
}]
|
||||
]
|
||||
],
|
||||
"events":[
|
||||
[
|
||||
"month", 1000,
|
||||
["or", ["Date", "==", null, 12], ["Date", "==", null, 6]],
|
||||
["CreateManyNPC", 15, 10],
|
||||
["DeleteEvent"]
|
||||
],
|
||||
[
|
||||
"month", 1000,
|
||||
["Date", "==", 181, 1],
|
||||
["RaiseNPCNation"],
|
||||
["DeleteEvent"]
|
||||
],
|
||||
[
|
||||
"month", 999,
|
||||
["Date", "==", 181, 1],
|
||||
["OpenNationBetting", 4, 5000],
|
||||
["OpenNationBetting", 1, 2000],
|
||||
["DeleteEvent"]
|
||||
],
|
||||
[
|
||||
"month", 1000,
|
||||
["Date", "==", 182, 1],
|
||||
["ChangeCity", "occupied", {
|
||||
"trade":100
|
||||
}],
|
||||
["DeleteEvent"]
|
||||
],
|
||||
[
|
||||
"month", 1000,
|
||||
["Date", "==", 182, 7],
|
||||
["ChangeCity", "occupied", {
|
||||
"trade":100
|
||||
}],
|
||||
["DeleteEvent"]
|
||||
],
|
||||
[
|
||||
"month", 999,
|
||||
["and",
|
||||
["Date", ">=", 183, 1],
|
||||
["RemainNation", "<=", 8]
|
||||
],
|
||||
["OpenNationBetting", 1, 1000],
|
||||
["DeleteEvent"]
|
||||
],
|
||||
[
|
||||
"destroy_nation", 1000,
|
||||
["and",
|
||||
["Date", ">=", 183, 1],
|
||||
["RemainNation", "==", 1]
|
||||
],
|
||||
["BlockScoutAction"],
|
||||
["DeleteEvent"]
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
{
|
||||
"title":"【공백지】 요양 ",
|
||||
"startYear":180,
|
||||
"map":{
|
||||
"mapName":"miniche"
|
||||
},
|
||||
"history":[
|
||||
"<C>●</>180년 1월:<L><b>【이벤트】</b></>쉬었다 가는 깃수입니다."
|
||||
],
|
||||
"const": {
|
||||
"joinRuinedNPCProp":0,
|
||||
"npcBanMessageProb":0.005,
|
||||
"defaultMaxGeneral": 800,
|
||||
"allItems":{
|
||||
"horse":{
|
||||
"che_명마_01_노기":0, "che_명마_02_조랑":0, "che_명마_03_노새":0,
|
||||
"che_명마_04_나귀":0, "che_명마_05_갈색마":0, "che_명마_06_흑색마":0,
|
||||
|
||||
"che_명마_07_백마" : 2, "che_명마_07_기주마" : 2, "che_명마_07_오환마" : 2, "che_명마_07_백상" : 2,
|
||||
"che_명마_08_양주마" : 2, "che_명마_08_흉노마" : 2, "che_명마_09_과하마" : 2, "che_명마_09_의남백마" : 2,
|
||||
"che_명마_10_대완마" : 2, "che_명마_10_옥추마" : 2, "che_명마_11_서량마" : 2, "che_명마_11_화종마" : 2,
|
||||
"che_명마_12_사륜거" : 2, "che_명마_12_옥란백용구": 2, "che_명마_13_절영" : 2, "che_명마_13_적로" : 2,
|
||||
"che_명마_14_적란마" : 2, "che_명마_14_조황비전" : 2, "che_명마_15_한혈마" : 2, "che_명마_15_적토마" : 2
|
||||
},
|
||||
"weapon":{
|
||||
"che_무기_01_단도":0, "che_무기_02_단궁":0, "che_무기_03_단극":0,
|
||||
"che_무기_04_목검":0, "che_무기_05_죽창":0, "che_무기_06_소부":0,
|
||||
|
||||
"che_무기_07_동추":2, "che_무기_07_철편":2, "che_무기_07_철쇄":2, "che_무기_07_맥궁":2,
|
||||
"che_무기_08_유성추":2, "che_무기_08_철질여골":2, "che_무기_09_쌍철극":2, "che_무기_09_동호비궁":2,
|
||||
"che_무기_10_삼첨도":2, "che_무기_10_대부":2, "che_무기_11_고정도":2, "che_무기_11_이광궁":2,
|
||||
"che_무기_12_철척사모":2, "che_무기_12_칠성검":2, "che_무기_13_사모":2, "che_무기_13_양유기궁":2,
|
||||
"che_무기_14_언월도":2, "che_무기_14_방천화극":2, "che_무기_15_청홍검":2, "che_무기_15_의천검":2
|
||||
},
|
||||
"book":{
|
||||
"che_서적_01_효경전":0, "che_서적_02_회남자":0, "che_서적_03_변도론":0,
|
||||
"che_서적_04_건상역주":0, "che_서적_05_여씨춘추":0, "che_서적_06_사민월령":0,
|
||||
|
||||
"che_서적_07_위료자":2, "che_서적_07_사마법":2, "che_서적_07_한서":2, "che_서적_07_논어":2,
|
||||
"che_서적_08_전론":2, "che_서적_08_사기":2, "che_서적_09_장자":2, "che_서적_09_역경":2,
|
||||
"che_서적_10_시경":2, "che_서적_10_구국론":2, "che_서적_11_상군서":2, "che_서적_11_춘추전":2,
|
||||
"che_서적_12_산해경":2, "che_서적_12_맹덕신서":2, "che_서적_13_관자":2, "che_서적_13_병법24편":2,
|
||||
"che_서적_14_한비자":2, "che_서적_14_오자병법":2, "che_서적_15_노자":2, "che_서적_15_손자병법":2
|
||||
},
|
||||
"item":{
|
||||
"che_치료_환약":0, "che_저격_수극":0, "che_사기_탁주":0,
|
||||
"che_훈련_청주":0, "che_계략_이추":0, "che_계략_향낭":0,
|
||||
|
||||
"event_전투특기_격노":0, "event_전투특기_견고":0, "event_전투특기_공성":0, "event_전투특기_궁병":0,
|
||||
"event_전투특기_귀병":0, "event_전투특기_기병":0, "event_전투특기_돌격":0, "event_전투특기_무쌍":0,
|
||||
"event_전투특기_반계":0, "event_전투특기_보병":0, "event_전투특기_신산":0, "event_전투특기_신중":0,
|
||||
"event_전투특기_위압":0, "event_전투특기_의술":0, "event_전투특기_저격":0, "event_전투특기_집중":0,
|
||||
"event_전투특기_징병":0, "event_전투특기_척사":0, "event_전투특기_필살":0, "event_전투특기_환술":0
|
||||
}
|
||||
}
|
||||
},
|
||||
"events":[
|
||||
[
|
||||
"month", 1000,
|
||||
["or", ["Date", "==", null, 12], ["Date", "==", null, 6]],
|
||||
["CreateManyNPC", 15, 10],
|
||||
["DeleteEvent"]
|
||||
],
|
||||
[
|
||||
"month", 1000,
|
||||
["Date", "==", 181, 1],
|
||||
["RaiseNPCNation"],
|
||||
["DeleteEvent"]
|
||||
],
|
||||
[
|
||||
"month", 999,
|
||||
["Date", "==", 181, 1],
|
||||
["OpenNationBetting", 4, 5000],
|
||||
["OpenNationBetting", 1, 2000],
|
||||
["DeleteEvent"]
|
||||
],
|
||||
[
|
||||
"month", 1000,
|
||||
["Date", "==", null, 1],
|
||||
["ChangeCity", "all", {
|
||||
"trade":100
|
||||
}]
|
||||
],
|
||||
[
|
||||
"month", 1000,
|
||||
["Date", "==", null, 7],
|
||||
["ChangeCity", "all", {
|
||||
"trade":100
|
||||
}]
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
{
|
||||
"title": "【공백지】 100기 올스타 클래식",
|
||||
"startYear": 180,
|
||||
"map": {
|
||||
"mapName": "miniche",
|
||||
"targetGeneralPool": "SPoolUnderU100",
|
||||
"generalPoolAllowOption": ["stat", "ego", "picture"],
|
||||
"centennialNpcDexTargetRatio": 0.4
|
||||
},
|
||||
"history": [
|
||||
"<C>●</>180년 1월:<L><b>【100기 이벤트】</b></> 역대 장수들이 평범한 능력으로 다시 모여, 지난 전성기의 힘과 서서히 동조하기 시작했다!"
|
||||
],
|
||||
"const": {
|
||||
"npcBanMessageProb":0.005,
|
||||
"defaultMaxGeneral": 800,
|
||||
"uniqueTrialCoef": 2
|
||||
},
|
||||
"events": [
|
||||
[
|
||||
"month", 8000,
|
||||
true,
|
||||
["AdvanceCentennialAllStar"]
|
||||
],
|
||||
[
|
||||
"month", 1000,
|
||||
["Date", "==", null, 12],
|
||||
["CreateManyNPC", 100, 0],
|
||||
["DeleteEvent"]
|
||||
],
|
||||
[
|
||||
"month", 1000,
|
||||
["Date", "==", 181, 1],
|
||||
["RaiseNPCNation"],
|
||||
["DeleteEvent"]
|
||||
],
|
||||
[
|
||||
"destroy_nation", 1000,
|
||||
["and",
|
||||
["Date", ">=", 183, 1],
|
||||
["RemainNation", "==", 1]
|
||||
],
|
||||
["BlockScoutAction"],
|
||||
["DeleteEvent"]
|
||||
],
|
||||
[
|
||||
"month", 999,
|
||||
["Date", "==", 181, 1],
|
||||
["OpenNationBetting", 4, 5000],
|
||||
["OpenNationBetting", 1, 2000],
|
||||
["DeleteEvent"]
|
||||
],
|
||||
[
|
||||
"month", 999,
|
||||
["and",
|
||||
["Date", ">=", 183, 1],
|
||||
["RemainNation", "<=", 8]
|
||||
],
|
||||
["OpenNationBetting", 1, 1000],
|
||||
["DeleteEvent"]
|
||||
],
|
||||
[
|
||||
"destroy_nation", 1000,
|
||||
["and",
|
||||
["Date", ">=", 183, 1],
|
||||
["RemainNation", "==", 1]
|
||||
],
|
||||
["BlockScoutAction"],
|
||||
["DeleteEvent"]
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -583,6 +583,20 @@ CREATE TABLE IF NOT EXISTS `nation_env` (
|
||||
UNIQUE INDEX `key` (`namespace`, `key`),
|
||||
CONSTRAINT `json` CHECK (json_valid(`value`))
|
||||
) COLLATE = 'utf8mb4_bin' ENGINE = Aria;
|
||||
CREATE TABLE IF NOT EXISTS `inheritance_result` (
|
||||
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
`server_id` CHAR(20) NOT NULL COLLATE 'utf8mb4_general_ci',
|
||||
`owner` INT(11) NOT NULL,
|
||||
`general_id` INT(11) NOT NULL,
|
||||
`year` INT(11) NOT NULL,
|
||||
`month` INT(11) NOT NULL,
|
||||
`value` LONGTEXT NOT NULL COLLATE 'utf8mb4_bin',
|
||||
PRIMARY KEY (`id`),
|
||||
INDEX `by_owner` (`server_id`, `owner`, `general_id`, `year`, `month`)
|
||||
)
|
||||
COLLATE='utf8mb4_general_ci'
|
||||
ENGINE=Aria
|
||||
;
|
||||
|
||||
##############################
|
||||
## 명장일람
|
||||
|
||||
@@ -172,7 +172,7 @@
|
||||
<BButton class="col-6 offset-6" variant="primary" @click="checkOwner"> 소유자 찾기 </BButton>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col col-lg-4 col-sm-6 col-12 py-2">
|
||||
<div v-if="canResetStat" class="col col-lg-4 col-sm-6 col-12 py-2">
|
||||
<div class="row px-4">
|
||||
<div class="a-right col-6 align-self-center">능력치 초기화</div>
|
||||
<div class="col-6">
|
||||
@@ -222,6 +222,17 @@
|
||||
<BButton class="col-6 offset-6" variant="primary" @click="resetStat"> 능력치 초기화</BButton>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="col col-lg-4 col-sm-6 col-12 py-2">
|
||||
<div class="row px-4">
|
||||
<div class="a-right col-6 align-self-center">능력치 초기화</div>
|
||||
<div class="col-6 align-self-center">사용 불가</div>
|
||||
</div>
|
||||
<div class="a-right">
|
||||
<small class="form-text text-muted">
|
||||
100기 올스타 장수는 장수 전환 시 능력치 성장 기록을 보존하기 위해 능력치 초기화를 사용할 수 없습니다.
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
@@ -297,6 +308,7 @@ declare const staticValues: {
|
||||
}
|
||||
>;
|
||||
availableTargetGeneral: Record<number, string>;
|
||||
canResetStat: boolean;
|
||||
currentStat: {
|
||||
leadership: number;
|
||||
strength: number;
|
||||
@@ -447,6 +459,7 @@ const {
|
||||
availableSpecialWar,
|
||||
availableUnique,
|
||||
availableTargetGeneral,
|
||||
canResetStat,
|
||||
currentStat
|
||||
} = staticValues;
|
||||
|
||||
@@ -739,4 +752,4 @@ async function getMoreLog(): Promise<void> {
|
||||
.tnum {
|
||||
font-feature-settings: "tnum";
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -510,7 +510,7 @@ async function reloadCommandList() {
|
||||
const turnTime = parseTime(result.turnTime);
|
||||
let nextTurnTime = new Date(turnTime);
|
||||
|
||||
const autorunLimitYearMonth = result.autorun_limit ?? yearMonth - 1;
|
||||
const autorunLimitYearMonth = (result.autorun_limit ?? yearMonth) - 1;
|
||||
const [autorunLimitYear, autorunLimitMonth] = parseYearMonth(autorunLimitYearMonth);
|
||||
|
||||
reservedCommandList.value = [];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { AutoLoginFailed, AutoLoginNonceResponse, AutoLoginResponse, LoginFailed, LoginResponse } from "./defs/API/Login";
|
||||
import { APIPathGen } from "./util/APIPathGen";
|
||||
import { callSammoAPI, extractHttpMethod, GET, POST, type APICallT, type APITail, type InvalidResponse, type RawArgType, type ValidResponse } from "./util/callSammoAPI";
|
||||
import { callSammoAPI, extractHttpMethod, POST, type APICallT, type APITail, type InvalidResponse, type RawArgType, type ValidResponse } from "./util/callSammoAPI";
|
||||
export type { ValidResponse, InvalidResponse };
|
||||
|
||||
const apiRealPath = {
|
||||
@@ -18,7 +18,7 @@ const apiRealPath = {
|
||||
hashedToken: string,
|
||||
token_id: number,
|
||||
}, AutoLoginResponse, AutoLoginFailed>,
|
||||
ReqNonce: GET as APICallT<undefined, AutoLoginNonceResponse, AutoLoginFailed>
|
||||
ReqNonce: POST as APICallT<undefined, AutoLoginNonceResponse, AutoLoginFailed>
|
||||
},
|
||||
} as const;
|
||||
|
||||
@@ -30,4 +30,4 @@ export const SammoRootAPI = APIPathGen(apiRealPath, (path: string[], tail: APITa
|
||||
}
|
||||
return callSammoAPI(method, path.join('/'), args, pathParam);
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
@@ -509,7 +509,7 @@ function updateCommandList() {
|
||||
const turnTime = parseTime(props.turnTime);
|
||||
let nextTurnTime = new Date(turnTime);
|
||||
|
||||
const autorunLimitYearMonth = autorun_limit.value ?? yearMonth - 1;
|
||||
const autorunLimitYearMonth = (autorun_limit.value ?? yearMonth) - 1;
|
||||
const [autorunLimitYear, autorunLimitMonth] = parseYearMonth(autorunLimitYearMonth);
|
||||
|
||||
for (const obj of props.turn) {
|
||||
|
||||
@@ -170,7 +170,7 @@ import { joinYearMonth } from "@/util/joinYearMonth";
|
||||
import { parseYearMonth } from "@/util/parseYearMonth";
|
||||
import type { GameConstStore } from "@/GameConstStore";
|
||||
import { unwrap_err } from "@/util/unwrap_err";
|
||||
import { getMaxRelativeTechLevel, TECH_LEVEL_YEAR_GAP } from "@/utilGame/techLevel";
|
||||
import { getMaxRelativeTechLevel } from "@/utilGame/techLevel";
|
||||
import { deviceType } from "detect-it";
|
||||
import MapCityBasic from "./MapCityBasic.vue";
|
||||
import MapCityDetail from "./MapCityDetail.vue";
|
||||
@@ -289,12 +289,14 @@ const titleTooltip = computed(()=>{
|
||||
const { startYear, year } = mapData.value;
|
||||
|
||||
const maxTechLevel = gameConstStore.value.gameConst.maxTechLevel;
|
||||
const currentTechLimit = getMaxRelativeTechLevel(startYear, year, maxTechLevel);
|
||||
const initialAllowedTechLevel = gameConstStore.value.gameConst.initialAllowedTechLevel;
|
||||
const techLevelIncYear = gameConstStore.value.gameConst.techLevelIncYear;
|
||||
const currentTechLimit = getMaxRelativeTechLevel(startYear, year, maxTechLevel, initialAllowedTechLevel, techLevelIncYear);
|
||||
|
||||
if (currentTechLimit == maxTechLevel) {
|
||||
result.push(`기술등급 제한 : ${currentTechLimit}등급 (최종)`);
|
||||
} else {
|
||||
const nextTechLimitYear = currentTechLimit * TECH_LEVEL_YEAR_GAP + startYear;
|
||||
const nextTechLimitYear = currentTechLimit * techLevelIncYear + startYear;
|
||||
result.push(`기술등급 제한 : ${currentTechLimit}등급 (${nextTechLimitYear}년 해제)`);
|
||||
}
|
||||
|
||||
|
||||
@@ -124,9 +124,12 @@ watch(
|
||||
(nation) => {
|
||||
const { startyear, year } = global.value;
|
||||
console.log(gameConstStore);
|
||||
maxTechLevel.value = getMaxRelativeTechLevel(startyear, year, gameConstStore.value.gameConst.maxTechLevel);
|
||||
|
||||
const initialAllowedTechLevel = gameConstStore.value.gameConst.initialAllowedTechLevel;
|
||||
const techLevelIncYear = gameConstStore.value.gameConst.techLevelIncYear;
|
||||
maxTechLevel.value = getMaxRelativeTechLevel(startyear, year, gameConstStore.value.gameConst.maxTechLevel, initialAllowedTechLevel, techLevelIncYear);
|
||||
currentTechLevel.value = convTechLevel(nation.tech, maxTechLevel.value);
|
||||
onTechLimit.value = isTechLimited(startyear, year, nation.tech, gameConstStore.value.gameConst.maxTechLevel);
|
||||
onTechLimit.value = isTechLimited(startyear, year, nation.tech, gameConstStore.value.gameConst.maxTechLevel, initialAllowedTechLevel, techLevelIncYear);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
@@ -57,6 +57,9 @@ export type GameConstType = {
|
||||
maxTechLevel: number;
|
||||
maxBetrayCnt: number;
|
||||
|
||||
techLevelIncYear: number;
|
||||
initialAllowedTechLevel: number;
|
||||
|
||||
basePopIncreaseAmount: number;
|
||||
expandCityPopIncreaseAmount: number;
|
||||
expandCityDevelIncreaseAmount: number;
|
||||
|
||||
+70
-55
@@ -37,14 +37,16 @@ function regNextToken(tokenInfo: [number, string]) {
|
||||
function getToken(): [number, string] | undefined {
|
||||
const trialToken = localStorage.getItem(LOGIN_TOKEN_KEY);
|
||||
if (!trialToken) {
|
||||
console.log('no token');
|
||||
return;
|
||||
}
|
||||
const tokenItems = JSON.parse(trialToken) as [number, [number, string], string];
|
||||
const tokenItems = JSON.parse(trialToken) as [number, [number, string], number];
|
||||
if (tokenItems[0] != TOKEN_VERSION) {
|
||||
console.log(tokenItems);
|
||||
resetToken();
|
||||
return;
|
||||
}
|
||||
console.debug(localStorage.getItem(LOGIN_TOKEN_KEY));
|
||||
const [, token,] = tokenItems;
|
||||
return token;
|
||||
}
|
||||
@@ -53,60 +55,73 @@ function resetToken() {
|
||||
localStorage.removeItem(LOGIN_TOKEN_KEY);
|
||||
}
|
||||
|
||||
async function tryAutoLogin() {
|
||||
try {
|
||||
const tokenInfo = getToken();
|
||||
if (!tokenInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [tokenID, token] = tokenInfo;
|
||||
|
||||
const result = await SammoRootAPI.Login.ReqNonce(undefined, true);
|
||||
|
||||
if (!result) {
|
||||
//api 에러.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result.result) {
|
||||
resetToken();
|
||||
return;
|
||||
}
|
||||
|
||||
const nonce = result.loginNonce;
|
||||
|
||||
const hashedToken = sha512(token + nonce);
|
||||
const loginResult = await SammoRootAPI.Login.LoginByToken({
|
||||
'hashedToken': hashedToken,
|
||||
'token_id': tokenID,
|
||||
}, true);
|
||||
|
||||
if (!loginResult.result) {
|
||||
if (!loginResult.silent) {
|
||||
alert(loginResult.reason);
|
||||
}
|
||||
console.error(loginResult.reason);
|
||||
return;
|
||||
}
|
||||
|
||||
if (loginResult.nextToken) {
|
||||
regNextToken(loginResult.nextToken);
|
||||
}
|
||||
window.location.href = "./";
|
||||
|
||||
}
|
||||
catch (e) {
|
||||
if (isString(e)) {
|
||||
alert(e);
|
||||
}
|
||||
console.error(e);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
async function tryAutoLogin() {
|
||||
try {
|
||||
const tokenInfo = getToken();
|
||||
if (!tokenInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [tokenID, token] = tokenInfo;
|
||||
|
||||
for (let attempt = 0; attempt < 2; attempt++) {
|
||||
const reqNonceStartAt = Date.now();
|
||||
const nonceResult = await SammoRootAPI.Login.ReqNonce(undefined, true);
|
||||
|
||||
if (!nonceResult) {
|
||||
//api 에러.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!nonceResult.result) {
|
||||
resetToken();
|
||||
return;
|
||||
}
|
||||
|
||||
const nonce = nonceResult.loginNonce;
|
||||
console.debug(
|
||||
'try auto login with token',
|
||||
tokenID,
|
||||
`attempt:${attempt + 1}`,
|
||||
`reqNonceElapsed:${Date.now() - reqNonceStartAt}ms`
|
||||
);
|
||||
|
||||
const hashedToken = sha512(token + nonce);
|
||||
const loginResult = await SammoRootAPI.Login.LoginByToken({
|
||||
'hashedToken': hashedToken,
|
||||
'token_id': tokenID,
|
||||
}, true);
|
||||
|
||||
if (!loginResult.result) {
|
||||
if (loginResult.reason === '자동 로그인: 절차 오류' && attempt === 0) {
|
||||
console.warn('auto login failed by procedure error. retrying once.');
|
||||
await delay(150);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!loginResult.silent) {
|
||||
alert(loginResult.reason);
|
||||
}
|
||||
console.error(loginResult.reason);
|
||||
return;
|
||||
}
|
||||
|
||||
if (loginResult.nextToken) {
|
||||
regNextToken(loginResult.nextToken);
|
||||
}
|
||||
window.location.href = "./";
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
if (isString(e)) {
|
||||
alert(e);
|
||||
}
|
||||
console.error(e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function getOAuthToken(mode: string, scope_list?: string[] | string) {
|
||||
if (mode === undefined) {
|
||||
mode = 'login';
|
||||
|
||||
@@ -10,7 +10,7 @@ import { unwrap } from '@util/unwrap';
|
||||
import { TemplateEngine } from '@util/TemplateEngine';
|
||||
import { Tooltip } from 'bootstrap';
|
||||
import { trim } from 'lodash-es';
|
||||
type CardItem = {
|
||||
type CardItem = {
|
||||
uniqueName: string,
|
||||
|
||||
imgsvr: 0|1,
|
||||
@@ -26,9 +26,18 @@ type CardItem = {
|
||||
specialWarInfo?: string,
|
||||
specialWarText?: string,
|
||||
|
||||
personal?: string,
|
||||
personalText?: string,
|
||||
}
|
||||
personal?: string,
|
||||
personalText?: string,
|
||||
event100Growth?: boolean,
|
||||
leadership?: number,
|
||||
strength?: number,
|
||||
intel?: number,
|
||||
selectionStatLabel?: string,
|
||||
selectionLeadership?: number,
|
||||
selectionStrength?: number,
|
||||
selectionIntel?: number,
|
||||
dex?: number[],
|
||||
}
|
||||
|
||||
type GeneralPoolResponse = {
|
||||
result: true,
|
||||
@@ -37,17 +46,23 @@ 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[];
|
||||
|
||||
const templateGeneralCard = '<div class="general_card">\
|
||||
<h4 class="bg1 with_border"><%generalName%></h4>\
|
||||
<h4><img src="<%iconPath%>" height=64 width=64></h4><p>\
|
||||
<%if(leadership){%>\
|
||||
<%leadership%> / <%strength%> / <%intel%><br>\
|
||||
<%}%>\
|
||||
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>역사 장수 최종 목표</b><br>능력치·기본 숙련 195년 / 추가 숙련 205년 동조<br><%}%>\
|
||||
<%if(leadership){%>\
|
||||
<%leadership%> / <%strength%> / <%intel%><br>\
|
||||
<%}%>\
|
||||
<%if(selectionStatLabel){%>\
|
||||
<b><%selectionStatLabel%></b><br>\
|
||||
<%selectionLeadership%> / <%selectionStrength%> / <%selectionIntel%><br>\
|
||||
<%}%>\
|
||||
<%if(personalText){%><%personalText%><br><%}%>\
|
||||
<%if(specialDomesticText||specialWarText){%>\
|
||||
<%specialDomesticText%> / <%specialWarText%><br>\
|
||||
@@ -101,9 +116,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) {
|
||||
@@ -132,19 +150,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>($('#leadership').val())),
|
||||
intel: parseInt(unwrap_any<string>($('#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;
|
||||
@@ -190,9 +214,13 @@ function printGenerals(value: GeneralPoolResponse) {
|
||||
|
||||
const emptyCard = {
|
||||
'leadership': null,
|
||||
'strength': null,
|
||||
'intel': null,
|
||||
'personalText': null,
|
||||
'strength': null,
|
||||
'intel': null,
|
||||
'selectionStatLabel': null,
|
||||
'selectionLeadership': null,
|
||||
'selectionStrength': null,
|
||||
'selectionIntel': null,
|
||||
'personalText': null,
|
||||
'specialDomesticText': null,
|
||||
'specialWarText': null,
|
||||
'dex': null
|
||||
@@ -287,4 +315,4 @@ $(async function ($) {
|
||||
$('.custom_stat').show();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
|
||||
import { clamp } from "lodash-es";
|
||||
|
||||
export const TECH_LEVEL_YEAR_GAP = 5;
|
||||
export const TECH_LEVEL_STEP = 1000;
|
||||
|
||||
export function isTechLimited(startYear: number, year: number, tech: number, maxTechLevel: number): boolean {
|
||||
const relMaxTech = getMaxRelativeTechLevel(startYear, year, maxTechLevel);
|
||||
export function isTechLimited(startYear: number, year: number, tech: number, maxTechLevel: number, initialAllowedTechLevel: number, techLevelIncYear: number): boolean {
|
||||
const relMaxTech = getMaxRelativeTechLevel(startYear, year, maxTechLevel, initialAllowedTechLevel, techLevelIncYear);
|
||||
const techLevel = convTechLevel(tech, maxTechLevel);
|
||||
|
||||
return techLevel >= relMaxTech;
|
||||
@@ -16,7 +15,7 @@ export function convTechLevel(tech: number, maxTechLevel: number): number{
|
||||
}
|
||||
|
||||
|
||||
export function getMaxRelativeTechLevel(startYear: number, year: number, maxTechLevel: number): number {
|
||||
export function getMaxRelativeTechLevel(startYear: number, year: number, maxTechLevel: number, initialAllowedTechLevel: number, techLevelIncYear: number): number {
|
||||
const relYear = year - startYear;
|
||||
return clamp(Math.floor(relYear / TECH_LEVEL_YEAR_GAP) + 1, 1, maxTechLevel);
|
||||
return clamp(Math.floor(relYear / techLevelIncYear) + initialAllowedTechLevel, 1, maxTechLevel);
|
||||
}
|
||||
@@ -105,6 +105,7 @@ $lastInheritPointLogs = $db->query('SELECT id, server_id, year, month, date, tex
|
||||
'availableUnique' => $availableUnique,
|
||||
'lastInheritPointLogs' => $lastInheritPointLogs,
|
||||
'availableTargetGeneral' => $availableTargetGeneral,
|
||||
'canResetStat' => CentennialAllStarGrowthService::isStatResetAllowed(),
|
||||
'currentStat' => [
|
||||
'leadership' => Util::clamp($me->getVar('leadership'), GameConst::$defaultStatMin, GameConst::$defaultStatMax),
|
||||
'strength' => Util::clamp($me->getVar('strength'), GameConst::$defaultStatMin, GameConst::$defaultStatMax),
|
||||
@@ -120,4 +121,4 @@ $lastInheritPointLogs = $db->query('SELECT id, server_id, year, month, date, tex
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -42,7 +42,7 @@ if($userInfo['delete_after']){
|
||||
|
||||
$penaltyList = Json::decode($userInfo['penalty']??'{}');
|
||||
foreach($penaltyList as $penaltyKey=>$penaltyValue){
|
||||
if($penaltyValue['expire'] ?? 0 > TimeUtil::now()){
|
||||
if(($penaltyValue['expire'] ?? 0) > TimeUtil::now()){
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reason'=>"징계가 남아있어 탈퇴할 수 없습니다."
|
||||
|
||||
+2
-2
@@ -174,7 +174,7 @@ function tryNpmInstall()
|
||||
|
||||
tryComposerInstall();
|
||||
if (tryNpmInstall()) {
|
||||
genJS(Util::array_last_key(ServConfig::getServerList()));
|
||||
genJS(array_key_last(ServConfig::getServerList()));
|
||||
}
|
||||
$session = Session::requireLogin(null)->setReadOnly();
|
||||
|
||||
@@ -227,7 +227,7 @@ if (!$allowFullUpdate || !$target) {
|
||||
$target = $request['target'];
|
||||
}
|
||||
|
||||
$baseServerName = Util::array_last_key(ServConfig::getServerList());
|
||||
$baseServerName = array_key_last(ServConfig::getServerList());
|
||||
|
||||
if (!$target && $server != $baseServerName) {
|
||||
Json::die([
|
||||
|
||||
@@ -109,16 +109,16 @@ if($expires < $nowDate){
|
||||
$access_token = $result['access_token'];
|
||||
$expires = TimeUtil::nowAddSeconds($result['expires_in']);
|
||||
if(isset($result['refresh_token'])){
|
||||
$refresh_token = Util::array_get($result['refresh_token']);
|
||||
$refresh_token = $result['refresh_token'] ?? null;
|
||||
$refresh_token_expires = TimeUtil::nowAddSeconds($result['refresh_token_expires_in']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$signupResult = $restAPI->signup();
|
||||
$kakaoID = Util::array_get($signupResult['id']);
|
||||
$kakaoID = $signupResult['id'] ?? null;
|
||||
|
||||
if(!$kakaoID && Util::array_get($signupResult['msg'])!='already registered'){
|
||||
if(!$kakaoID && ($signupResult['msg'] ?? null) !='already registered'){
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reason'=>'카카오 로그인 과정 중 앱 연결 절차를 실패했습니다'.Json::encode($signupResult)
|
||||
@@ -126,7 +126,7 @@ if(!$kakaoID && Util::array_get($signupResult['msg'])!='already registered'){
|
||||
}
|
||||
|
||||
$me = $restAPI->meWithEmail();
|
||||
$me['code'] = Util::array_get($me['code'], 0);
|
||||
$me['code'] = $me['code'] ?? 0;
|
||||
$kakao_account = $me['kakao_account']??[];
|
||||
if ($me['code']< 0) {
|
||||
$restAPI->unlink();
|
||||
|
||||
@@ -133,7 +133,7 @@ $userInfo = $RootDB->queryFirstRow(
|
||||
if (!$userInfo) {
|
||||
if(!$oauthID){
|
||||
$me = $restAPI->meWithEmail();
|
||||
if ($me['code']??0 < 0) {
|
||||
if (($me['code']??0) < 0) {
|
||||
Json::die([
|
||||
'result' => false,
|
||||
'reqOTP' => false,
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
"main": "js/index.js",
|
||||
"scripts": {
|
||||
"test": "npm-run-all test-php-gateway test-ts",
|
||||
"test-php-gateway": "vendor/bin/phpunit --bootstrap vendor/autoload.php tests",
|
||||
"test-php-gateway": "vendor/bin/phpunit --do-not-cache-result --bootstrap vendor/autoload.php tests",
|
||||
"test-ts": "mocha",
|
||||
"build": "webpack",
|
||||
"buildDev": "webpack --mode=development",
|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Build the static 100th-season all-star pool from a tab-separated query result.
|
||||
*
|
||||
* Usage:
|
||||
* mariadb --batch --raw --skip-column-names DATABASE \
|
||||
* < src/centennial_allstar_candidates.sql \
|
||||
* | php src/build_centennial_allstar_pool.php OUTPUT.json
|
||||
*/
|
||||
|
||||
const OUTPUT_COLUMNS = [
|
||||
'generalName',
|
||||
'leadership',
|
||||
'strength',
|
||||
'intel',
|
||||
'specialDomestic',
|
||||
'dex',
|
||||
'imgsvr',
|
||||
'picture',
|
||||
'sourcePhase',
|
||||
'sourceServerId',
|
||||
'sourceGeneralNo',
|
||||
'selectionReasons',
|
||||
];
|
||||
|
||||
const EXCLUDED_EVENT_PHASES = [
|
||||
5, 10, 15, 20, 25, 30, 35, 40, 45, 50,
|
||||
55, 60, 65, 70, 75, 80, 85, 90, 95,
|
||||
];
|
||||
|
||||
const LEGACY_SPECIAL_WAR_MAP = [
|
||||
40 => 'che_event_귀병',
|
||||
41 => 'che_event_신산',
|
||||
42 => 'che_event_환술',
|
||||
43 => 'che_event_집중',
|
||||
44 => 'che_event_신중',
|
||||
45 => 'che_event_반계',
|
||||
50 => 'che_event_보병',
|
||||
51 => 'che_event_궁병',
|
||||
52 => 'che_event_기병',
|
||||
53 => 'che_event_공성',
|
||||
60 => 'che_event_돌격',
|
||||
61 => 'che_event_무쌍',
|
||||
62 => 'che_event_견고',
|
||||
63 => 'che_event_위압',
|
||||
70 => 'che_event_저격',
|
||||
71 => 'che_event_필살',
|
||||
72 => 'che_event_징병',
|
||||
73 => 'che_event_의술',
|
||||
74 => 'che_event_격노',
|
||||
75 => 'che_event_척사',
|
||||
];
|
||||
|
||||
function fail(string $message): never
|
||||
{
|
||||
fwrite(STDERR, "error: {$message}\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
function firstInt(array $data, array $keys): int
|
||||
{
|
||||
foreach ($keys as $key) {
|
||||
if (array_key_exists($key, $data) && is_numeric($data[$key])) {
|
||||
return (int) $data[$key];
|
||||
}
|
||||
}
|
||||
fail('missing numeric field: ' . implode(' or ', $keys));
|
||||
}
|
||||
|
||||
function normalizeEventSpecial(mixed $rawSpecial): ?string
|
||||
{
|
||||
if (is_numeric($rawSpecial)) {
|
||||
return LEGACY_SPECIAL_WAR_MAP[(int) $rawSpecial] ?? null;
|
||||
}
|
||||
if (
|
||||
!is_string($rawSpecial)
|
||||
|| $rawSpecial === ''
|
||||
|| $rawSpecial === '0'
|
||||
|| strcasecmp($rawSpecial, 'none') === 0
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
if (str_starts_with($rawSpecial, 'che_event_')) {
|
||||
return $rawSpecial;
|
||||
}
|
||||
if (str_starts_with($rawSpecial, 'che_')) {
|
||||
return 'che_event_' . substr($rawSpecial, strlen('che_'));
|
||||
}
|
||||
fail("unknown historical war special: {$rawSpecial}");
|
||||
}
|
||||
|
||||
function decodeSourceRow(string $line, int $lineNo): array
|
||||
{
|
||||
$fields = explode("\t", rtrim($line, "\r\n"), 6);
|
||||
if (count($fields) !== 6) {
|
||||
fail("line {$lineNo}: expected 6 tab-separated fields, got " . count($fields));
|
||||
}
|
||||
|
||||
[$phase, $serverId, $generalNo, $name, $rawData, $rawReasons] = $fields;
|
||||
$data = json_decode($rawData, true, 512, JSON_THROW_ON_ERROR);
|
||||
if (!is_array($data)) {
|
||||
fail("line {$lineNo}: general data is not an object");
|
||||
}
|
||||
|
||||
$phaseNo = (int) $phase;
|
||||
$sourceGeneralNo = (int) $generalNo;
|
||||
if ($phaseNo < 1 || $phaseNo > 99 || $sourceGeneralNo <= 2) {
|
||||
fail("line {$lineNo}: invalid phase/general number");
|
||||
}
|
||||
|
||||
$sourceName = trim($name);
|
||||
if ($sourceName === '') {
|
||||
fail("line {$lineNo}: empty general name");
|
||||
}
|
||||
$generalName = sprintf('%d·%s', $phaseNo, $sourceName);
|
||||
if (mb_strlen($generalName) > 32) {
|
||||
fail("line {$lineNo}: generated name exceeds 32 characters: {$generalName}");
|
||||
}
|
||||
|
||||
$dex = [
|
||||
firstInt($data, ['dex1', 'dex0']),
|
||||
firstInt($data, ['dex2', 'dex10']),
|
||||
firstInt($data, ['dex3', 'dex20']),
|
||||
firstInt($data, ['dex4', 'dex30']),
|
||||
firstInt($data, ['dex5', 'dex40']),
|
||||
];
|
||||
foreach ($dex as $value) {
|
||||
if ($value < 0) {
|
||||
fail("line {$lineNo}: negative dex value");
|
||||
}
|
||||
}
|
||||
|
||||
$reasons = $rawReasons === '' ? [] : explode(',', $rawReasons);
|
||||
sort($reasons, SORT_STRING);
|
||||
|
||||
return [
|
||||
$generalName,
|
||||
firstInt($data, ['leadership', 'leader']),
|
||||
firstInt($data, ['strength', 'power']),
|
||||
firstInt($data, ['intel']),
|
||||
normalizeEventSpecial($data['special2'] ?? null),
|
||||
$dex,
|
||||
firstInt($data, ['imgsvr']),
|
||||
is_string($data['picture'] ?? null) && $data['picture'] !== ''
|
||||
? $data['picture']
|
||||
: 'default.jpg',
|
||||
$phaseNo,
|
||||
$serverId,
|
||||
$sourceGeneralNo,
|
||||
$reasons,
|
||||
];
|
||||
}
|
||||
|
||||
if ($argc !== 2) {
|
||||
fail('usage: php build_centennial_allstar_pool.php OUTPUT.json');
|
||||
}
|
||||
|
||||
$outputPath = $argv[1];
|
||||
$rows = [];
|
||||
$seenSources = [];
|
||||
$nameIndexes = [];
|
||||
$phaseCounts = [];
|
||||
$chiefCounts = [];
|
||||
$reasonCounts = [];
|
||||
$lineNo = 0;
|
||||
|
||||
while (($line = fgets(STDIN)) !== false) {
|
||||
$lineNo++;
|
||||
if (trim($line) === '') {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
$row = decodeSourceRow($line, $lineNo);
|
||||
} catch (JsonException $e) {
|
||||
fail("line {$lineNo}: invalid JSON: {$e->getMessage()}");
|
||||
}
|
||||
|
||||
$sourceKey = "{$row[8]}:{$row[10]}";
|
||||
if (isset($seenSources[$sourceKey])) {
|
||||
fail("line {$lineNo}: duplicate source general {$sourceKey}");
|
||||
}
|
||||
$seenSources[$sourceKey] = true;
|
||||
$nameIndexes[$row[0]][] = count($rows);
|
||||
$phaseCounts[$row[8]] = ($phaseCounts[$row[8]] ?? 0) + 1;
|
||||
foreach ($row[11] as $reason) {
|
||||
$reasonGroup = str_starts_with($reason, 'chief:') ? 'chief' : 'hall';
|
||||
$reasonCounts[$reasonGroup] = ($reasonCounts[$reasonGroup] ?? 0) + 1;
|
||||
if ($reasonGroup === 'chief') {
|
||||
$chiefCounts[$row[8]] = ($chiefCounts[$row[8]] ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
$rows[] = $row;
|
||||
}
|
||||
|
||||
if ($rows === []) {
|
||||
fail('no input rows');
|
||||
}
|
||||
foreach ($nameIndexes as $generalName => $indexes) {
|
||||
if (count($indexes) === 1) {
|
||||
continue;
|
||||
}
|
||||
foreach ($indexes as $index) {
|
||||
$rows[$index][0] .= "#{$rows[$index][10]}";
|
||||
if (mb_strlen($rows[$index][0]) > 32) {
|
||||
fail("disambiguated name exceeds 32 characters: {$rows[$index][0]}");
|
||||
}
|
||||
}
|
||||
}
|
||||
ksort($phaseCounts, SORT_NUMERIC);
|
||||
$expectedPhases = array_values(array_diff(range(1, 99), EXCLUDED_EVENT_PHASES));
|
||||
if (array_keys($phaseCounts) !== $expectedPhases) {
|
||||
fail('input does not cover every non-event phase from 1 through 99');
|
||||
}
|
||||
foreach ($expectedPhases as $phase) {
|
||||
if (($chiefCounts[$phase] ?? 0) !== 8) {
|
||||
fail("phase {$phase}: expected 8 unification chiefs including the ruler");
|
||||
}
|
||||
}
|
||||
|
||||
$payload = [
|
||||
'excludedEventPhases' => EXCLUDED_EVENT_PHASES,
|
||||
'columns' => OUTPUT_COLUMNS,
|
||||
'data' => $rows,
|
||||
];
|
||||
$encoded = json_encode(
|
||||
$payload,
|
||||
JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR
|
||||
) . "\n";
|
||||
|
||||
$outputDir = dirname($outputPath);
|
||||
if (!is_dir($outputDir)) {
|
||||
fail("output directory does not exist: {$outputDir}");
|
||||
}
|
||||
$tempPath = tempnam($outputDir, basename($outputPath) . '.tmp.');
|
||||
if ($tempPath === false) {
|
||||
fail("could not create temporary output in {$outputDir}");
|
||||
}
|
||||
if (file_put_contents($tempPath, $encoded) === false || !rename($tempPath, $outputPath)) {
|
||||
@unlink($tempPath);
|
||||
fail("could not write output: {$outputPath}");
|
||||
}
|
||||
|
||||
fwrite(
|
||||
STDERR,
|
||||
sprintf(
|
||||
"wrote %d candidates across phases %d-%d (%d hall reasons, %d chief reasons)\n",
|
||||
count($rows),
|
||||
min(array_keys($phaseCounts)),
|
||||
max(array_keys($phaseCounts)),
|
||||
$reasonCounts['hall'] ?? 0,
|
||||
$reasonCounts['chief'] ?? 0,
|
||||
)
|
||||
);
|
||||
@@ -0,0 +1,131 @@
|
||||
WITH
|
||||
phases AS (
|
||||
SELECT
|
||||
e.no AS phase_no,
|
||||
e.server_id,
|
||||
g.winner_nation,
|
||||
e.l12name,
|
||||
e.l12pic,
|
||||
e.l11name,
|
||||
e.l11pic,
|
||||
e.l10name,
|
||||
e.l10pic,
|
||||
e.l9name,
|
||||
e.l9pic,
|
||||
e.l8name,
|
||||
e.l8pic,
|
||||
e.l7name,
|
||||
e.l7pic,
|
||||
e.l6name,
|
||||
e.l6pic,
|
||||
e.l5name,
|
||||
e.l5pic
|
||||
FROM emperior e
|
||||
LEFT JOIN ng_games g ON g.server_id = e.server_id
|
||||
WHERE e.no BETWEEN 1 AND 99
|
||||
AND MOD(e.no, 5) <> 0
|
||||
),
|
||||
hall_eligible AS (
|
||||
SELECT
|
||||
p.phase_no,
|
||||
h.server_id,
|
||||
h.general_no,
|
||||
h.type,
|
||||
h.value,
|
||||
h.id
|
||||
FROM phases p
|
||||
JOIN hall h ON h.server_id = p.server_id
|
||||
JOIN ng_old_generals og
|
||||
ON og.server_id = h.server_id
|
||||
AND og.general_no = h.general_no
|
||||
WHERE h.general_no > 2
|
||||
AND CAST(COALESCE(JSON_VALUE(og.data, '$.npc'), 0) AS SIGNED) = 0
|
||||
),
|
||||
hall_ranked AS (
|
||||
SELECT
|
||||
phase_no,
|
||||
server_id,
|
||||
general_no,
|
||||
type,
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY server_id, type
|
||||
ORDER BY value DESC, id ASC
|
||||
) AS hall_rank
|
||||
FROM hall_eligible
|
||||
),
|
||||
selection_reasons AS (
|
||||
SELECT
|
||||
phase_no,
|
||||
server_id,
|
||||
general_no,
|
||||
CONCAT('hall:', type) AS reason
|
||||
FROM hall_ranked
|
||||
WHERE hall_rank <= 10
|
||||
),
|
||||
chief_slots AS (
|
||||
SELECT phase_no, server_id, winner_nation, 12 AS officer_level, l12name AS name, l12pic AS picture FROM phases
|
||||
UNION ALL
|
||||
SELECT phase_no, server_id, winner_nation, 11, l11name, l11pic FROM phases
|
||||
UNION ALL
|
||||
SELECT phase_no, server_id, winner_nation, 10, l10name, l10pic FROM phases
|
||||
UNION ALL
|
||||
SELECT phase_no, server_id, winner_nation, 9, l9name, l9pic FROM phases
|
||||
UNION ALL
|
||||
SELECT phase_no, server_id, winner_nation, 8, l8name, l8pic FROM phases
|
||||
UNION ALL
|
||||
SELECT phase_no, server_id, winner_nation, 7, l7name, l7pic FROM phases
|
||||
UNION ALL
|
||||
SELECT phase_no, server_id, winner_nation, 6, l6name, l6pic FROM phases
|
||||
UNION ALL
|
||||
SELECT phase_no, server_id, winner_nation, 5, l5name, l5pic FROM phases
|
||||
),
|
||||
chief_reasons AS (
|
||||
SELECT
|
||||
c.phase_no,
|
||||
c.server_id,
|
||||
og.general_no,
|
||||
CONCAT('chief:', c.officer_level) AS reason
|
||||
FROM chief_slots c
|
||||
JOIN ng_old_generals og
|
||||
ON og.server_id = c.server_id
|
||||
AND og.name = c.name
|
||||
AND SUBSTRING_INDEX(
|
||||
COALESCE(JSON_VALUE(og.data, '$.picture'), ''),
|
||||
'?=',
|
||||
1
|
||||
) = SUBSTRING_INDEX(COALESCE(c.picture, ''), '?=', 1)
|
||||
AND (
|
||||
CAST(COALESCE(JSON_VALUE(og.data, '$.officer_level'), -1) AS SIGNED) = c.officer_level
|
||||
OR (
|
||||
JSON_VALUE(og.data, '$.officer_level') IS NULL
|
||||
AND (
|
||||
c.winner_nation IS NULL
|
||||
OR CAST(JSON_VALUE(og.data, '$.nation') AS SIGNED) = c.winner_nation
|
||||
)
|
||||
)
|
||||
)
|
||||
WHERE og.general_no > 2
|
||||
),
|
||||
all_reasons AS (
|
||||
SELECT phase_no, server_id, general_no, reason FROM selection_reasons
|
||||
UNION ALL
|
||||
SELECT phase_no, server_id, general_no, reason FROM chief_reasons
|
||||
)
|
||||
SELECT
|
||||
r.phase_no,
|
||||
r.server_id,
|
||||
r.general_no,
|
||||
og.name,
|
||||
og.data,
|
||||
GROUP_CONCAT(DISTINCT r.reason ORDER BY r.reason SEPARATOR ',') AS reasons
|
||||
FROM all_reasons r
|
||||
JOIN ng_old_generals og
|
||||
ON og.server_id = r.server_id
|
||||
AND og.general_no = r.general_no
|
||||
GROUP BY
|
||||
r.phase_no,
|
||||
r.server_id,
|
||||
r.general_no,
|
||||
og.name,
|
||||
og.data
|
||||
ORDER BY r.phase_no, r.general_no;
|
||||
+440
@@ -0,0 +1,440 @@
|
||||
#!/usr/bin/env node
|
||||
/* eslint-disable no-console */
|
||||
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { setTimeout as delay } from "node:timers/promises";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
//const __dirname = path.dirname(__filename);
|
||||
|
||||
// ============ 환경설정 ============
|
||||
const REQUIRE_PUBLIC = (process.env.REQUIRE_PUBLIC ?? "true").toLowerCase() === "true";
|
||||
const FETCH_TIMEOUT_MS = nenv("FETCH_TIMEOUT_MS", 60_000);
|
||||
const IF_LOCKED_WAIT_MS = nenv("IF_LOCKED_WAIT_MS", 10_000);
|
||||
const IF_UPDATED_WAIT_MS = nenv("IF_UPDATED_WAIT_MS", 1_000);
|
||||
const DEFAULT_WAIT_MS = nenv("DEFAULT_WAIT_MS", 8_000);
|
||||
const BASE_SCAN_DEPTH_ONE = (process.env.BASE_SCAN_DEPTH_ONE ?? "true").toLowerCase() === "true";
|
||||
const RESCAN_INTERVAL_MS = nenv("RESCAN_INTERVAL_MS", 300_000); // 5분
|
||||
const SHUTDOWN_TIMEOUT_MS = nenv("SHUTDOWN_TIMEOUT_MS", 15_000);
|
||||
const AUTO_RESET_JITTER_MS = nenv("AUTO_RESET_JITTER_MS", 0); // 0초 직후 지연(밀리초)
|
||||
|
||||
function nenv(key: string, def: number): number {
|
||||
const v = process.env[key];
|
||||
if (!v) return def;
|
||||
const num = Number(v);
|
||||
return Number.isFinite(num) ? num : def;
|
||||
}
|
||||
|
||||
// ============ 유틸 ============
|
||||
type Json = Record<string, unknown>;
|
||||
type ProcResult = {
|
||||
result: boolean;
|
||||
updated: boolean;
|
||||
locked: boolean;
|
||||
lastExecuted?: string;
|
||||
};
|
||||
|
||||
class CookieJar {
|
||||
#cookies = new Map<string, string>();
|
||||
|
||||
getCookieHeader(): string | undefined {
|
||||
if (this.#cookies.size === 0) {
|
||||
return undefined;
|
||||
}
|
||||
return [...this.#cookies.entries()].map(([key, value]) => `${key}=${value}`).join("; ");
|
||||
}
|
||||
|
||||
ingestSetCookie(setCookieHeaders: string[]): void {
|
||||
for (const setCookie of setCookieHeaders) {
|
||||
const cookiePart = setCookie.split(";", 1)[0]?.trim();
|
||||
if (!cookiePart) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const eqPos = cookiePart.indexOf("=");
|
||||
if (eqPos <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const key = cookiePart.slice(0, eqPos).trim();
|
||||
const value = cookiePart.slice(eqPos + 1).trim();
|
||||
|
||||
if (!key) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value === "") {
|
||||
this.#cookies.delete(key);
|
||||
continue;
|
||||
}
|
||||
this.#cookies.set(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getSetCookieHeaders(headers: Headers): string[] {
|
||||
const headerObj = headers as unknown as { getSetCookie?: () => string[] };
|
||||
if (typeof headerObj.getSetCookie === "function") {
|
||||
return headerObj.getSetCookie();
|
||||
}
|
||||
|
||||
const fallback = headers.get("set-cookie");
|
||||
if (!fallback) {
|
||||
return [];
|
||||
}
|
||||
return [fallback];
|
||||
}
|
||||
|
||||
function nowStr() {
|
||||
const d = new Date();
|
||||
const pad = (n: number) => n.toString().padStart(2, "0");
|
||||
const ms = d.getMilliseconds().toString().padStart(3, "0");
|
||||
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}.${ms}`;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function log(...args: any[]) { console.log(nowStr(), ...args); }
|
||||
|
||||
async function fileExists(fp: string): Promise<boolean> {
|
||||
try { await fs.access(fp, fs.constants.F_OK); return true; } catch { return false; }
|
||||
}
|
||||
|
||||
async function readFileSafe(fp: string): Promise<string | null> {
|
||||
try { return await fs.readFile(fp, "utf8"); } catch { return null; }
|
||||
}
|
||||
|
||||
async function listDirsOnceAsync(base: string): Promise<string[]> {
|
||||
const out: string[] = [];
|
||||
const names = await fs.readdir(base);
|
||||
for (const name of names) {
|
||||
const p = path.join(base, name);
|
||||
try { if ((await fs.stat(p)).isDirectory()) out.push(p); } catch { /* empty */ }
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function joinUrl(base: string, suffix: string): string {
|
||||
if (base.endsWith("/") && suffix.startsWith("/")) return base + suffix.slice(1);
|
||||
if (!base.endsWith("/") && !suffix.startsWith("/")) return base + "/" + suffix;
|
||||
return base + suffix;
|
||||
}
|
||||
|
||||
// 분 0초 정렬 대기 시간 계산: 지금 시각에서 다음 분의 0초(+지터)까지 남은 ms
|
||||
function msUntilNextMinuteZero(jitterMs = 0): number {
|
||||
const now = Date.now();
|
||||
const nextMinuteStart = Math.ceil(now / 60_000) * 60_000;
|
||||
const target = nextMinuteStart + Math.max(0, jitterMs);
|
||||
return Math.max(0, target - now);
|
||||
}
|
||||
|
||||
// ============ 경로/루트 URL ============
|
||||
const basepath = path.dirname(path.dirname(path.resolve(__filename)));
|
||||
async function parseWebBaseFromCommonPath(base: string): Promise<string> {
|
||||
const jsPath = path.join(base, "d_shared", "common_path.js");
|
||||
const text = await readFileSafe(jsPath);
|
||||
if (!text) { log("[WARN] common_path.js 읽기 실패 → fallback=http://127.0.0.1"); return "http://127.0.0.1"; }
|
||||
for (const line of text.split(/\r?\n/)) {
|
||||
if (line.includes("root:")) {
|
||||
let v = line.slice(line.indexOf("root:") + 5).trim();
|
||||
v = v.replace(/^[\s,'"`]+/, "").replace(/[\s,'"`,]+$/, "");
|
||||
if (v) return v;
|
||||
}
|
||||
}
|
||||
log("[WARN] root: 항목 없음 → fallback=http://127.0.0.1");
|
||||
return "http://127.0.0.1";
|
||||
}
|
||||
const webBase = process.env.PUBLIC_CHECK_URL || await parseWebBaseFromCommonPath(basepath);
|
||||
log("BASEPATH =", basepath);
|
||||
log("WEBBASE =", webBase);
|
||||
|
||||
// ============ 서버 모델 ============
|
||||
type ServerEntry = {
|
||||
name: string; // servRelPath
|
||||
abs: string; // 절대 경로
|
||||
isHidden: () => Promise<boolean>; // .htaccess 존재 여부
|
||||
procUrl: string;
|
||||
autoresetUrl: string;
|
||||
};
|
||||
|
||||
async function scanOnce(): Promise<ServerEntry[]> {
|
||||
const dirs = await listDirsOnceAsync(basepath); // 깊이는 1단계만
|
||||
const out: ServerEntry[] = [];
|
||||
for (const dir of dirs) {
|
||||
if (BASE_SCAN_DEPTH_ONE === false) {
|
||||
// 필요하면 확장 로직 배치
|
||||
}
|
||||
const dbphp = path.join(dir, "d_setting", "DB.php");
|
||||
if (!await fileExists(dbphp)) continue;
|
||||
|
||||
const name = path.relative(basepath, dir);
|
||||
const isHidden = () => fileExists(path.join(dir, ".htaccess"));
|
||||
|
||||
out.push({
|
||||
name,
|
||||
abs: dir,
|
||||
isHidden,
|
||||
procUrl: joinUrl(webBase, `/${name}/proc.php`),
|
||||
autoresetUrl:joinUrl(webBase, `/${name}/j_autoreset.php`),
|
||||
});
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// ============ 공개 접근 체크 ============
|
||||
async function isPublicReachable(signal?: AbortSignal, cookieJar?: CookieJar): Promise<boolean> {
|
||||
if (!REQUIRE_PUBLIC) return true;
|
||||
const ctrl = new AbortController();
|
||||
const timer = setTimeout(() => ctrl.abort(), Math.min(5_000, FETCH_TIMEOUT_MS));
|
||||
const composite = anySignal([ctrl.signal, signal].filter(Boolean) as AbortSignal[]);
|
||||
try {
|
||||
const cookieHeader = cookieJar?.getCookieHeader();
|
||||
const res = await fetch(webBase, {
|
||||
method: "GET",
|
||||
signal: composite,
|
||||
headers: cookieHeader ? { cookie: cookieHeader } : undefined,
|
||||
});
|
||||
cookieJar?.ingestSetCookie(getSetCookieHeaders(res.headers));
|
||||
if (!res.ok) { log(`[PUBLIC] 응답 ${res.status} → 비공개로 간주`); return false; }
|
||||
return true;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (e: any) {
|
||||
if (e?.name !== "AbortError") log(`[PUBLIC] 체크 실패: ${e?.name || e}`);
|
||||
return false;
|
||||
} finally {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
}
|
||||
|
||||
// ============ HTTP ============
|
||||
async function httpGetJson<T extends Json>(url: string, outerSignal?: AbortSignal, cookieJar?: CookieJar): Promise<T | null> {
|
||||
const ctrl = new AbortController();
|
||||
const timer = setTimeout(() => ctrl.abort(), FETCH_TIMEOUT_MS);
|
||||
const signal = anySignal([ctrl.signal, outerSignal].filter(Boolean) as AbortSignal[]);
|
||||
|
||||
const t0 = performance.now();
|
||||
try {
|
||||
const cookieHeader = cookieJar?.getCookieHeader();
|
||||
const res = await fetch(url, {
|
||||
signal,
|
||||
cache: "no-store",
|
||||
headers: cookieHeader ? { cookie: cookieHeader } : undefined,
|
||||
});
|
||||
cookieJar?.ingestSetCookie(getSetCookieHeaders(res.headers));
|
||||
const dt = Math.round(performance.now() - t0);
|
||||
if (!res.ok) { log("HTTPError:", res.status, url); return null; }
|
||||
try {
|
||||
const data = (await res.json()) as T;
|
||||
log(url, `${dt}ms`);
|
||||
return data;
|
||||
} catch {
|
||||
await res.arrayBuffer().catch(() => { /* empty */ });
|
||||
log("ParseError: JSON 변환 실패", url);
|
||||
return null;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (e: any) {
|
||||
if (e?.name !== "AbortError") log(e?.name || "Exception", e?.message || String(e), url);
|
||||
return null;
|
||||
} finally {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
}
|
||||
|
||||
function anySignal(signals: AbortSignal[]): AbortSignal {
|
||||
if (signals.length === 1) return signals[0];
|
||||
const ctrl = new AbortController();
|
||||
for (const s of signals) {
|
||||
if (s.aborted) { ctrl.abort(); break; }
|
||||
s.addEventListener("abort", () => ctrl.abort(), { once: true });
|
||||
}
|
||||
return ctrl.signal;
|
||||
}
|
||||
|
||||
// ============ 서버 러너 ============
|
||||
class ServerRunner {
|
||||
#entry: ServerEntry;
|
||||
#task?: Promise<void>;
|
||||
#stopCtrl = new AbortController();
|
||||
#stopped = false;
|
||||
#lastAutoResetAt = 0;
|
||||
#cookieJar = new CookieJar();
|
||||
|
||||
constructor(entry: ServerEntry) {
|
||||
this.#entry = entry;
|
||||
}
|
||||
|
||||
name() { return this.#entry.name; }
|
||||
|
||||
start() {
|
||||
if (!this.#task) {
|
||||
log(`[${this.name()}] 루프 시작`);
|
||||
this.#task = this.#run();
|
||||
}
|
||||
return this.#task;
|
||||
}
|
||||
|
||||
async stop(): Promise<void> {
|
||||
if (this.#stopped) return;
|
||||
this.#stopped = true;
|
||||
this.#stopCtrl.abort();
|
||||
try { await this.#task; } catch { /* empty */ }
|
||||
log(`[${this.name()}] 루프 종료`);
|
||||
}
|
||||
|
||||
async #run() {
|
||||
while (!this.#stopCtrl.signal.aborted) {
|
||||
try {
|
||||
// 공개 접근 체크
|
||||
const pub = await isPublicReachable(this.#stopCtrl.signal, publicCookieJar);
|
||||
if (!pub) { await delay(15_000, undefined, { signal: this.#stopCtrl.signal }).catch(() => { /* empty */ }); continue; }
|
||||
|
||||
const hidden = await this.#entry.isHidden();
|
||||
|
||||
if (hidden) {
|
||||
// ★ 매 분 0초에 맞춰 호출
|
||||
const waitMs = msUntilNextMinuteZero(AUTO_RESET_JITTER_MS);
|
||||
if (waitMs > 0) {
|
||||
// 신호 안전하게 대기
|
||||
await delay(waitMs, undefined, { signal: this.#stopCtrl.signal }).catch(() => { /* empty */ });
|
||||
if (this.#stopCtrl.signal.aborted) break;
|
||||
}
|
||||
log(`[${this.name()}] 닫힘 - (정렬) autoreset 호출 @ mm:00${AUTO_RESET_JITTER_MS ? `+${AUTO_RESET_JITTER_MS}ms` : ""}`);
|
||||
await httpGetJson<Json>(this.#entry.autoresetUrl, this.#stopCtrl.signal, this.#cookieJar);
|
||||
// 같은 분에 중복 호출 방지: 0초 직후 잠깐 쉬고 다음 루프는 자연스럽게 다음 분으로 정렬됨
|
||||
await delay(200, undefined, { signal: this.#stopCtrl.signal }).catch(() => { /* empty */ });
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// 열림: proc
|
||||
const data = await httpGetJson<ProcResult>(this.#entry.procUrl, this.#stopCtrl.signal, this.#cookieJar);
|
||||
if (!data) {
|
||||
await delay(IF_LOCKED_WAIT_MS, undefined, { signal: this.#stopCtrl.signal }).catch(() => { /* empty */ });
|
||||
continue;
|
||||
}
|
||||
|
||||
if (data.result === false || data.locked === true) {
|
||||
log(`[${this.name()}] result=${data.result} locked=${data.locked} → ${IF_LOCKED_WAIT_MS}ms 대기`);
|
||||
await delay(IF_LOCKED_WAIT_MS, undefined, { signal: this.#stopCtrl.signal }).catch(() => { /* empty */ });
|
||||
continue;
|
||||
}
|
||||
|
||||
if (data.updated === true) {
|
||||
log(`[${this.name()}] updated=true → ${IF_UPDATED_WAIT_MS}ms 후 재호출 (last=${data.lastExecuted ?? "-"})`);
|
||||
await delay(IF_UPDATED_WAIT_MS, undefined, { signal: this.#stopCtrl.signal }).catch(() => { /* empty */ });
|
||||
continue;
|
||||
}
|
||||
|
||||
await delay(DEFAULT_WAIT_MS, undefined, { signal: this.#stopCtrl.signal }).catch(() => { /* empty */ });
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (e: any) {
|
||||
if (this.#stopCtrl.signal.aborted) break;
|
||||
log(`[${this.name()}] 루프 예외:`, e?.message || String(e));
|
||||
await delay(5_000, undefined, { signal: this.#stopCtrl.signal }).catch(() => { /* empty */ });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============ 매니저: 재스캔 & 종료 ============
|
||||
class DaemonManager {
|
||||
#runners = new Map<string, ServerRunner>();
|
||||
#rescanCtrl = new AbortController();
|
||||
|
||||
async start() {
|
||||
log("턴 데몬 시작");
|
||||
await this.#rescanOnce(); // 초기 스캔
|
||||
this.#scheduleRescan(); // 주기적 재스캔
|
||||
}
|
||||
|
||||
async shutdown(timeoutMs = SHUTDOWN_TIMEOUT_MS) {
|
||||
log("[SHUTDOWN] 신호 수신 → 종료 절차 시작");
|
||||
this.#rescanCtrl.abort();
|
||||
|
||||
const stops: Promise<void>[] = [];
|
||||
for (const r of this.#runners.values()) stops.push(r.stop());
|
||||
|
||||
// 타임아웃 대기
|
||||
const done = Promise.allSettled(stops);
|
||||
const timeout = delay(timeoutMs).then(() => { throw new Error("Shutdown timeout"); });
|
||||
try { await Promise.race([done, timeout]); }
|
||||
catch { log("[SHUTDOWN] 타임아웃 도달 → 강제 종료"); }
|
||||
log("[SHUTDOWN] 종료 완료");
|
||||
}
|
||||
|
||||
#scheduleRescan() {
|
||||
// 반복 타이머(AbortSignal과 함께)
|
||||
const loop = async () => {
|
||||
while (!this.#rescanCtrl.signal.aborted) {
|
||||
await delay(RESCAN_INTERVAL_MS, undefined, { signal: this.#rescanCtrl.signal }).catch(() => { /* empty */ });
|
||||
if (this.#rescanCtrl.signal.aborted) break;
|
||||
try { await this.#rescanOnce(); }
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
catch (e: any) { log("[RESCAN] 예외:", e?.message || String(e)); }
|
||||
}
|
||||
};
|
||||
loop().catch(() => { /* empty */ });
|
||||
}
|
||||
|
||||
async #rescanOnce() {
|
||||
const entries = await scanOnce();
|
||||
log(`[RESCAN] 서버 ${entries.length}개 발견`);
|
||||
|
||||
// 추가/갱신/삭제 판정
|
||||
const seen = new Set<string>();
|
||||
|
||||
for (const e of entries) {
|
||||
seen.add(e.name);
|
||||
if (!this.#runners.has(e.name)) {
|
||||
// 신규 서버
|
||||
const runner = new ServerRunner(e);
|
||||
this.#runners.set(e.name, runner);
|
||||
runner.start().catch(() => { /* empty */ });
|
||||
log(`[RESCAN] 추가: ${e.name}`);
|
||||
} else {
|
||||
// 이미 러너 있음 → 경로/URL 변경 가능성 거의 없지만, 필요시 갱신 로직 여기에
|
||||
}
|
||||
}
|
||||
|
||||
// 사라진 서버 중지
|
||||
for (const name of Array.from(this.#runners.keys())) {
|
||||
if (!seen.has(name)) {
|
||||
log(`[RESCAN] 제거: ${name}`);
|
||||
const r = this.#runners.get(name);
|
||||
if (!r) continue;
|
||||
this.#runners.delete(name);
|
||||
r.stop().catch(() => { /* empty */ });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============ 메인 ============
|
||||
const manager = new DaemonManager();
|
||||
const publicCookieJar = new CookieJar();
|
||||
|
||||
async function main() {
|
||||
// 신호 처리: SIGINT/SIGTERM → graceful shutdown
|
||||
const onSignal = async (sig: NodeJS.Signals) => {
|
||||
log(`[SIGNAL] ${sig} 수신`);
|
||||
// 중복 호출 방지
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
process.off("SIGINT", onSignal as any);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
process.off("SIGTERM", onSignal as any);
|
||||
await manager.shutdown().catch(() => { /* empty */ });
|
||||
process.exit(0);
|
||||
};
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
process.on("SIGINT", onSignal as any);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
process.on("SIGTERM", onSignal as any);
|
||||
|
||||
await manager.start();
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
log("치명적 예외:", e);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
+11
-2
@@ -21,8 +21,17 @@ def run(webPath):
|
||||
print(now.strftime("%Y-%m-%d %H:%M:%S"), webPath, flush=True)
|
||||
startTime = getCurrentMillisecTime()
|
||||
|
||||
obj = urllib.request.urlopen(webPath)
|
||||
obj.read()
|
||||
try:
|
||||
obj = urllib.request.urlopen(webPath, timeout=60)
|
||||
obj.read()
|
||||
except urllib.error.HTTPError as e:
|
||||
print('HTTPError:', e.code, webPath)
|
||||
except urllib.error.URLError as e:
|
||||
print('URLError:', e.reason, webPath)
|
||||
except Exception as e:
|
||||
print('Exception:', str(e), webPath)
|
||||
else:
|
||||
pass
|
||||
|
||||
timeGap = getCurrentMillisecTime() - startTime
|
||||
if timeGap < RETRY_SEC * 1000:
|
||||
|
||||
@@ -13,6 +13,8 @@ use sammo\TimeUtil;
|
||||
use sammo\Util;
|
||||
use sammo\Validator;
|
||||
|
||||
use function sammo\logError;
|
||||
|
||||
class LoginByToken extends LoginByID
|
||||
{
|
||||
static array $sensitiveArgs = ['hashedToken'];
|
||||
|
||||
@@ -73,7 +73,7 @@ class APIHelper
|
||||
$logPath = "{$rootPath}/d_log/{$realYearMonth}_api_log.db";
|
||||
}
|
||||
$logDB = FileDB::db($logPath, __DIR__ . '/../../f_install/sql/api_log.sql');
|
||||
$ip = $_SERVER['REMOTE_ADDR'] ?? 'local';
|
||||
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? 'local';
|
||||
$date = date('Y-m-d H:i:s');
|
||||
|
||||
//NOTE: array_merge([], {})의 상황이 가능함.
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace sammo;
|
||||
|
||||
final class CentennialAllStarGrowth
|
||||
{
|
||||
public const DEFAULT_GROWTH_YEARS = 15;
|
||||
|
||||
public static function progress(
|
||||
int $startYear,
|
||||
int $year,
|
||||
int $month,
|
||||
int $growthYears = self::DEFAULT_GROWTH_YEARS
|
||||
): float {
|
||||
if ($growthYears <= 0) {
|
||||
throw new \InvalidArgumentException('growthYears must be positive');
|
||||
}
|
||||
if ($month < 1 || $month > 12) {
|
||||
throw new \InvalidArgumentException('month must be between 1 and 12');
|
||||
}
|
||||
|
||||
$elapsedMonths = max(0, ($year - $startYear) * 12 + $month - 1);
|
||||
return min(1.0, $elapsedMonths / ($growthYears * 12));
|
||||
}
|
||||
|
||||
public static function statFloor(int $target, int $minimum, float $progress): int
|
||||
{
|
||||
$progress = self::clampProgress($progress);
|
||||
if ($target <= $minimum) {
|
||||
return $target;
|
||||
}
|
||||
return min($target, (int) floor($minimum + ($target - $minimum) * $progress));
|
||||
}
|
||||
|
||||
public static function dexFloor(int $target, float $progress): int
|
||||
{
|
||||
$progress = self::clampProgress($progress);
|
||||
return min($target, (int) floor($target * $progress * $progress));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{value:int, granted:int, delta:int}
|
||||
*/
|
||||
public static function advance(int $current, int $granted, int $floor): array
|
||||
{
|
||||
$delta = max(0, $floor - $current);
|
||||
return [
|
||||
'value' => $current + $delta,
|
||||
'granted' => max(0, $granted) + $delta,
|
||||
'delta' => $delta,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{value:int, granted:int, organic:int}
|
||||
*/
|
||||
public static function replaceTarget(int $current, int $oldGranted, int $newFloor): array
|
||||
{
|
||||
$organic = max(0, $current - max(0, $oldGranted));
|
||||
$value = max($organic, $newFloor);
|
||||
return [
|
||||
'value' => $value,
|
||||
'granted' => $value - $organic,
|
||||
'organic' => $organic,
|
||||
];
|
||||
}
|
||||
|
||||
public static function recordableValue(int $current, int $granted): int
|
||||
{
|
||||
return max(0, $current - max(0, $granted));
|
||||
}
|
||||
|
||||
private static function clampProgress(float $progress): float
|
||||
{
|
||||
return max(0.0, min(1.0, $progress));
|
||||
}
|
||||
}
|
||||
@@ -88,9 +88,9 @@ class Setting
|
||||
$tail = new FileTail($this->versionFile);
|
||||
$version = 'noVersionJson';
|
||||
foreach ($tail->smart(5, 100, true) as $line) {
|
||||
if (Util::starts_with($line, '//{')) {
|
||||
if (str_starts_with($line, '//{')) {
|
||||
$version = Json::decode(substr($line, 2));
|
||||
$version = Util::array_get($version['version'], 'noVersionValue');
|
||||
$version = $version['version'] ?? 'noVersionValue';
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -106,8 +106,8 @@ class Setting
|
||||
$templates = new \League\Plates\Engine(__DIR__.'/templates');
|
||||
//TODO: .htaccess가 서버 오픈에도 사용될 수 있으니 별도의 방법이 필요함
|
||||
$allow_ip = Util::get_client_ip(false);
|
||||
if (Util::starts_with($allow_ip, '192.168.') ||
|
||||
Util::starts_with($allow_ip, '10.')) {
|
||||
if (str_starts_with($allow_ip, '192.168.') ||
|
||||
str_starts_with($allow_ip, '10.')) {
|
||||
//172.16~172.31은 코딩하기 귀찮으니까 안할거다
|
||||
$allow_ip = Util::get_client_ip(true);
|
||||
}
|
||||
|
||||
+89
-1
@@ -3,7 +3,7 @@ namespace sammo;
|
||||
|
||||
use Iterator;
|
||||
|
||||
class Util extends \utilphp\util
|
||||
class Util
|
||||
{
|
||||
|
||||
/**
|
||||
@@ -889,4 +889,92 @@ class Util extends \utilphp\util
|
||||
}
|
||||
return join('|', $result);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* http://github.com/brandonwamboldt/utilphp/ 의 구버전 코드에서 이식함
|
||||
* 일부 코드는 수정
|
||||
* 해당 라이선스: MIT
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns the first element in an array.
|
||||
* PHP 8.5부터 array_first 사용 가능
|
||||
*
|
||||
* @param array $array
|
||||
* @return mixed
|
||||
*/
|
||||
public static function array_first(array $array)
|
||||
{
|
||||
return $array[array_key_first($array)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last element in an array.
|
||||
* PHP 8.5부터 array_last 사용 가능
|
||||
*
|
||||
* @param array $array
|
||||
* @return mixed
|
||||
*/
|
||||
public static function array_last(array $array)
|
||||
{
|
||||
return $array[array_key_last($array)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Flatten a multi-dimensional array into a one dimensional array.
|
||||
*
|
||||
* Contributed by Theodore R. Smith of PHP Experts, Inc. <http://www.phpexperts.pro/>
|
||||
*
|
||||
* @param array $array The array to flatten
|
||||
* @param boolean $preserve_keys Whether or not to preserve array keys.
|
||||
* Keys from deeply nested arrays will
|
||||
* overwrite keys from shallowy nested arrays
|
||||
* @return array
|
||||
*/
|
||||
public static function array_flatten(array $array, $preserve_keys = true)
|
||||
{
|
||||
$flattened = array();
|
||||
|
||||
array_walk_recursive($array, function($value, $key) use (&$flattened, $preserve_keys) {
|
||||
if ($preserve_keys && !is_int($key)) {
|
||||
$flattened[$key] = $value;
|
||||
} else {
|
||||
$flattened[] = $value;
|
||||
}
|
||||
});
|
||||
|
||||
return $flattened;
|
||||
}
|
||||
|
||||
public static function get_client_ip($trust_proxy_headers = false)
|
||||
{
|
||||
if (!$trust_proxy_headers) {
|
||||
return $_SERVER['REMOTE_ADDR'];
|
||||
}
|
||||
|
||||
return $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? 'local';
|
||||
}
|
||||
|
||||
/**
|
||||
* Access an array index, retrieving the value stored there if it
|
||||
* exists or a default if it does not. This function allows you to
|
||||
* concisely access an index which may or may not exist without
|
||||
* raising a warning.
|
||||
*
|
||||
* @deprecated PHP 7.0 이상에서는 null 병합 연산자 사용을 권장
|
||||
*
|
||||
* @param array $var Array value to access
|
||||
* @param mixed $default Default value to return if the key is not
|
||||
* present in the array
|
||||
* @return mixed
|
||||
*/
|
||||
public static function array_get(&$var, $default = null)
|
||||
{
|
||||
if (isset($var)) {
|
||||
return $var;
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,1209 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use sammo\CentennialAllStarGrowth;
|
||||
use sammo\CentennialAllStarGrowthService;
|
||||
use sammo\General;
|
||||
use sammo\GameConst;
|
||||
|
||||
$loader = require __DIR__ . '/../vendor/autoload.php';
|
||||
$loader->addPsr4('sammo\\', __DIR__ . '/../hwe/sammo', true);
|
||||
|
||||
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
|
||||
{
|
||||
public function testProgressMilestones(): void
|
||||
{
|
||||
self::assertSame(0.0, CentennialAllStarGrowth::progress(180, 180, 1));
|
||||
self::assertEqualsWithDelta(0.2, CentennialAllStarGrowth::progress(180, 183, 1), 0.000001);
|
||||
self::assertEqualsWithDelta(0.4, CentennialAllStarGrowth::progress(180, 186, 1), 0.000001);
|
||||
self::assertSame(1.0, CentennialAllStarGrowth::progress(180, 195, 1));
|
||||
self::assertSame(1.0, CentennialAllStarGrowth::progress(180, 210, 1));
|
||||
}
|
||||
|
||||
public function testStatResetIsDisabledOnlyForCentennialAllStarPool(): void
|
||||
{
|
||||
$previousPool = GameConst::$targetGeneralPool;
|
||||
try {
|
||||
GameConst::$targetGeneralPool = CentennialAllStarGrowthService::POOL_CLASS;
|
||||
self::assertFalse(CentennialAllStarGrowthService::isStatResetAllowed());
|
||||
|
||||
GameConst::$targetGeneralPool = 'RandomNameGeneral';
|
||||
self::assertTrue(CentennialAllStarGrowthService::isStatResetAllowed());
|
||||
} finally {
|
||||
GameConst::$targetGeneralPool = $previousPool;
|
||||
}
|
||||
}
|
||||
|
||||
public function testMAndGGeneralsAdvanceAtNinetyPercentProgress(): void
|
||||
{
|
||||
self::assertSame(
|
||||
CentennialAllStarGrowthService::NPC_PROGRESS_MULTIPLIER,
|
||||
CentennialAllStarGrowthService::progressMultiplierForNPCType(3)
|
||||
);
|
||||
self::assertSame(
|
||||
CentennialAllStarGrowthService::NPC_PROGRESS_MULTIPLIER,
|
||||
CentennialAllStarGrowthService::progressMultiplierForNPCType(4)
|
||||
);
|
||||
self::assertSame(1.0, CentennialAllStarGrowthService::progressMultiplierForNPCType(2));
|
||||
self::assertEqualsWithDelta(
|
||||
0.36,
|
||||
CentennialAllStarGrowthService::calculateProgress(180, 186, 1, 0.9),
|
||||
0.000001
|
||||
);
|
||||
self::assertEqualsWithDelta(
|
||||
0.9,
|
||||
CentennialAllStarGrowthService::calculateProgress(180, 195, 1, 0.9),
|
||||
0.000001
|
||||
);
|
||||
self::assertEqualsWithDelta(
|
||||
0.9,
|
||||
CentennialAllStarGrowthService::calculateProgress(180, 210, 1, 0.9),
|
||||
0.000001
|
||||
);
|
||||
self::assertSame(
|
||||
91,
|
||||
CentennialAllStarGrowth::statFloor(100, 15, 0.9)
|
||||
);
|
||||
self::assertSame(
|
||||
0.4,
|
||||
CentennialAllStarGrowthService::dexTargetRatioForNPCType(3)
|
||||
);
|
||||
self::assertSame(
|
||||
0.4,
|
||||
CentennialAllStarGrowthService::dexTargetRatioForNPCType(4)
|
||||
);
|
||||
self::assertSame(
|
||||
1.0,
|
||||
CentennialAllStarGrowthService::dexTargetRatioForNPCType(2)
|
||||
);
|
||||
}
|
||||
|
||||
public function testGeneratedNpcKeepsOrdinaryTotalAndUsesCreationDateTargetFloor(): void
|
||||
{
|
||||
$vars = [
|
||||
'leadership' => 72,
|
||||
'strength' => 66,
|
||||
'intel' => 12,
|
||||
'dex1' => 120000,
|
||||
'dex2' => 240000,
|
||||
'dex3' => 360000,
|
||||
'dex4' => 480000,
|
||||
'dex5' => 600000,
|
||||
'special' => 'None',
|
||||
];
|
||||
$target = [
|
||||
'uniqueName' => 'A1000001',
|
||||
'leadership' => 100,
|
||||
'strength' => 80,
|
||||
'intel' => 10,
|
||||
'dex' => [900000, 800000, 700000, 600000, 500000],
|
||||
];
|
||||
$aux = CentennialAllStarGrowthService::initialAux($target);
|
||||
$general = $this->createStateGeneralMock($vars, $aux);
|
||||
|
||||
CentennialAllStarGrowthService::initializeGeneratedNPC($general, $target);
|
||||
CentennialAllStarGrowthService::applyTarget(
|
||||
$general,
|
||||
$target,
|
||||
['startyear' => 180, 'year' => 195, 'month' => 1],
|
||||
CentennialAllStarGrowthService::NPC_PROGRESS_MULTIPLIER,
|
||||
GameConst::$centennialNpcDexTargetRatio
|
||||
);
|
||||
|
||||
self::assertSame(91, $vars['leadership']);
|
||||
self::assertSame(73, $vars['strength']);
|
||||
self::assertSame(12, $vars['intel']);
|
||||
self::assertSame(176, $vars['leadership'] + $vars['strength'] + $vars['intel']);
|
||||
self::assertSame(
|
||||
[360000, 320000, 280000, 240000, 200000],
|
||||
$this->dexValues($vars)
|
||||
);
|
||||
self::assertSame(19, $aux['granted']['leadership']);
|
||||
self::assertSame(7, $aux['granted']['strength']);
|
||||
self::assertSame(0, $aux['granted']['intel']);
|
||||
self::assertSame(360000, $aux['granted']['dex1']);
|
||||
}
|
||||
|
||||
public function testFernandoAndUmaMusumeStartAtOrdinaryNpcTotal(): void
|
||||
{
|
||||
$pool = json_decode(
|
||||
file_get_contents(__DIR__ . '/../hwe/sammo/GeneralPool/Pool/UnderS100.json'),
|
||||
true,
|
||||
512,
|
||||
JSON_THROW_ON_ERROR
|
||||
);
|
||||
$columns = array_flip($pool['columns']);
|
||||
$targets = [];
|
||||
foreach ($pool['data'] as $row) {
|
||||
$name = $row[$columns['generalName']];
|
||||
if (!in_array($name, ['43·페르난도', '47·우마무스메'], true)) {
|
||||
continue;
|
||||
}
|
||||
$targets[$name] = [
|
||||
'leadership' => $row[$columns['leadership']],
|
||||
'strength' => $row[$columns['strength']],
|
||||
'intel' => $row[$columns['intel']],
|
||||
];
|
||||
}
|
||||
self::assertCount(2, $targets);
|
||||
|
||||
$generated = [
|
||||
'leadership' => 73,
|
||||
'strength' => 10,
|
||||
'intel' => 67,
|
||||
];
|
||||
$fernando = CentennialAllStarGrowthService::calculateGeneratedNPCInitialStats(
|
||||
$targets['43·페르난도'],
|
||||
$generated
|
||||
);
|
||||
$umaMusume = CentennialAllStarGrowthService::calculateGeneratedNPCInitialStats(
|
||||
$targets['47·우마무스메'],
|
||||
$generated
|
||||
);
|
||||
|
||||
self::assertSame([
|
||||
'leadership' => 67,
|
||||
'strength' => 73,
|
||||
'intel' => 10,
|
||||
], $fernando);
|
||||
self::assertSame([
|
||||
'leadership' => 10,
|
||||
'strength' => 67,
|
||||
'intel' => 73,
|
||||
], $umaMusume);
|
||||
self::assertSame(GameConst::$defaultStatNPCTotal, array_sum($fernando));
|
||||
self::assertSame(GameConst::$defaultStatNPCTotal, array_sum($umaMusume));
|
||||
}
|
||||
|
||||
public function testEveryCandidateKeepsTheGeneratedNpcStatTotal(): void
|
||||
{
|
||||
$pool = json_decode(
|
||||
file_get_contents(__DIR__ . '/../hwe/sammo/GeneralPool/Pool/UnderS100.json'),
|
||||
true,
|
||||
512,
|
||||
JSON_THROW_ON_ERROR
|
||||
);
|
||||
$columns = array_flip($pool['columns']);
|
||||
$generated = [
|
||||
'leadership' => 73,
|
||||
'strength' => 67,
|
||||
'intel' => 10,
|
||||
];
|
||||
|
||||
foreach ($pool['data'] as $row) {
|
||||
$target = [
|
||||
'leadership' => $row[$columns['leadership']],
|
||||
'strength' => $row[$columns['strength']],
|
||||
'intel' => $row[$columns['intel']],
|
||||
];
|
||||
$initial = CentennialAllStarGrowthService::calculateGeneratedNPCInitialStats(
|
||||
$target,
|
||||
$generated
|
||||
);
|
||||
self::assertSame(
|
||||
GameConst::$defaultStatNPCTotal,
|
||||
array_sum($initial),
|
||||
$row[$columns['generalName']]
|
||||
);
|
||||
sort($initial, SORT_NUMERIC);
|
||||
self::assertSame([10, 67, 73], array_values($initial));
|
||||
}
|
||||
}
|
||||
|
||||
public function testProgressMultiplierMustStayWithinUnitInterval(): void
|
||||
{
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
CentennialAllStarGrowthService::calculateProgress(180, 195, 1, 1.01);
|
||||
}
|
||||
|
||||
public function testLowStatGrowsWhileHigherStatStays(): void
|
||||
{
|
||||
$floor = CentennialAllStarGrowth::statFloor(90, 15, 0.6);
|
||||
self::assertSame(60, $floor);
|
||||
self::assertSame(
|
||||
['value' => 70, 'granted' => 0, 'delta' => 0],
|
||||
CentennialAllStarGrowth::advance(70, 0, $floor)
|
||||
);
|
||||
self::assertSame(
|
||||
['value' => 60, 'granted' => 10, 'delta' => 10],
|
||||
CentennialAllStarGrowth::advance(50, 0, $floor)
|
||||
);
|
||||
}
|
||||
|
||||
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 testCurrentReselectionBaselineUsesCurrentYearProgress(): void
|
||||
{
|
||||
$target = [
|
||||
'leadership' => 100,
|
||||
'strength' => 15,
|
||||
'intel' => 100,
|
||||
];
|
||||
|
||||
self::assertSame([
|
||||
'leadership' => 75,
|
||||
'strength' => 15,
|
||||
'intel' => 75,
|
||||
], CentennialAllStarGrowthService::calculateUserCurrentTargetStats(
|
||||
$target,
|
||||
['startyear' => 180, 'year' => 180, 'month' => 1]
|
||||
));
|
||||
self::assertSame([
|
||||
'leadership' => 83,
|
||||
'strength' => 15,
|
||||
'intel' => 83,
|
||||
], CentennialAllStarGrowthService::calculateUserCurrentTargetStats(
|
||||
$target,
|
||||
['startyear' => 180, 'year' => 192, 'month' => 1]
|
||||
));
|
||||
self::assertSame([
|
||||
'leadership' => 100,
|
||||
'strength' => 15,
|
||||
'intel' => 100,
|
||||
], CentennialAllStarGrowthService::calculateUserCurrentTargetStats(
|
||||
$target,
|
||||
['startyear' => 180, 'year' => 195, 'month' => 1]
|
||||
));
|
||||
}
|
||||
|
||||
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(1.0, $aux['dexTargetRatio']);
|
||||
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(
|
||||
['value' => 82, 'granted' => 7, 'organic' => 75],
|
||||
CentennialAllStarGrowth::replaceTarget(86, 11, 82)
|
||||
);
|
||||
self::assertSame(
|
||||
['value' => 90, 'granted' => 0, 'organic' => 90],
|
||||
CentennialAllStarGrowth::replaceTarget(101, 11, 82)
|
||||
);
|
||||
}
|
||||
|
||||
public function testRepeatedAdvanceIsIdempotent(): void
|
||||
{
|
||||
$first = CentennialAllStarGrowth::advance(50, 0, 60);
|
||||
$second = CentennialAllStarGrowth::advance($first['value'], $first['granted'], 60);
|
||||
self::assertSame(60, $second['value']);
|
||||
self::assertSame(10, $second['granted']);
|
||||
self::assertSame(0, $second['delta']);
|
||||
self::assertSame(50, CentennialAllStarGrowth::recordableValue(60, 10));
|
||||
}
|
||||
|
||||
public function testDexUsesSlowEarlyCurve(): void
|
||||
{
|
||||
self::assertSame(0, CentennialAllStarGrowth::dexFloor(900000, 0));
|
||||
self::assertSame(36000, CentennialAllStarGrowth::dexFloor(900000, 0.2));
|
||||
self::assertSame(144000, CentennialAllStarGrowth::dexFloor(900000, 0.4));
|
||||
self::assertSame(900000, CentennialAllStarGrowth::dexFloor(900000, 1));
|
||||
}
|
||||
|
||||
public function testDexExtensionRunsFromYear200ThroughYear205(): void
|
||||
{
|
||||
$env = static fn(int $year, int $month): array => [
|
||||
'startyear' => 180,
|
||||
'year' => $year,
|
||||
'month' => $month,
|
||||
];
|
||||
|
||||
self::assertSame(0.0, CentennialAllStarGrowthService::calculateDexExtensionProgress($env(199, 12)));
|
||||
self::assertSame(0.0, CentennialAllStarGrowthService::calculateDexExtensionProgress($env(200, 1)));
|
||||
self::assertEqualsWithDelta(
|
||||
0.2,
|
||||
CentennialAllStarGrowthService::calculateDexExtensionProgress($env(201, 1)),
|
||||
0.000001
|
||||
);
|
||||
self::assertEqualsWithDelta(
|
||||
0.5,
|
||||
CentennialAllStarGrowthService::calculateDexExtensionProgress($env(202, 7)),
|
||||
0.000001
|
||||
);
|
||||
self::assertSame(1.0, CentennialAllStarGrowthService::calculateDexExtensionProgress($env(205, 1)));
|
||||
self::assertSame(1.0, CentennialAllStarGrowthService::calculateDexExtensionProgress($env(210, 1)));
|
||||
}
|
||||
|
||||
public function testDexExtensionOnlyAddsHistoricalTargetAboveOneMillion(): void
|
||||
{
|
||||
$env = static fn(int $year, int $month): array => [
|
||||
'startyear' => 180,
|
||||
'year' => $year,
|
||||
'month' => $month,
|
||||
];
|
||||
|
||||
self::assertSame(
|
||||
900000,
|
||||
CentennialAllStarGrowthService::calculateDexTargetFloor(900000, $env(205, 1))
|
||||
);
|
||||
self::assertSame(
|
||||
1000000,
|
||||
CentennialAllStarGrowthService::calculateDexTargetFloor(2000000, $env(199, 12))
|
||||
);
|
||||
self::assertSame(
|
||||
1000000,
|
||||
CentennialAllStarGrowthService::calculateDexTargetFloor(2000000, $env(200, 1))
|
||||
);
|
||||
self::assertSame(
|
||||
1200000,
|
||||
CentennialAllStarGrowthService::calculateDexTargetFloor(2000000, $env(201, 1))
|
||||
);
|
||||
self::assertSame(
|
||||
1500000,
|
||||
CentennialAllStarGrowthService::calculateDexTargetFloor(2000000, $env(202, 7))
|
||||
);
|
||||
self::assertSame(
|
||||
2000000,
|
||||
CentennialAllStarGrowthService::calculateDexTargetFloor(2000000, $env(205, 1))
|
||||
);
|
||||
}
|
||||
|
||||
public function testEveryCandidateDexTargetExtendsMonotonicallyToItsHistoricalValue(): void
|
||||
{
|
||||
$pool = json_decode(
|
||||
file_get_contents(__DIR__ . '/../hwe/sammo/GeneralPool/Pool/UnderS100.json'),
|
||||
true,
|
||||
512,
|
||||
JSON_THROW_ON_ERROR
|
||||
);
|
||||
$columns = array_flip($pool['columns']);
|
||||
$dates = [
|
||||
['startyear' => 180, 'year' => 199, 'month' => 7],
|
||||
['startyear' => 180, 'year' => 200, 'month' => 1],
|
||||
['startyear' => 180, 'year' => 201, 'month' => 1],
|
||||
['startyear' => 180, 'year' => 202, 'month' => 7],
|
||||
['startyear' => 180, 'year' => 205, 'month' => 1],
|
||||
];
|
||||
$extendedCandidateCount = 0;
|
||||
$maximumTarget = 0;
|
||||
|
||||
foreach ($pool['data'] as $row) {
|
||||
$hasExtendedTarget = false;
|
||||
foreach ($row[$columns['dex']] as $target) {
|
||||
$target = (int) $target;
|
||||
$maximumTarget = max($maximumTarget, $target);
|
||||
$hasExtendedTarget = $hasExtendedTarget || $target > GameConst::$dexLimit;
|
||||
$floors = array_map(
|
||||
static fn(array $env): int => CentennialAllStarGrowthService::calculateDexTargetFloor(
|
||||
$target,
|
||||
$env
|
||||
),
|
||||
$dates
|
||||
);
|
||||
self::assertSame(
|
||||
min(GameConst::$dexLimit, $target),
|
||||
$floors[0],
|
||||
$row[$columns['generalName']]
|
||||
);
|
||||
self::assertSame($floors[0], $floors[1], $row[$columns['generalName']]);
|
||||
$sortedFloors = $floors;
|
||||
sort($sortedFloors, SORT_NUMERIC);
|
||||
self::assertSame($sortedFloors, $floors, $row[$columns['generalName']]);
|
||||
self::assertSame($target, $floors[4], $row[$columns['generalName']]);
|
||||
}
|
||||
if ($hasExtendedTarget) {
|
||||
$extendedCandidateCount++;
|
||||
}
|
||||
}
|
||||
|
||||
self::assertSame(208, $extendedCandidateCount);
|
||||
self::assertSame(2990401, $maximumTarget);
|
||||
}
|
||||
|
||||
public function testNpcReceivesFortyPercentOfExtendedHistoricalDexTarget(): void
|
||||
{
|
||||
$ratio = GameConst::$centennialNpcDexTargetRatio;
|
||||
|
||||
self::assertSame(
|
||||
400000,
|
||||
CentennialAllStarGrowthService::calculateDexTargetFloor(
|
||||
2000000,
|
||||
['startyear' => 180, 'year' => 200, 'month' => 1],
|
||||
$ratio
|
||||
)
|
||||
);
|
||||
self::assertSame(
|
||||
480000,
|
||||
CentennialAllStarGrowthService::calculateDexTargetFloor(
|
||||
2000000,
|
||||
['startyear' => 180, 'year' => 201, 'month' => 1],
|
||||
$ratio
|
||||
)
|
||||
);
|
||||
self::assertSame(
|
||||
800000,
|
||||
CentennialAllStarGrowthService::calculateDexTargetFloor(
|
||||
2000000,
|
||||
['startyear' => 180, 'year' => 205, 'month' => 1],
|
||||
$ratio
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function testDexExtensionNeverLowersNaturalGrowthAndKeepsItRecordable(): void
|
||||
{
|
||||
$target = $this->singleDexTarget('extended', 0, 2000000);
|
||||
$vars = $this->emptyGeneralVars();
|
||||
$aux = CentennialAllStarGrowthService::initialAux($target, []);
|
||||
$general = $this->createStateGeneralMock($vars, $aux);
|
||||
|
||||
CentennialAllStarGrowthService::applyTarget(
|
||||
$general,
|
||||
$target,
|
||||
['startyear' => 180, 'year' => 199, 'month' => 7]
|
||||
);
|
||||
self::assertSame(1000000, $vars['dex1']);
|
||||
self::assertSame(1000000, $aux['granted']['dex1']);
|
||||
|
||||
$vars['dex1'] += 300000;
|
||||
$result = CentennialAllStarGrowthService::applyTarget(
|
||||
$general,
|
||||
$target,
|
||||
['startyear' => 180, 'year' => 201, 'month' => 1]
|
||||
);
|
||||
self::assertSame(1300000, $vars['dex1']);
|
||||
self::assertSame(1000000, $aux['granted']['dex1']);
|
||||
self::assertSame(300000, CentennialAllStarGrowthService::recordableValue($general, 'dex1'));
|
||||
self::assertSame(1, $result['dexExtensionMilestone']);
|
||||
self::assertSame(0, $result['previousDexExtensionMilestone']);
|
||||
|
||||
$result = CentennialAllStarGrowthService::applyTarget(
|
||||
$general,
|
||||
$target,
|
||||
['startyear' => 180, 'year' => 205, 'month' => 1]
|
||||
);
|
||||
self::assertSame(2000000, $vars['dex1']);
|
||||
self::assertSame(1700000, $aux['granted']['dex1']);
|
||||
self::assertSame(300000, CentennialAllStarGrowthService::recordableValue($general, 'dex1'));
|
||||
self::assertSame(5, $result['dexExtensionMilestone']);
|
||||
self::assertSame(1, $result['previousDexExtensionMilestone']);
|
||||
}
|
||||
|
||||
public function testDexExtensionDoesNotRefillConsumedConversionBudget(): void
|
||||
{
|
||||
$target = $this->singleDexTarget('extended-consumed', 0, 2000000);
|
||||
$vars = $this->emptyGeneralVars();
|
||||
$vars['dex1'] = 800000;
|
||||
$aux = CentennialAllStarGrowthService::initialAux($target, []);
|
||||
$aux['granted']['dex1'] = 800000;
|
||||
$aux['dexConsumed']['dex1'] = 200000;
|
||||
$aux['dexFloor']['dex1'] = 800000;
|
||||
$general = $this->createStateGeneralMock($vars, $aux);
|
||||
|
||||
CentennialAllStarGrowthService::applyTarget(
|
||||
$general,
|
||||
$target,
|
||||
['startyear' => 180, 'year' => 205, 'month' => 1]
|
||||
);
|
||||
|
||||
self::assertSame(1800000, $vars['dex1']);
|
||||
self::assertSame(1800000, $aux['granted']['dex1']);
|
||||
self::assertSame(200000, $aux['dexConsumed']['dex1']);
|
||||
self::assertSame(1800000, $aux['dexFloor']['dex1']);
|
||||
self::assertSame(0, CentennialAllStarGrowthService::recordableValue($general, 'dex1'));
|
||||
}
|
||||
|
||||
public function testNpcDexStopsAtFortyPercentOfHistoricalTarget(): void
|
||||
{
|
||||
$target = 900000;
|
||||
$ratio = GameConst::$centennialNpcDexTargetRatio;
|
||||
|
||||
self::assertSame(0.4, $ratio);
|
||||
self::assertSame(
|
||||
57600,
|
||||
CentennialAllStarGrowthService::calculateDexTargetFloor(
|
||||
$target,
|
||||
['startyear' => 180, 'year' => 186, 'month' => 1],
|
||||
$ratio
|
||||
)
|
||||
);
|
||||
self::assertSame(
|
||||
360000,
|
||||
CentennialAllStarGrowthService::calculateDexTargetFloor(
|
||||
$target,
|
||||
['startyear' => 180, 'year' => 195, 'month' => 1],
|
||||
$ratio
|
||||
)
|
||||
);
|
||||
self::assertSame(
|
||||
360000,
|
||||
CentennialAllStarGrowthService::calculateDexTargetFloor(
|
||||
$target,
|
||||
['startyear' => 180, 'year' => 210, 'month' => 1],
|
||||
$ratio
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function testScenarioConfigKeepsNpcDexTargetRatioAtFortyPercent(): void
|
||||
{
|
||||
$scenario = json_decode(
|
||||
file_get_contents(__DIR__ . '/../hwe/scenario/scenario_915.json'),
|
||||
true,
|
||||
512,
|
||||
JSON_THROW_ON_ERROR
|
||||
);
|
||||
|
||||
self::assertSame(
|
||||
GameConst::$centennialNpcDexTargetRatio,
|
||||
$scenario['map']['centennialNpcDexTargetRatio']
|
||||
);
|
||||
}
|
||||
|
||||
public function testNpcDexRatioChangeRemovesOnlyOldEventGrant(): void
|
||||
{
|
||||
self::assertSame(
|
||||
['value' => 360000, 'granted' => 360000, 'organic' => 0],
|
||||
CentennialAllStarGrowth::replaceTarget(810000, 810000, 360000)
|
||||
);
|
||||
self::assertSame(
|
||||
['value' => 360000, 'granted' => 310000, 'organic' => 50000],
|
||||
CentennialAllStarGrowth::replaceTarget(860000, 810000, 360000)
|
||||
);
|
||||
self::assertSame(
|
||||
['value' => 400000, 'granted' => 0, 'organic' => 400000],
|
||||
CentennialAllStarGrowth::replaceTarget(1210000, 810000, 360000)
|
||||
);
|
||||
}
|
||||
|
||||
public function testExistingNpcDexGrantIsRebasedWithoutChangingFinalStats(): void
|
||||
{
|
||||
$vars = [
|
||||
'leadership' => 91,
|
||||
'strength' => 91,
|
||||
'intel' => 91,
|
||||
'dex1' => 810000,
|
||||
'dex2' => 810000,
|
||||
'dex3' => 810000,
|
||||
'dex4' => 810000,
|
||||
'dex5' => 810000,
|
||||
'special' => 'None',
|
||||
];
|
||||
$aux = [
|
||||
'targetId' => 'A1000001',
|
||||
'granted' => [
|
||||
'leadership' => 76,
|
||||
'strength' => 76,
|
||||
'intel' => 76,
|
||||
'dex1' => 810000,
|
||||
'dex2' => 810000,
|
||||
'dex3' => 810000,
|
||||
'dex4' => 810000,
|
||||
'dex5' => 810000,
|
||||
],
|
||||
'progressMonth' => 162,
|
||||
'milestone' => 4,
|
||||
'naturalSpecialDomestic' => null,
|
||||
'eventSpecialDomestic' => null,
|
||||
'userInitialStats' => null,
|
||||
];
|
||||
$general = $this->getMockBuilder(General::class)
|
||||
->disableOriginalConstructor()
|
||||
->onlyMethods(['getAuxVar', 'setAuxVar', 'getVar', 'updateVar'])
|
||||
->getMock();
|
||||
$general->method('getAuxVar')->willReturnCallback(
|
||||
static fn(string $key) => $key === CentennialAllStarGrowthService::AUX_KEY
|
||||
? $aux
|
||||
: null
|
||||
);
|
||||
$general->method('getVar')->willReturnCallback(
|
||||
static fn(string $key) => $vars[$key] ?? null
|
||||
);
|
||||
$general->method('updateVar')->willReturnCallback(
|
||||
static function (string $key, $value) use (&$vars): void {
|
||||
$vars[$key] = $value;
|
||||
}
|
||||
);
|
||||
$general->method('setAuxVar')->willReturnCallback(
|
||||
static function (string $key, $value) use (&$aux): void {
|
||||
if ($key === CentennialAllStarGrowthService::AUX_KEY) {
|
||||
$aux = $value;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
$result = CentennialAllStarGrowthService::applyTarget(
|
||||
$general,
|
||||
[
|
||||
'uniqueName' => 'A1000001',
|
||||
'leadership' => 100,
|
||||
'strength' => 100,
|
||||
'intel' => 100,
|
||||
'dex' => [900000, 900000, 900000, 900000, 900000],
|
||||
],
|
||||
['startyear' => 180, 'year' => 195, 'month' => 1],
|
||||
CentennialAllStarGrowthService::NPC_PROGRESS_MULTIPLIER,
|
||||
GameConst::$centennialNpcDexTargetRatio
|
||||
);
|
||||
|
||||
self::assertTrue($result['changed']);
|
||||
self::assertSame(91, $vars['leadership']);
|
||||
self::assertSame(91, $vars['strength']);
|
||||
self::assertSame(91, $vars['intel']);
|
||||
foreach (['dex1', 'dex2', 'dex3', 'dex4', 'dex5'] as $key) {
|
||||
self::assertSame(360000, $vars[$key]);
|
||||
self::assertSame(360000, $aux['granted'][$key]);
|
||||
}
|
||||
self::assertSame(0.4, $aux['dexTargetRatio']);
|
||||
}
|
||||
|
||||
public function testDexConversionConsumesEventFloorWithoutMonthlyRefill(): void
|
||||
{
|
||||
$vars = [
|
||||
'leadership' => 100,
|
||||
'strength' => 100,
|
||||
'intel' => 100,
|
||||
'dex1' => 216000,
|
||||
'dex2' => 489600,
|
||||
'dex3' => 360000,
|
||||
'dex4' => 360000,
|
||||
'dex5' => 360000,
|
||||
'special' => 'None',
|
||||
];
|
||||
$aux = [
|
||||
'targetId' => 'A1000001',
|
||||
'granted' => [
|
||||
'leadership' => 85,
|
||||
'strength' => 85,
|
||||
'intel' => 85,
|
||||
'dex1' => 360000,
|
||||
'dex2' => 360000,
|
||||
'dex3' => 360000,
|
||||
'dex4' => 360000,
|
||||
'dex5' => 360000,
|
||||
],
|
||||
'dexConsumed' => [
|
||||
'dex1' => 0,
|
||||
'dex2' => 0,
|
||||
'dex3' => 0,
|
||||
'dex4' => 0,
|
||||
'dex5' => 0,
|
||||
],
|
||||
'progressMonth' => 180,
|
||||
'milestone' => 5,
|
||||
'naturalSpecialDomestic' => null,
|
||||
'eventSpecialDomestic' => null,
|
||||
'userInitialStats' => [],
|
||||
'dexTargetRatio' => 1.0,
|
||||
];
|
||||
$general = $this->createStateGeneralMock($vars, $aux);
|
||||
|
||||
CentennialAllStarGrowthService::reconcileDexConversion(
|
||||
$general,
|
||||
'dex1',
|
||||
'dex2',
|
||||
360000,
|
||||
216000,
|
||||
360000,
|
||||
489600,
|
||||
0.9
|
||||
);
|
||||
|
||||
self::assertSame(216000, $aux['granted']['dex1']);
|
||||
self::assertSame(489600, $aux['granted']['dex2']);
|
||||
self::assertSame(144000, $aux['dexConsumed']['dex1']);
|
||||
|
||||
CentennialAllStarGrowthService::applyTarget(
|
||||
$general,
|
||||
[
|
||||
'uniqueName' => 'A1000001',
|
||||
'leadership' => 100,
|
||||
'strength' => 100,
|
||||
'intel' => 100,
|
||||
'dex' => [360000, 360000, 360000, 360000, 360000],
|
||||
],
|
||||
['startyear' => 180, 'year' => 195, 'month' => 1]
|
||||
);
|
||||
|
||||
self::assertSame(216000, $vars['dex1']);
|
||||
self::assertSame(489600, $vars['dex2']);
|
||||
self::assertSame(216000, $aux['dexFloor']['dex1']);
|
||||
self::assertSame(0, CentennialAllStarGrowthService::recordableValue($general, 'dex1'));
|
||||
self::assertSame(0, CentennialAllStarGrowthService::recordableValue($general, 'dex2'));
|
||||
|
||||
$vars['dex1'] = 129600;
|
||||
$vars['dex2'] = 567360;
|
||||
CentennialAllStarGrowthService::reconcileDexConversion(
|
||||
$general,
|
||||
'dex1',
|
||||
'dex2',
|
||||
216000,
|
||||
129600,
|
||||
489600,
|
||||
567360,
|
||||
0.9
|
||||
);
|
||||
CentennialAllStarGrowthService::applyTarget(
|
||||
$general,
|
||||
[
|
||||
'uniqueName' => 'A1000001',
|
||||
'leadership' => 100,
|
||||
'strength' => 100,
|
||||
'intel' => 100,
|
||||
'dex' => [360000, 360000, 360000, 360000, 360000],
|
||||
],
|
||||
['startyear' => 180, 'year' => 195, 'month' => 1]
|
||||
);
|
||||
|
||||
self::assertSame(129600, $vars['dex1']);
|
||||
self::assertSame(567360, $vars['dex2']);
|
||||
self::assertSame(230400, $aux['dexConsumed']['dex1']);
|
||||
self::assertSame(129600, $aux['dexFloor']['dex1']);
|
||||
self::assertSame(0, CentennialAllStarGrowthService::recordableValue($general, 'dex1'));
|
||||
self::assertSame(0, CentennialAllStarGrowthService::recordableValue($general, 'dex2'));
|
||||
}
|
||||
|
||||
public function testNaturalDexConversionDoesNotBecomeEventGrant(): void
|
||||
{
|
||||
$vars = [
|
||||
'dex1' => 216000,
|
||||
'dex2' => 129600,
|
||||
];
|
||||
$aux = [
|
||||
'targetId' => 'A1000001',
|
||||
'granted' => [
|
||||
'dex1' => 0,
|
||||
'dex2' => 0,
|
||||
],
|
||||
'dexConsumed' => [
|
||||
'dex1' => 0,
|
||||
'dex2' => 0,
|
||||
],
|
||||
'dexFloor' => [
|
||||
'dex1' => 360000,
|
||||
'dex2' => 0,
|
||||
],
|
||||
];
|
||||
$general = $this->createStateGeneralMock($vars, $aux);
|
||||
|
||||
CentennialAllStarGrowthService::reconcileDexConversion(
|
||||
$general,
|
||||
'dex1',
|
||||
'dex2',
|
||||
360000,
|
||||
216000,
|
||||
0,
|
||||
129600,
|
||||
0.9
|
||||
);
|
||||
|
||||
self::assertSame(0, $aux['granted']['dex1']);
|
||||
self::assertSame(0, $aux['granted']['dex2']);
|
||||
self::assertSame(144000, $aux['dexConsumed']['dex1']);
|
||||
self::assertSame(216000, CentennialAllStarGrowthService::recordableValue($general, 'dex1'));
|
||||
self::assertSame(129600, CentennialAllStarGrowthService::recordableValue($general, 'dex2'));
|
||||
}
|
||||
|
||||
public function testMixedDexConversionSplitsEventAndNaturalPointsByTotalShare(): void
|
||||
{
|
||||
$vars = [
|
||||
'dex1' => 360000,
|
||||
'dex2' => 600000,
|
||||
];
|
||||
$aux = [
|
||||
'targetId' => 'archer',
|
||||
'granted' => [
|
||||
'dex1' => 0,
|
||||
'dex2' => 900000,
|
||||
],
|
||||
'dexConsumed' => [
|
||||
'dex1' => 0,
|
||||
'dex2' => 0,
|
||||
],
|
||||
'dexFloor' => [
|
||||
'dex1' => 0,
|
||||
'dex2' => 900000,
|
||||
],
|
||||
];
|
||||
$general = $this->createStateGeneralMock($vars, $aux);
|
||||
|
||||
CentennialAllStarGrowthService::reconcileDexConversion(
|
||||
$general,
|
||||
'dex2',
|
||||
'dex1',
|
||||
1000000,
|
||||
600000,
|
||||
0,
|
||||
360000,
|
||||
0.9
|
||||
);
|
||||
|
||||
self::assertSame(540000, $aux['granted']['dex2']);
|
||||
self::assertSame(324000, $aux['granted']['dex1']);
|
||||
self::assertSame(60_000, CentennialAllStarGrowthService::recordableValue($general, 'dex2'));
|
||||
self::assertSame(36_000, CentennialAllStarGrowthService::recordableValue($general, 'dex1'));
|
||||
self::assertSame(864000, $aux['granted']['dex1'] + $aux['granted']['dex2']);
|
||||
self::assertSame(
|
||||
96000,
|
||||
CentennialAllStarGrowthService::recordableValue($general, 'dex1')
|
||||
+ CentennialAllStarGrowthService::recordableValue($general, 'dex2')
|
||||
);
|
||||
}
|
||||
|
||||
public function testArcherCavalryGhostConversionChainKeepsOnlyCurrentTargetBudget(): void
|
||||
{
|
||||
$vars = $this->emptyGeneralVars();
|
||||
$aux = CentennialAllStarGrowthService::initialAux(
|
||||
$this->singleDexTarget('archer', 1),
|
||||
[]
|
||||
);
|
||||
$general = $this->createStateGeneralMock($vars, $aux);
|
||||
$env = ['startyear' => 180, 'year' => 195, 'month' => 1];
|
||||
|
||||
CentennialAllStarGrowthService::applyTarget(
|
||||
$general,
|
||||
$this->singleDexTarget('archer', 1),
|
||||
$env
|
||||
);
|
||||
$this->convertDex($general, $vars, 'dex2', 'dex1');
|
||||
self::assertSame([324000, 540000, 0, 0, 0], $this->dexValues($vars));
|
||||
|
||||
CentennialAllStarGrowthService::applyTarget(
|
||||
$general,
|
||||
$this->singleDexTarget('cavalry', 2),
|
||||
$env
|
||||
);
|
||||
self::assertSame([0, 0, 900000, 0, 0], $this->dexValues($vars));
|
||||
$this->convertDex($general, $vars, 'dex3', 'dex1');
|
||||
self::assertSame([324000, 0, 540000, 0, 0], $this->dexValues($vars));
|
||||
|
||||
CentennialAllStarGrowthService::applyTarget(
|
||||
$general,
|
||||
$this->singleDexTarget('ghost', 3),
|
||||
$env
|
||||
);
|
||||
self::assertSame([0, 0, 0, 900000, 0], $this->dexValues($vars));
|
||||
$this->convertDex($general, $vars, 'dex4', 'dex1');
|
||||
|
||||
self::assertSame([324000, 0, 0, 540000, 0], $this->dexValues($vars));
|
||||
self::assertSame(864000, array_sum($this->dexValues($vars)));
|
||||
foreach (['dex1', 'dex2', 'dex3', 'dex4', 'dex5'] as $key) {
|
||||
self::assertSame(0, CentennialAllStarGrowthService::recordableValue($general, $key));
|
||||
}
|
||||
}
|
||||
|
||||
public function testSelectingInfantryAfterConversionReplacesOldEventBudget(): void
|
||||
{
|
||||
$vars = $this->emptyGeneralVars();
|
||||
$aux = CentennialAllStarGrowthService::initialAux(
|
||||
$this->singleDexTarget('archer', 1),
|
||||
[]
|
||||
);
|
||||
$general = $this->createStateGeneralMock($vars, $aux);
|
||||
$env = ['startyear' => 180, 'year' => 195, 'month' => 1];
|
||||
|
||||
CentennialAllStarGrowthService::applyTarget(
|
||||
$general,
|
||||
$this->singleDexTarget('archer', 1),
|
||||
$env
|
||||
);
|
||||
$this->convertDex($general, $vars, 'dex2', 'dex1');
|
||||
CentennialAllStarGrowthService::applyTarget(
|
||||
$general,
|
||||
$this->singleDexTarget('infantry', 0),
|
||||
$env
|
||||
);
|
||||
|
||||
self::assertSame([900000, 0, 0, 0, 0], $this->dexValues($vars));
|
||||
self::assertSame(900000, array_sum($aux['granted']));
|
||||
self::assertSame(0, CentennialAllStarGrowthService::recordableValue($general, 'dex1'));
|
||||
}
|
||||
|
||||
public function testMixedConversionThenReselectionPreservesOnlyPostLossNaturalTotal(): void
|
||||
{
|
||||
$vars = $this->emptyGeneralVars();
|
||||
$aux = CentennialAllStarGrowthService::initialAux(
|
||||
$this->singleDexTarget('archer', 1),
|
||||
[]
|
||||
);
|
||||
$general = $this->createStateGeneralMock($vars, $aux);
|
||||
$env = ['startyear' => 180, 'year' => 195, 'month' => 1];
|
||||
|
||||
CentennialAllStarGrowthService::applyTarget(
|
||||
$general,
|
||||
$this->singleDexTarget('archer', 1),
|
||||
$env
|
||||
);
|
||||
$vars['dex2'] += 100000;
|
||||
$this->convertDex($general, $vars, 'dex2', 'dex1');
|
||||
CentennialAllStarGrowthService::applyTarget(
|
||||
$general,
|
||||
$this->singleDexTarget('infantry', 0),
|
||||
$env
|
||||
);
|
||||
|
||||
self::assertSame([900000, 60000, 0, 0, 0], $this->dexValues($vars));
|
||||
self::assertSame(864000, $aux['granted']['dex1']);
|
||||
self::assertSame(0, $aux['granted']['dex2']);
|
||||
self::assertSame(96000, array_sum($this->recordableDexValues($general)));
|
||||
self::assertSame(960000, array_sum($this->dexValues($vars)));
|
||||
}
|
||||
|
||||
public function testReselectionResetsConsumedDexFloorAndTransferredGrant(): void
|
||||
{
|
||||
$vars = [
|
||||
'leadership' => 100,
|
||||
'strength' => 100,
|
||||
'intel' => 100,
|
||||
'dex1' => 216000,
|
||||
'dex2' => 489600,
|
||||
'dex3' => 360000,
|
||||
'dex4' => 360000,
|
||||
'dex5' => 360000,
|
||||
'special' => 'None',
|
||||
];
|
||||
$aux = [
|
||||
'targetId' => 'A1000001',
|
||||
'granted' => [
|
||||
'leadership' => 85,
|
||||
'strength' => 85,
|
||||
'intel' => 85,
|
||||
'dex1' => 216000,
|
||||
'dex2' => 489600,
|
||||
'dex3' => 360000,
|
||||
'dex4' => 360000,
|
||||
'dex5' => 360000,
|
||||
],
|
||||
'dexConsumed' => [
|
||||
'dex1' => 144000,
|
||||
'dex2' => 0,
|
||||
'dex3' => 0,
|
||||
'dex4' => 0,
|
||||
'dex5' => 0,
|
||||
],
|
||||
'dexFloor' => [
|
||||
'dex1' => 216000,
|
||||
'dex2' => 360000,
|
||||
'dex3' => 360000,
|
||||
'dex4' => 360000,
|
||||
'dex5' => 360000,
|
||||
],
|
||||
'progressMonth' => 180,
|
||||
'milestone' => 5,
|
||||
'naturalSpecialDomestic' => null,
|
||||
'eventSpecialDomestic' => null,
|
||||
'userInitialStats' => [],
|
||||
'dexTargetRatio' => 1.0,
|
||||
];
|
||||
$general = $this->createStateGeneralMock($vars, $aux);
|
||||
|
||||
CentennialAllStarGrowthService::applyTarget(
|
||||
$general,
|
||||
[
|
||||
'uniqueName' => 'A1000002',
|
||||
'leadership' => 100,
|
||||
'strength' => 100,
|
||||
'intel' => 100,
|
||||
'dex' => [400000, 400000, 400000, 400000, 400000],
|
||||
],
|
||||
['startyear' => 180, 'year' => 195, 'month' => 1]
|
||||
);
|
||||
|
||||
self::assertSame(400000, $vars['dex1']);
|
||||
self::assertSame(400000, $vars['dex2']);
|
||||
self::assertSame(0, $aux['dexConsumed']['dex1']);
|
||||
self::assertSame(400000, $aux['dexFloor']['dex1']);
|
||||
self::assertSame(400000, $aux['granted']['dex1']);
|
||||
self::assertSame(400000, $aux['granted']['dex2']);
|
||||
}
|
||||
|
||||
public function testUserDexStillReachesFullHistoricalTarget(): void
|
||||
{
|
||||
self::assertSame(
|
||||
900000,
|
||||
CentennialAllStarGrowthService::calculateDexTargetFloor(
|
||||
900000,
|
||||
['startyear' => 180, 'year' => 195, 'month' => 1]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function testUntrackedGeneralKeepsFullHallValue(): void
|
||||
{
|
||||
self::assertSame(
|
||||
123456,
|
||||
CentennialAllStarGrowthService::recordableRawValue(
|
||||
['dex1' => 123456, 'aux' => '{}'],
|
||||
'dex1'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private function createStateGeneralMock(array &$vars, array &$aux): General
|
||||
{
|
||||
$general = $this->getMockBuilder(General::class)
|
||||
->disableOriginalConstructor()
|
||||
->onlyMethods(['getAuxVar', 'setAuxVar', 'getVar', 'updateVar'])
|
||||
->getMock();
|
||||
$general->method('getAuxVar')->willReturnCallback(
|
||||
static function (string $key) use (&$aux) {
|
||||
return $key === CentennialAllStarGrowthService::AUX_KEY
|
||||
? $aux
|
||||
: null;
|
||||
}
|
||||
);
|
||||
$general->method('getVar')->willReturnCallback(
|
||||
static function (string $key) use (&$vars) {
|
||||
return $vars[$key] ?? null;
|
||||
}
|
||||
);
|
||||
$general->method('updateVar')->willReturnCallback(
|
||||
static function (string $key, $value) use (&$vars): void {
|
||||
$vars[$key] = $value;
|
||||
}
|
||||
);
|
||||
$general->method('setAuxVar')->willReturnCallback(
|
||||
static function (string $key, $value) use (&$aux): void {
|
||||
if ($key === CentennialAllStarGrowthService::AUX_KEY) {
|
||||
$aux = $value;
|
||||
}
|
||||
}
|
||||
);
|
||||
return $general;
|
||||
}
|
||||
|
||||
private function emptyGeneralVars(): array
|
||||
{
|
||||
return [
|
||||
'leadership' => 100,
|
||||
'strength' => 100,
|
||||
'intel' => 100,
|
||||
'dex1' => 0,
|
||||
'dex2' => 0,
|
||||
'dex3' => 0,
|
||||
'dex4' => 0,
|
||||
'dex5' => 0,
|
||||
'special' => 'None',
|
||||
];
|
||||
}
|
||||
|
||||
private function singleDexTarget(string $id, int $dexIndex, int $amount = 900000): array
|
||||
{
|
||||
$dex = [0, 0, 0, 0, 0];
|
||||
$dex[$dexIndex] = $amount;
|
||||
return [
|
||||
'uniqueName' => $id,
|
||||
'leadership' => 100,
|
||||
'strength' => 100,
|
||||
'intel' => 100,
|
||||
'dex' => $dex,
|
||||
];
|
||||
}
|
||||
|
||||
private function convertDex(
|
||||
General $general,
|
||||
array &$vars,
|
||||
string $sourceKey,
|
||||
string $destinationKey
|
||||
): void {
|
||||
$sourceBefore = $vars[$sourceKey];
|
||||
$destinationBefore = $vars[$destinationKey];
|
||||
$cut = (int) ($sourceBefore * 0.4);
|
||||
$add = (int) ($cut * 0.9);
|
||||
$vars[$sourceKey] -= $cut;
|
||||
$vars[$destinationKey] += $add;
|
||||
CentennialAllStarGrowthService::reconcileDexConversion(
|
||||
$general,
|
||||
$sourceKey,
|
||||
$destinationKey,
|
||||
$sourceBefore,
|
||||
$vars[$sourceKey],
|
||||
$destinationBefore,
|
||||
$vars[$destinationKey],
|
||||
0.9
|
||||
);
|
||||
}
|
||||
|
||||
private function dexValues(array $vars): array
|
||||
{
|
||||
return [
|
||||
$vars['dex1'],
|
||||
$vars['dex2'],
|
||||
$vars['dex3'],
|
||||
$vars['dex4'],
|
||||
$vars['dex5'],
|
||||
];
|
||||
}
|
||||
|
||||
private function recordableDexValues(General $general): array
|
||||
{
|
||||
return array_map(
|
||||
static fn(string $key): int => CentennialAllStarGrowthService::recordableValue(
|
||||
$general,
|
||||
$key
|
||||
),
|
||||
['dex1', 'dex2', 'dex3', 'dex4', 'dex5']
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use sammo\GameConst;
|
||||
use sammo\GeneralPool\SPoolUnderU100;
|
||||
|
||||
require_once __DIR__ . '/../hwe/sammo/AbsGeneralPool.php';
|
||||
require_once __DIR__ . '/../hwe/sammo/AbsFromUserPool.php';
|
||||
require_once __DIR__ . '/../hwe/sammo/GeneralPool/SPoolUnderU100.php';
|
||||
require_once __DIR__ . '/../hwe/sammo/ActionLogger.php';
|
||||
require_once __DIR__ . '/../hwe/sammo/GameConstBase.php';
|
||||
require_once __DIR__ . '/../hwe/d_setting/GameConst.php';
|
||||
|
||||
final class CentennialAllStarPoolTest extends TestCase
|
||||
{
|
||||
private const POOL_PATH = __DIR__ . '/../hwe/sammo/GeneralPool/Pool/UnderS100.json';
|
||||
|
||||
public function testPoolCoversEveryCompletedNonEventPhase(): void
|
||||
{
|
||||
$pool = json_decode(
|
||||
file_get_contents(self::POOL_PATH),
|
||||
true,
|
||||
512,
|
||||
JSON_THROW_ON_ERROR
|
||||
);
|
||||
self::assertSame(
|
||||
[
|
||||
'generalName',
|
||||
'leadership',
|
||||
'strength',
|
||||
'intel',
|
||||
'specialDomestic',
|
||||
'dex',
|
||||
'imgsvr',
|
||||
'picture',
|
||||
'sourcePhase',
|
||||
'sourceServerId',
|
||||
'sourceGeneralNo',
|
||||
'selectionReasons',
|
||||
],
|
||||
$pool['columns']
|
||||
);
|
||||
self::assertCount(4682, $pool['data']);
|
||||
self::assertSame(
|
||||
[
|
||||
5, 10, 15, 20, 25, 30, 35, 40, 45, 50,
|
||||
55, 60, 65, 70, 75, 80, 85, 90, 95,
|
||||
],
|
||||
$pool['excludedEventPhases']
|
||||
);
|
||||
|
||||
$column = array_flip($pool['columns']);
|
||||
$phases = [];
|
||||
$chiefCounts = [];
|
||||
$sourceKeys = [];
|
||||
$generalNames = [];
|
||||
foreach ($pool['data'] as $row) {
|
||||
self::assertCount(count($pool['columns']), $row);
|
||||
|
||||
$phase = $row[$column['sourcePhase']];
|
||||
$sourceKey = "{$phase}:{$row[$column['sourceGeneralNo']]}";
|
||||
self::assertArrayNotHasKey($sourceKey, $sourceKeys);
|
||||
$sourceKeys[$sourceKey] = true;
|
||||
$phases[$phase] = true;
|
||||
|
||||
$generalName = $row[$column['generalName']];
|
||||
self::assertArrayNotHasKey($generalName, $generalNames);
|
||||
$generalNames[$generalName] = true;
|
||||
self::assertMatchesRegularExpression('/^(\d{1,2})·.+$/u', $generalName);
|
||||
self::assertSame(
|
||||
1,
|
||||
preg_match('/^(\d{1,2})·/u', $generalName, $nameMatch)
|
||||
);
|
||||
self::assertSame($phase, (int) $nameMatch[1]);
|
||||
self::assertLessThanOrEqual(32, mb_strlen($generalName));
|
||||
|
||||
self::assertCount(5, $row[$column['dex']]);
|
||||
self::assertNotEmpty($row[$column['selectionReasons']]);
|
||||
foreach ($row[$column['selectionReasons']] as $reason) {
|
||||
self::assertMatchesRegularExpression(
|
||||
'/^(hall:[a-z0-9_]+|chief:(?:5|6|7|8|9|10|11|12))$/',
|
||||
$reason
|
||||
);
|
||||
if (str_starts_with($reason, 'chief:')) {
|
||||
$chiefCounts[$phase] = ($chiefCounts[$phase] ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ksort($phases, SORT_NUMERIC);
|
||||
$expectedPhases = array_values(array_diff(
|
||||
range(1, 99),
|
||||
$pool['excludedEventPhases']
|
||||
));
|
||||
self::assertSame($expectedPhases, array_keys($phases));
|
||||
foreach ($expectedPhases as $phase) {
|
||||
self::assertSame(
|
||||
8,
|
||||
$chiefCounts[$phase] ?? 0,
|
||||
"phase {$phase} must include the ruler and seven chiefs"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function testCentennialSelectionWeightFavorsUserStatsAndKeepsZeroDexEligible(): void
|
||||
{
|
||||
$method = new ReflectionMethod(SPoolUnderU100::class, 'getCandidateWeight');
|
||||
$method->setAccessible(true);
|
||||
$low = [
|
||||
'leadership' => 80,
|
||||
'strength' => 70,
|
||||
'intel' => 10,
|
||||
'dex' => [0, 0, 0, 0, 0],
|
||||
];
|
||||
$high = [
|
||||
'leadership' => 95,
|
||||
'strength' => 85,
|
||||
'intel' => 10,
|
||||
'dex' => [0, 0, 0, 0, 0],
|
||||
];
|
||||
|
||||
self::assertSame(100000, $method->invoke(null, $low, 0));
|
||||
self::assertSame(100000.0, $method->invoke(null, $low, 1));
|
||||
self::assertSame(150000.0, $method->invoke(null, $high, 1));
|
||||
self::assertSame(100000, $method->invoke(null, $high, 0));
|
||||
}
|
||||
|
||||
public function testEveryHistoricalEventSpecialHasAnImplementation(): void
|
||||
{
|
||||
$pool = json_decode(
|
||||
file_get_contents(self::POOL_PATH),
|
||||
true,
|
||||
512,
|
||||
JSON_THROW_ON_ERROR
|
||||
);
|
||||
$column = array_flip($pool['columns']);
|
||||
|
||||
foreach ($pool['data'] as $row) {
|
||||
$special = $row[$column['specialDomestic']];
|
||||
if ($special === null) {
|
||||
continue;
|
||||
}
|
||||
self::assertFileExists(
|
||||
__DIR__ . "/../hwe/sammo/ActionSpecialDomestic/{$special}.php"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function testEveryHistoricalEventSpecialIsExposedToTheFrontend(): void
|
||||
{
|
||||
$pool = json_decode(
|
||||
file_get_contents(self::POOL_PATH),
|
||||
true,
|
||||
512,
|
||||
JSON_THROW_ON_ERROR
|
||||
);
|
||||
$column = array_flip($pool['columns']);
|
||||
$registeredSpecials = array_flip(array_merge(
|
||||
GameConst::$availableSpecialDomestic,
|
||||
GameConst::$optionalSpecialDomestic
|
||||
));
|
||||
|
||||
foreach ($pool['data'] as $row) {
|
||||
$special = $row[$column['specialDomestic']];
|
||||
if ($special === null || $special === '') {
|
||||
continue;
|
||||
}
|
||||
self::assertArrayHasKey(
|
||||
$special,
|
||||
$registeredSpecials,
|
||||
"{$special} must be included in GetConst iActionInfo.specialDomestic"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
import fs from 'node:fs';
|
||||
import {createHash} from 'node:crypto';
|
||||
import {chromium} from 'playwright';
|
||||
|
||||
const baseURL = process.env.REF_BROWSER_URL;
|
||||
const screenshotPath = process.env.REF_SCREENSHOT_PATH;
|
||||
const resultPath = process.env.REF_RESULT_PATH;
|
||||
const username = process.env.REF_USER_ID ?? 's100user01';
|
||||
const password = fs.readFileSync('/run/secrets/test_user_password', 'utf8').trim();
|
||||
const expectedReason = '100기 올스타 장수는 능력치 초기화를 사용할 수 없습니다.';
|
||||
const expectedWarning = '100기 올스타 장수는 장수 전환 시 능력치 성장 기록을 보존하기 위해 능력치 초기화를 사용할 수 없습니다.';
|
||||
|
||||
if (!baseURL || !screenshotPath || !resultPath) {
|
||||
throw new Error('REF_BROWSER_URL, REF_SCREENSHOT_PATH, and REF_RESULT_PATH are required');
|
||||
}
|
||||
|
||||
const browser = await chromium.launch({headless: true});
|
||||
const context = await browser.newContext({
|
||||
viewport: {width: 1280, height: 960},
|
||||
deviceScaleFactor: 1,
|
||||
});
|
||||
const browserMessages = [];
|
||||
const attachPageListeners = targetPage => {
|
||||
targetPage.on('console', message => {
|
||||
browserMessages.push({type: message.type(), text: message.text()});
|
||||
});
|
||||
targetPage.on('pageerror', error => {
|
||||
browserMessages.push({type: 'pageerror', text: error.message});
|
||||
});
|
||||
};
|
||||
let page = await context.newPage();
|
||||
attachPageListeners(page);
|
||||
|
||||
await page.goto(baseURL, {waitUntil: 'domcontentloaded', timeout: 60_000});
|
||||
const salt = await page.locator('#global_salt').inputValue();
|
||||
const passwordHash = createHash('sha512')
|
||||
.update(salt + password + salt)
|
||||
.digest('hex');
|
||||
const loginResponse = await page.request.post(
|
||||
new URL('api.php?path=Login/LoginByID', baseURL).href,
|
||||
{data: {username, password: passwordHash}},
|
||||
);
|
||||
const loginResult = await loginResponse.json();
|
||||
if (!loginResponse.ok() || loginResult.result !== true) {
|
||||
throw new Error(`login failed: ${String(loginResult.reason ?? loginResponse.status())}`);
|
||||
}
|
||||
|
||||
const gameLoginResponse = await page.request.get(
|
||||
new URL('hwe/api.php?path=Global/GetConst', baseURL).href,
|
||||
);
|
||||
const gameLoginResult = await gameLoginResponse.json();
|
||||
if (!gameLoginResponse.ok() || gameLoginResult.result !== true) {
|
||||
throw new Error(`game login failed: ${JSON.stringify(gameLoginResult)}`);
|
||||
}
|
||||
|
||||
await page.close();
|
||||
browserMessages.length = 0;
|
||||
page = await context.newPage();
|
||||
attachPageListeners(page);
|
||||
await page.goto(
|
||||
new URL('hwe/v_inheritPoint.php', baseURL).href,
|
||||
{waitUntil: 'networkidle', timeout: 60_000},
|
||||
);
|
||||
const bodyText = await page.locator('body').innerText();
|
||||
if (bodyText.includes('#0 /var/www/html/')) {
|
||||
throw new Error('PHP stack trace was rendered in the inheritance page');
|
||||
}
|
||||
if (!bodyText.includes(expectedWarning)) {
|
||||
await page.screenshot({path: screenshotPath, fullPage: true});
|
||||
fs.writeFileSync(
|
||||
resultPath,
|
||||
`${JSON.stringify({
|
||||
username,
|
||||
url: page.url(),
|
||||
bodyText: bodyText.slice(0, 4000),
|
||||
browserMessages,
|
||||
}, null, 2)}\n`,
|
||||
{mode: 0o600},
|
||||
);
|
||||
throw new Error('S100 stat-reset restriction was not rendered');
|
||||
}
|
||||
if (await page.getByRole('button', {name: '능력치 초기화'}).count() !== 0) {
|
||||
throw new Error('S100 stat-reset button must not be rendered');
|
||||
}
|
||||
|
||||
const warning = page.getByText(expectedWarning, {exact: true});
|
||||
await warning.waitFor({state: 'visible', timeout: 60_000});
|
||||
const warningGeometry = await warning.evaluate(element => {
|
||||
const rect = element.getBoundingClientRect();
|
||||
const style = getComputedStyle(element);
|
||||
return {
|
||||
rect: {
|
||||
x: rect.x,
|
||||
y: rect.y,
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
},
|
||||
style: {
|
||||
color: style.color,
|
||||
fontFamily: style.fontFamily,
|
||||
fontSize: style.fontSize,
|
||||
lineHeight: style.lineHeight,
|
||||
},
|
||||
};
|
||||
});
|
||||
const beforeState = await page.evaluate(() => ({
|
||||
currentStat: staticValues.currentStat,
|
||||
previousPoint: staticValues.items.previous,
|
||||
}));
|
||||
|
||||
const resetResponse = await page.request.put(
|
||||
new URL('hwe/api.php?path=InheritAction/ResetStat', baseURL).href,
|
||||
{
|
||||
data: {
|
||||
leadership: 55,
|
||||
strength: 55,
|
||||
intel: 55,
|
||||
},
|
||||
},
|
||||
);
|
||||
const resetResult = await resetResponse.json();
|
||||
if (!resetResponse.ok()
|
||||
|| resetResult.result !== false
|
||||
|| resetResult.reason !== expectedReason
|
||||
) {
|
||||
throw new Error(`unexpected ResetStat response: ${JSON.stringify(resetResult)}`);
|
||||
}
|
||||
|
||||
await page.reload({waitUntil: 'networkidle', timeout: 60_000});
|
||||
const afterState = await page.evaluate(() => ({
|
||||
currentStat: staticValues.currentStat,
|
||||
previousPoint: staticValues.items.previous,
|
||||
}));
|
||||
if (JSON.stringify(afterState) !== JSON.stringify(beforeState)) {
|
||||
throw new Error(`ResetStat rejection changed state: ${JSON.stringify({beforeState, afterState})}`);
|
||||
}
|
||||
|
||||
await page.screenshot({path: screenshotPath, fullPage: true});
|
||||
fs.writeFileSync(
|
||||
resultPath,
|
||||
`${JSON.stringify({
|
||||
username,
|
||||
url: page.url(),
|
||||
viewport: {width: 1280, height: 960, deviceScaleFactor: 1},
|
||||
warningGeometry,
|
||||
resetResult,
|
||||
beforeState,
|
||||
afterState,
|
||||
browserMessages,
|
||||
}, null, 2)}\n`,
|
||||
{mode: 0o600},
|
||||
);
|
||||
|
||||
await browser.close();
|
||||
console.log(`S100 inheritance stat-reset guard verified: ${screenshotPath}`);
|
||||
@@ -0,0 +1,76 @@
|
||||
import fs from 'node:fs';
|
||||
import {createHash} from 'node:crypto';
|
||||
import {chromium} from 'playwright';
|
||||
|
||||
const baseURL = process.env.REF_BROWSER_URL;
|
||||
const screenshotPath = process.env.REF_SCREENSHOT_PATH;
|
||||
const resultPath = process.env.REF_RESULT_PATH;
|
||||
const username = process.env.REF_USER_ID ?? 's100user01';
|
||||
const password = fs.readFileSync('/run/secrets/test_user_password', 'utf8').trim();
|
||||
|
||||
if (!baseURL || !screenshotPath || !resultPath) {
|
||||
throw new Error('REF_BROWSER_URL, REF_SCREENSHOT_PATH, and REF_RESULT_PATH are required');
|
||||
}
|
||||
|
||||
const browser = await chromium.launch({headless: true});
|
||||
const context = await browser.newContext({
|
||||
viewport: {width: 1280, height: 960},
|
||||
deviceScaleFactor: 1,
|
||||
});
|
||||
const page = await context.newPage();
|
||||
|
||||
await page.goto(baseURL, {waitUntil: 'domcontentloaded', timeout: 60_000});
|
||||
const salt = await page.locator('#global_salt').inputValue();
|
||||
const passwordHash = createHash('sha512')
|
||||
.update(salt + password + salt)
|
||||
.digest('hex');
|
||||
const loginResponse = await page.request.post(
|
||||
new URL('api.php?path=Login/LoginByID', baseURL).href,
|
||||
{data: {username, password: passwordHash}},
|
||||
);
|
||||
const loginResult = await loginResponse.json();
|
||||
if (!loginResponse.ok() || loginResult.result !== true) {
|
||||
throw new Error(`login failed: ${String(loginResult.reason ?? loginResponse.status())}`);
|
||||
}
|
||||
|
||||
const myPageURL = new URL('hwe/b_myPage.php', baseURL).href;
|
||||
const readCooldownText = async () => {
|
||||
await page.goto(myPageURL, {waitUntil: 'networkidle', timeout: 60_000});
|
||||
const bodyText = await page.locator('body').innerText();
|
||||
const lines = bodyText.split('\n').map(line => line.trim());
|
||||
const reselect = lines.find(line => line.startsWith('다른 장수 선택 ('));
|
||||
const deletion = lines.find(line => line.startsWith('가오픈 기간 내 장수 삭제 ('));
|
||||
if (!reselect || !deletion) {
|
||||
throw new Error(`cooldown text missing: ${bodyText.slice(0, 4000)}`);
|
||||
}
|
||||
return {reselect, deletion};
|
||||
};
|
||||
|
||||
const first = await readCooldownText();
|
||||
await page.waitForTimeout(1100);
|
||||
const second = await readCooldownText();
|
||||
await page.waitForTimeout(1100);
|
||||
const third = await readCooldownText();
|
||||
|
||||
if (JSON.stringify(first) !== JSON.stringify(second)
|
||||
|| JSON.stringify(second) !== JSON.stringify(third)
|
||||
) {
|
||||
throw new Error(`cooldown moved after refresh: ${JSON.stringify({first, second, third})}`);
|
||||
}
|
||||
|
||||
await page.screenshot({path: screenshotPath, fullPage: true});
|
||||
fs.writeFileSync(
|
||||
resultPath,
|
||||
`${JSON.stringify({
|
||||
username,
|
||||
url: page.url(),
|
||||
viewport: {width: 1280, height: 960, deviceScaleFactor: 1},
|
||||
first,
|
||||
second,
|
||||
third,
|
||||
}, null, 2)}\n`,
|
||||
{mode: 0o600},
|
||||
);
|
||||
|
||||
await browser.close();
|
||||
console.log(`S100 my-page cooldown refresh guard verified: ${screenshotPath}`);
|
||||
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use sammo\CentennialAllStarGrowthService;
|
||||
use sammo\DB;
|
||||
use sammo\GameConst;
|
||||
use sammo\General;
|
||||
use sammo\GeneralPool\SPoolUnderU100;
|
||||
use sammo\Json;
|
||||
use sammo\LiteHashDRBG;
|
||||
use sammo\RandUtil;
|
||||
use sammo\Util;
|
||||
|
||||
const APP_ROOT = '/var/www/html';
|
||||
const TARGET_NAMES = ['43·페르난도', '47·우마무스메'];
|
||||
|
||||
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
|
||||
$_SERVER['REQUEST_URI'] = '/s100-generated-npc-stat-floor-check';
|
||||
|
||||
require APP_ROOT . '/hwe/lib.php';
|
||||
require APP_ROOT . '/hwe/func.php';
|
||||
|
||||
$env = [
|
||||
'startyear' => 180,
|
||||
'year' => 180,
|
||||
'month' => 12,
|
||||
'fiction' => [1],
|
||||
'show_img_level' => 3,
|
||||
];
|
||||
$pool = Json::decode((string) file_get_contents(
|
||||
APP_ROOT . '/hwe/sammo/GeneralPool/Pool/UnderS100.json'
|
||||
));
|
||||
$columns = array_flip($pool['columns']);
|
||||
$targets = [];
|
||||
foreach ($pool['data'] as $idx => $row) {
|
||||
$name = $row[$columns['generalName']];
|
||||
if (!in_array($name, TARGET_NAMES, true)) {
|
||||
continue;
|
||||
}
|
||||
$info = array_combine($pool['columns'], $row);
|
||||
$info['uniqueName'] = sprintf('A100%04d', $idx + 1);
|
||||
$info['event100Growth'] = true;
|
||||
$targets[$name] = $info;
|
||||
}
|
||||
if (count($targets) !== count(TARGET_NAMES)) {
|
||||
throw new RuntimeException('Required S100 candidates are missing');
|
||||
}
|
||||
|
||||
$db = DB::db();
|
||||
$results = [];
|
||||
foreach (TARGET_NAMES as $name) {
|
||||
$target = $targets[$name];
|
||||
$rng = new RandUtil(new LiteHashDRBG(Util::simpleSerialize(
|
||||
's100-generated-npc-stat-floor-check',
|
||||
$target['uniqueName']
|
||||
)));
|
||||
$poolGeneral = new SPoolUnderU100(
|
||||
$db,
|
||||
$rng,
|
||||
$target,
|
||||
'2999-01-01 00:00:00'
|
||||
);
|
||||
$builder = $poolGeneral->getGeneralBuilder();
|
||||
$builder->setNationID(0)
|
||||
->setNPCType(3)
|
||||
->setMoney(1000, 1000)
|
||||
->setExpDed(0, 0)
|
||||
->setLifeSpan(160, 230);
|
||||
$builder->fillRandomStat(['무' => 0.333, '지' => 0.333, '무지' => 0.334]);
|
||||
$generatedStats = $builder->getStat();
|
||||
assertSameValue(
|
||||
GameConst::$defaultStatNPCTotal,
|
||||
array_sum($generatedStats),
|
||||
"{$name} generic total"
|
||||
);
|
||||
$builder->fillRemainSpecAsZero($env);
|
||||
$builder->build($env);
|
||||
CentennialAllStarGrowthService::applyCurrentTargetToBuiltNPC(
|
||||
$db,
|
||||
$builder,
|
||||
$target,
|
||||
$env
|
||||
);
|
||||
|
||||
$general = General::createObjFromDB($builder->getGeneralID());
|
||||
$actualStats = [
|
||||
'leadership' => (int) $general->getVar('leadership'),
|
||||
'strength' => (int) $general->getVar('strength'),
|
||||
'intel' => (int) $general->getVar('intel'),
|
||||
];
|
||||
if (array_sum($actualStats) < GameConst::$defaultStatNPCTotal) {
|
||||
throw new RuntimeException(
|
||||
"{$name} total fell below " . GameConst::$defaultStatNPCTotal
|
||||
);
|
||||
}
|
||||
|
||||
$targetOrder = ['leadership', 'strength', 'intel'];
|
||||
usort(
|
||||
$targetOrder,
|
||||
static fn (string $lhs, string $rhs): int =>
|
||||
(int) $target[$rhs] <=> (int) $target[$lhs]
|
||||
);
|
||||
if ($actualStats[$targetOrder[0]] < $actualStats[$targetOrder[1]]
|
||||
|| $actualStats[$targetOrder[1]] < $actualStats[$targetOrder[2]]
|
||||
) {
|
||||
throw new RuntimeException("{$name} target stat order was not preserved");
|
||||
}
|
||||
|
||||
$results[] = [
|
||||
'name' => $name,
|
||||
'target' => array_intersect_key($target, $actualStats),
|
||||
'generic' => array_combine(
|
||||
['leadership', 'strength', 'intel'],
|
||||
$generatedStats
|
||||
),
|
||||
'actual' => $actualStats,
|
||||
'total' => array_sum($actualStats),
|
||||
];
|
||||
}
|
||||
|
||||
echo Json::encode($results, Json::PRETTY), PHP_EOL;
|
||||
|
||||
function assertSameValue(int $expected, int $actual, string $label): void
|
||||
{
|
||||
if ($actual !== $expected) {
|
||||
throw new RuntimeException(
|
||||
"{$label}: expected {$expected}, got {$actual}"
|
||||
);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user