유산 포인트 시스템, 추가 기수 대비 #187

Merged
Hide_D merged 29 commits from Inheritance into devel 2021-08-11 21:57:53 +09:00
44 changed files with 1377 additions and 142 deletions
+18
View File
@@ -0,0 +1,18 @@
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"browsers": [
"last 2 versions"
]
},
"modules":false
}
]
],
"plugins": [
"lodash"
]
}
+22
View File
@@ -0,0 +1,22 @@
module.exports = {
root: true,
parser: "@typescript-eslint/parser",
parserOptions: {
"project": "./tsconfig.json"
},
ignorePatterns: ['*.test.ts', '.eslintrc.js', 'webpack.config.js', '*.js'],
overrides: [{
files: ['*.ts', '*.tsx'],
}],
plugins: [
"@typescript-eslint",
],
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
],
rules: {
'@typescript-eslint/no-floating-promises': 'error',
}
}
+2 -1
View File
@@ -4,7 +4,8 @@
!.vscode/launch.json
!.vscode/extensions.json
/node_modules
package-lock.json
# 체섭 ignore
+4
View File
@@ -24,3 +24,7 @@ foreach ($db->queryFirstColumn(
) as $generalNo) {
CheckHall($generalNo);
}
foreach(General::createGeneralObjListFromDB($db->queryFirstColumn('SELECT `no` FROM general WHERE npc = 0')) as $genObj){
$genObj->mergeTotalInheritancePoint();
}
+131
View File
@@ -0,0 +1,131 @@
<?php
namespace sammo;
include "lib.php";
include "func.php";
//로그인 검사
$session = Session::requireGameLogin()->setReadOnly();
$userID = Session::getUserID();
$generalID = $session->generalID;
$db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env');
$me = General::createGeneralObjFromDB($generalID);
$pointHelpText = [
'sum' => '다음 플레이에서 사용할 수 있는 총 포인트입니다.',
'new' => '이번 플레이에서 얻은 총 포인트입니다.',
'previous' => '이전에 물려받은 포인트입니다.',
'lived_month' => '살아남은 기간입니다. (1개월 단위)',
'max_belong' => '가장 오래 임관했던 국가의 연도입니다.',
'max_domestic_critical' => '성공한 내정 중 최대 연속값입니다.',
'snipe_combat' => '유리한 상성을 가지고 전투했습니다.',
'combat' => '전투 횟수입니다.',
'sabotage' => '계략 성공 횟수입니다.',
'unifier' => '천통에 기여한 포인트입니다. <br>각 국의 군주, 천통 수뇌, 천통 군주가 받습니다.',
'dex' => '총 숙련도합입니다.',
'tournament' => '토너먼트 입상 포인트입니다.',
'betting' => '성공적인 베팅을 했습니다. <br>수익율과 베팅 성공 횟수를 따릅니다.',
];
$items = [];
foreach(array_keys(General::INHERITANCE_KEY) as $key){
$items[$key] = $me->getInheritancePoint($key)??0;
}
?>
<!DOCTYPE html>
<html>
<head>
<title><?= UniqueConst::$serverName ?>: 유산 관리</title>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=1024" />
<?= WebUtil::printCSS('../e_lib/bootstrap.min.css') ?>
<?= WebUtil::printCSS('../d_shared/common.css') ?>
<?= WebUtil::printCSS('../css/config.css') ?>
<?= WebUtil::printCSS('css/common.css') ?>
<?= WebUtil::printCSS('css/inheritPoint.css') ?>
<?= WebUtil::printJS('../d_shared/common_path.js') ?>
<?= WebUtil::printJS('../e_lib/jquery-3.3.1.min.js') ?>
<?= WebUtil::printJS('../e_lib/bootstrap.bundle.min.js') ?>
<?= WebUtil::printJS('js/common.js') ?>
<?= WebUtil::printJS('js/inheritPoint.js') ?>
<script>
var items = <?=Json::encode($items)?>;
var helpText = <?=Json::encode($pointHelpText)?>;
</script>
</head>
<body onload="formStart()">
<table style='width:1000px;margin:auto;' class='tb_layout bg0'>
<tr>
<td style='text-align:left;'>유산 관리<br><?= backButton() ?></td>
</tr>
</table>
<div id="container" class="tb_layout bg0" style="width:1000px;margin:auto;border:solid 1px #888888;">
<div id='inheritance_list'>
<div id="inherit_sum" class='inherit_item'>
<div class="row">
<label id="inherit_sum_head" class='inherit_head col-sm-6 col-form-label'>총 포인트</label>
<div class="col-sm-6">
<input type="text" class="form-control inherit_value" readonly id="inherit_sum_value" value="">
</div>
</div>
<div style="text-align:right">
<small class="form-text text-muted"></small>
</div>
</div>
<div id="inherit_previous" class='inherit_item'>
<div class="row">
<label id="inherit_sum_head" class='inherit_head col-sm-6 col-form-label'>기존 포인트</label>
<div class="col-sm-6">
<input type="text" class="form-control inherit_value" readonly id="inherit_previous_value" value="">
</div>
</div>
<div style="text-align:right">
<small class="form-text text-muted"></small>
</div>
</div>
<div id="inherit_new" class='inherit_item'>
<div class="row">
<label id="inherit_sum_head" class='inherit_head col-sm-6 col-form-label'>신규 포인트</label>
<div class="col-sm-6">
<input type="text" class="form-control inherit_value" readonly id="inherit_new_value" value="">
</div>
</div>
<div style="text-align:right">
<small class="form-text text-muted"></small>
</div>
</div>
<div style="width:100%; padding:0 10px"><hr style="border-top:1px solid #888888;"></div>
<?php foreach (General::INHERITANCE_KEY as $key => [$type, $multiplier, $name]) : ?>
<?php if ($key == 'previous') {
continue;
} ?>
<div id="inherit_<?= $key ?>" class='inherit_item inherit_template_item'>
<div class="row">
<label id="inherit_<?= $key ?>_head" class='inherit_head col-sm-6 col-form-label'><?= $name ?></label>
<div class="col-sm-6">
<input type="text" class="form-control inherit_value" readonly id="inherit_<?= $key ?>_value" value="">
</div>
</div>
<div style="text-align:right">
<small class="form-text text-muted"></small>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
</body>
</html>
+1
View File
@@ -91,6 +91,7 @@ $zeroPolicy = new AutorunNationPolicy($general, ($gameStor->autorun_user)['optio
var availableGeneralActionPriorityItems = <?= Json::encode(AutorunGeneralPolicy::$default_priority) ?>;
var btnHelpMessage = {
'불가침제의': '군주가 NPC이고, 타국에서 원조를 받았을 때,<br>세입(금/쌀) 대비 원조량에 따라 불가침제의를 합니다.',
'선전포고': '군주가 NPC이고, 전쟁중이 아닐 때,<br>주변국중 하나를 골라 선포합니다.<br><br>선포 시점은 다음을 참고합니다.<br>- 인구율<br>- 도시내정률<br>- NPC전투장권장 금 충족률<br>- NPC전투장권장 쌀 충족률<br><br>국력이 낮은 국가를 조금 더 선호합니다.',
'천도': '인구가 많은 곳을 찾아 천도를 시도합니다.<br>영토의 가운데를 선호합니다.<br><br>도시 인구가 충분하다면, 굳이 천도하지는 않습니다.',
'유저장긴급포상': '금/쌀이 부족한 유저전투장에게 긴급하게 포상합니다.<br>국고가 권장량보다 적어지더라도 시도합니다.',
+23
View File
@@ -0,0 +1,23 @@
#inheritance_list{
display: flex;
flex-wrap: wrap;
}
.inherit_padding{
width: 33%;
padding: 10px 2px;
}
.inherit_item{
width: 33%;
padding: 10px 2px;
}
.col-form-label{
text-align:right;
padding-right:2ch;
}
.inherit_value{
text-align: right;
}
+7 -1
View File
@@ -1707,7 +1707,7 @@ function deleteNation(General $lord, bool $applyDB):array
$nationID,
$lordID
),
['npc', 'gold', 'rice', 'experience', 'explevel', 'dedication', 'dedlevel', 'aux'], 1
['npc', 'gold', 'rice', 'experience', 'explevel', 'dedication', 'dedlevel', 'belong', 'aux'], 1
);
$nationGeneralList[$lordID] = $lord;
@@ -1724,6 +1724,12 @@ function deleteNation(General $lord, bool $applyDB):array
// 전 장수 재야로
foreach($nationGeneralList as $general){
$general->setAuxVar('max_belong',
max(
$general->getVar('belong'),
$general->getAuxVar('max_belong')??0
)
);
$general->setVar('belong', 0);
$general->setVar('troop', 0);
$general->setVar('officer_level', 0);
+40 -1
View File
@@ -623,10 +623,14 @@ function updateNationState()
$nation['nation'],
$targetKillTurn
);
$nationGenList = General::createGeneralObjListFromDB($nationGenIDList, ['belong'], 2);
$nationGenList = General::createGeneralObjListFromDB($nationGenIDList, ['belong', 'npc'], 2);
$chiefObj = null;
$uniqueLotteryWeightList = [];
foreach ($nationGenList as $nationGen) {
if ($nationGen->getVar('officer_level') == 12) {
$chiefObj = $nationGen;
}
$trialCnt = count(GameConst::$allItems);
foreach ($nationGen->getItems() as $item) {
@@ -665,6 +669,10 @@ function updateNationState()
giveRandomUniqueItem($winnerObj, '작위보상');
$winnerObj->applyDB($db);
}
if($chiefObj){
$chiefObj->increaseInheritancePoint('unifier', 250 * $levelDiff);
}
}
}
@@ -982,6 +990,15 @@ function checkEmperior()
$nationLogger = new ActionLogger(0, $nationID, $admin['year'], $admin['month']);
$nationLogger->pushNationalHistoryLog("<D><b>{$nationName}</b></>{$josaYi} 전토를 통일");
foreach(General::createGeneralObjListFromDB($db->queryFirstColumn('SELECT `no` FROM general WHERE npc = 0')) as $genObj){
if($genObj->getNationID() == $nationID){
if($genObj->getVar('officer_level') > 4){
$genObj->increaseInheritancePoint('unifier', 2000);
};
}
$genObj->mergeTotalInheritancePoint();
}
$gameStor->isunited = 2;
$gameStor->conlimit = $gameStor->conlimit * 100;
@@ -1132,3 +1149,25 @@ function checkEmperior()
//연감 월결산
LogHistory();
}
function resetInheritanceUser(int $userID){
$inheritStor = KVStorage::getStorage(DB::db(), "inheritance_{$userID}");
$totalPoint = 0;
foreach($inheritStor->getAll() as [$value,]){
$totalPoint += $value;
}
$totalPoint = Util::toInt($totalPoint);
$inheritStor->resetValues();
$inheritStor->setValue('previous', [$totalPoint, null]);
}
function updateMaxDomesticCritical(General $general, $score){
$maxDomesticCritical = $general->getAuxVar('max_domestic_critical')??0;
$maxDomesticCritical += $score / 2;
$general->setAuxVar('max_domestic_critical', $maxDomesticCritical);
$oldMaxDomesticCritical = $general->getInheritancePoint('max_domestic_critical');
if($maxDomesticCritical > $oldMaxDomesticCritical){
$general->setInheritancePoint('max_domestic_critical', $maxDomesticCritical);
}
}
+11 -6
View File
@@ -689,7 +689,8 @@ function setGift($tnmt_type, $tnmt, $phase) {
'grp_no'=>$general['grp_no'],
'reward'=>$cost,
'msg'=>"<span class='ev_highlight'>16강 진출</span>",
'logger'=>$logger
'logger'=>$logger,
'inheritance_point'=>10,
];
}
//8강자 명성 돈
@@ -723,6 +724,7 @@ function setGift($tnmt_type, $tnmt, $phase) {
//포상 장수 이름, 금액
$resultHelper[$generalID]['reward'] += $cost;
$resultHelper[$generalID]['msg'] = "<span class='ev_highlight'>4강 진출</span>";
General::createGeneralObjFromDB($generalID)->increaseInheritancePoint('tournament', 10);
}
//결승자 명성 돈
$cost = $admin['develcost'] * 6;
@@ -739,6 +741,7 @@ function setGift($tnmt_type, $tnmt, $phase) {
//포상 장수 이름, 금액
$resultHelper[$generalID]['reward'] += $cost;
$resultHelper[$generalID]['msg'] = "<span class='ev_highlight'>준우승</span>으";
$resultHelper[$generalID]['inheritance_point'] = 50;
if($general['lose'] > 0){
$runnerUp = $general;
}
@@ -761,6 +764,7 @@ function setGift($tnmt_type, $tnmt, $phase) {
//포상 장수 이름, 금액
$resultHelper[$generalID]['reward'] += $cost;
$resultHelper[$generalID]['msg'] = "<span class='ev_highlight'>우승</span>으";
$resultHelper[$generalID]['inheritance_point'] = 100;
$winner = $general;
}
@@ -775,9 +779,6 @@ function setGift($tnmt_type, $tnmt, $phase) {
$runnerUpLogger = $resultHelper[$runnerUp['no']]['logger'];
$runnerUpLogger->pushGeneralHistoryLog("<C>{$tp}</> 대회에서 준우승");
$winnerRewardText = number_format($resultHelper[$winner['no']]['reward']);
$runnerUpRewardText = number_format($resultHelper[$runnerUp['no']]['reward']);
@@ -786,12 +787,16 @@ function setGift($tnmt_type, $tnmt, $phase) {
$winnerLogger->pushGlobalHistoryLog("<C>{$tp}</> 대회에서 <Y>{$winner['name']}</>{$josaYiWinner} <C>우승</>, <Y>{$runnerUp['name']}</>{$josaYiRunnerUp} <C>준우승</>을 차지하여 천하에 이름을 떨칩니다!", ActionLogger::EVENT_YEAR_MONTH);
$winnerLogger->pushGlobalHistoryLog("<C>{$tp}</> 대회의 <S>우승자</>에게는 <C>{$winnerRewardText}</>, <S>준우승자</>에겐 <C>{$runnerUpRewardText}</>의 <S>상금</>과 약간의 <S>명성</>이 주어집니다!", ActionLogger::EVENT_YEAR_MONTH);
foreach($resultHelper as $general){
$generalObjList = General::createGeneralObjListFromDB(array_keys($resultHelper));
foreach($resultHelper as $generalID=>$general){
$rewardText = number_format($general['reward']);
/** @var ActionLogger */
$logger = $general['logger'];
$logger->pushGeneralActionLog("<C>{$tp}</> 대회의 {$general['msg']}로 <C>{$rewardText}</>의 <S>상금</>, 약간의 <S>명성</> 획득!", ActionLogger::EVENT_PLAIN);
//TODO: 토너먼트의 다른 값이 모두 sql에 직접 입력하므로 기반을 바꿔야함.
$generalObjList[$generalID]->increaseInheritancePoint('tournament', $general['inheritance_point']);
}
//우승자 번호
+2
View File
@@ -140,6 +140,7 @@ if(!$v->validate()){
'reason'=>'[출병자]'.$v->errorStr()
]);
}
$rawAttacker['owner'] = 0;
$defenderList = [];
foreach($rawDefenderList as $idx=>$rawDefenderGeneral){
@@ -152,6 +153,7 @@ foreach($rawDefenderList as $idx=>$rawDefenderGeneral){
'reason'=>"[수비자{$idx}]".$v->errorStr()
]);
}
$rawDefenderGeneral['owner'] = 0;
$defenderList[] = new General($rawDefenderGeneral, extractRankVar($rawDefenderGeneral), $rawDefenderCity, $rawAttackerNation, $year, $month, true);
}
+2
View File
@@ -266,6 +266,8 @@ foreach(Util::range(GameConst::$maxTurn) as $turnIdx){
}
$db->insert('general_turn', $turnRows);
resetInheritanceUser($userID);
$rank_data = [];
foreach(array_keys(General::RANK_COLUMN) as $rankColumn){
$rank_data[] = [
File diff suppressed because one or more lines are too long
+8
View File
@@ -0,0 +1,8 @@
/**
* @license
* Lodash <https://lodash.com/>
* Copyright OpenJS Foundation and other contributors <https://openjsf.org/>
* Released under MIT license <https://lodash.com/license>
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
*/
File diff suppressed because one or more lines are too long
+10 -1
View File
@@ -261,6 +261,15 @@ function processWar_NG(
$attacker->addTrain(1);
$defender->addTrain(1);
$attackerCrewTypeCoef = $attacker->getCrewType()->getAttackCoef($defender->getCrewType()) * $defender->getCrewType()->getDefenceCoef($attacker->getCrewType());
$defenderCrewTypeCoef = $defender->getCrewType()->getAttackCoef($attacker->getCrewType()) * $attacker->getCrewType()->getDefenceCoef($defender->getCrewType());
if($attackerCrewTypeCoef > $defenderCrewTypeCoef && $attacker instanceof WarUnitGeneral){
$attacker->getGeneral()->increaseInheritancePoint('snipe_combat', 1);
}
if($defenderCrewTypeCoef > $attackerCrewTypeCoef && $defender instanceof WarUnitGeneral){
$defender->getGeneral()->increaseInheritancePoint('snipe_combat', 1);
}
$attackerName = $attacker->getName();
$attackerCrewTypeName = $attacker->getCrewTypeName();
@@ -522,7 +531,7 @@ function ConquerCity(array $admin, General $general, array $city) {
$lord = new General($db->queryFirstRow(
'SELECT %l FROM general WHERE nation = %i AND officer_level = %i LIMIT 1',
Util::formatListOfBackticks(General::mergeQueryColumn(['npc', 'gold', 'rice', 'experience', 'explevel', 'dedication', 'dedlevel', 'aux'], 1)[0]),
Util::formatListOfBackticks(General::mergeQueryColumn(['npc', 'gold', 'rice', 'experience', 'explevel', 'belong', 'dedication', 'dedlevel', 'aux'], 1)[0]),
$defenderNationID,
12
), null, $city, $loseNation, $year, $month, false);
+3
View File
@@ -28,6 +28,7 @@ class AutorunNationPolicy {
static $NPC몰수 = 'NPC몰수';
// 군주 행동
static $불가침제의 = '불가침제의';
static $선전포고 = '선전포고';
static $천도 = '천도';
@@ -35,6 +36,7 @@ class AutorunNationPolicy {
//실제 행동
static public $defaultPriority = [
'불가침제의',
'선전포고',
'천도',
@@ -106,6 +108,7 @@ class AutorunNationPolicy {
public $canNPC포상 = true;
public $canNPC몰수 = true;
public $can불가침제의 = true;
public $can선전포고 = true;
public $can천도 = true;
@@ -14,7 +14,8 @@ use function sammo\{
getDomesticExpLevelBonus,
CriticalRatioDomestic,
CriticalScoreEx,
tryUniqueItemLottery
tryUniqueItemLottery,
updateMaxDomesticCritical
};
use \sammo\Constraint\Constraint;
@@ -93,6 +94,13 @@ class che_기술연구 extends che_상업투자{
$exp = $score * 0.7;
$ded = $score * 1.0;
if($pick == 'success'){
updateMaxDomesticCritical($general, $score);
}
else{
$general->setAuxVar('max_domestic_critical', 0);
}
$scoreText = number_format($score, 0);
$josaUl = JosaUtil::pick(static::$actionName, '을');
+86 -81
View File
@@ -1,21 +1,22 @@
<?php
namespace sammo\Command\General;
use \sammo\{
DB, Util, JosaUtil, TimeUtil, Json,
General,
ActionLogger,
GameConst, GameUnitConst,
LastTurn,
Command
};
use \sammo\DB;
use \sammo\Util;
use \sammo\JosaUtil;
use \sammo\TimeUtil;
use \sammo\Json;
use \sammo\General;
use \sammo\ActionLogger;
use \sammo\GameConst;
use \sammo\GameUnitConst;
use \sammo\LastTurn;
use \sammo\Command;
use function \sammo\{
tryUniqueItemLottery,
getInvitationList,
getNationStaticInfo
};
use function \sammo\tryUniqueItemLottery;
use function \sammo\getInvitationList;
use function \sammo\getNationStaticInfo;
use \sammo\Constraint\Constraint;
use \sammo\Constraint\ConstraintHelper;
@@ -24,10 +25,12 @@ use sammo\MustNotBeReachedException;
class che_랜덤임관 extends Command\GeneralCommand{
class che_랜덤임관 extends Command\GeneralCommand
{
static protected $actionName = '랜덤임관';
protected function argTest():bool{
protected function argTest(): bool
{
$this->arg = null;
return true;
@@ -71,7 +74,7 @@ class che_랜덤임관 extends Command\GeneralCommand{
$relYear = $env['year'] - $env['startyear'];
$this->fullConditionConstraints=[
$this->fullConditionConstraints = [
ConstraintHelper::BeNeutral(),
ConstraintHelper::AllowJoinAction(),
];
@@ -83,28 +86,34 @@ class che_랜덤임관 extends Command\GeneralCommand{
*/
}
public function getCommandDetailTitle():string{
public function getCommandDetailTitle(): string
{
return '무작위 국가로 임관';
}
public function getBrief():string{
public function getBrief(): string
{
return '무작위 국가로 임관';
}
public function getCost():array{
public function getCost(): array
{
return [0, 0];
}
public function getPreReqTurn():int{
public function getPreReqTurn(): int
{
return 0;
}
public function getPostReqTurn():int{
public function getPostReqTurn(): int
{
return 0;
}
public function run():bool{
if(!$this->hasFullConditionMet()){
public function run(): bool
{
if (!$this->hasFullConditionMet()) {
throw new \RuntimeException('불가능한 커맨드를 강제로 실행 시도');
}
@@ -126,108 +135,107 @@ class che_랜덤임관 extends Command\GeneralCommand{
$destNation = null;
if ($general->getNPCType() >= 2 && !$env['fiction'] && 1000 <= $env['scenario'] && $env['scenario'] < 2000) {
if($notIn){
if ($notIn) {
$nations = $db->query(
'SELECT nation.`name` as `name`,nation.nation as nation,scout,gennum,`affinity` FROM nation join general on general.nation = nation.nation and general.officer_level = 12 WHERE scout=0 and gennum<%i and nation.nation not in %li',
$relYear<3?GameConst::$initialNationGenLimit:GameConst::$defaultMaxGeneral,
$relYear < 3 ? GameConst::$initialNationGenLimit : GameConst::$defaultMaxGeneral,
$notIn
);
}
else{
} else {
$nations = $db->query(
'SELECT nation.`name` as `name`,nation.nation as nation,scout,gennum,`affinity` FROM nation join general on general.nation = nation.nation and general.officer_level = 12 WHERE scout=0 and gennum<%i',
$relYear<3?GameConst::$initialNationGenLimit:GameConst::$defaultMaxGeneral
$relYear < 3 ? GameConst::$initialNationGenLimit : GameConst::$defaultMaxGeneral
);
}
shuffle($nations);
$allGen = Util::arraySum($nations, 'gennum');
$maxScore = 1<<30;
$maxScore = 1 << 30;
$affinity = $general->getVar('affinity');
foreach($nations as $testNation){
foreach ($nations as $testNation) {
$affinity = abs($affinity - $testNation['affinity']);
$affinity = min($affinity, abs($affinity - 150));
$score = log($affinity + 1, 2);//0~
$score = log($affinity + 1, 2); //0~
//쉐킷쉐킷
$score += Util::randF();
$score += sqrt($testNation['gennum']/$allGen);
if($score < $maxScore){
$score += sqrt($testNation['gennum'] / $allGen);
if ($score < $maxScore) {
$maxScore = $score;
$destNation = $testNation;
}
}
}
else{
} else {
$onlyRandom = $env['join_mode'] == 'onlyRandom';
$genLimit = GameConst::$defaultMaxGeneral;
if($onlyRandom && TimeUtil::IsRangeMonth($env['init_year'], $env['init_month'], 1, $env['year'], $env['month'])){
if ($onlyRandom && TimeUtil::IsRangeMonth($env['init_year'], $env['init_month'], 1, $env['year'], $env['month'])) {
$genLimit = GameConst::$initialNationGenLimitForRandInit;
}
else if($relYear < 3){
} else if ($relYear < 3) {
$genLimit = GameConst::$initialNationGenLimit;
}
$generalsCnt = [];
if($notIn){
if ($notIn) {
$rawGeneralsCnt = $db->query(
'SELECT general.nation as nation, nation.gennum, nation.name, npc, count(*) as cnt FROM general JOIN nation ON general.nation = nation.nation WHERE npc < 4 AND nation.gennum < %i AND nation.scout=0 AND nation.nation NOT IN %li GROUP BY general.nation, general.npc',
'SELECT general.nation as nation, nation.gennum, nation.name, npc, count(*) as cnt FROM general JOIN nation ON general.nation = nation.nation WHERE npc != 9 AND nation.gennum < %i AND nation.scout=0 AND nation.nation NOT IN %li GROUP BY general.nation, general.npc',
$genLimit,
$notIn
);
}
else{
} else {
$rawGeneralsCnt = $db->query(
'SELECT general.nation as nation, nation.gennum, nation.name, npc, count(*) as cnt FROM general JOIN nation ON general.nation = nation.nation WHERE npc < 4 AND nation.gennum < %i AND nation.scout=0 GROUP BY general.nation, general.npc',
'SELECT general.nation as nation, nation.gennum, nation.name, npc, count(*) as cnt FROM general JOIN nation ON general.nation = nation.nation WHERE npc != 9 AND nation.gennum < %i AND nation.scout=0 GROUP BY general.nation, general.npc',
$genLimit
);
}
foreach($rawGeneralsCnt as $nation){
foreach ($rawGeneralsCnt as $nation) {
$nationID = $nation['nation'];
if(!\key_exists($nationID, $generalsCnt)){
if (!\key_exists($nationID, $generalsCnt)) {
$generalsCnt[$nationID] = [
'nation'=>$nationID,
'gennum'=>$nation['gennum'],
'name'=>$nation['name'],
'cnt'=>0,
'nation' => $nationID,
'gennum' => $nation['gennum'],
'name' => $nation['name'],
'cnt' => 0,
];
$generalsCnt[$nationID]['cnt'] = 0;
}
if($nation['npc'] <= 2){
if ($nation['npc'] <= 2) {
$calcCnt = $nation['cnt'];
}
else{
} else {
$calcCnt = $nation['cnt'] / 2;
}
if ($general->getNPCType() < 2 && Util::starts_with($nation['name'], 'ⓤ')) {
$calcCnt *= 100;
}
$generalsCnt[$nationID]['cnt'] += $calcCnt;
}
$randVals = [];
foreach($generalsCnt as $testNation){
$randVals[] = [$testNation, 1/$testNation['cnt']];
foreach ($generalsCnt as $testNation) {
$randVals[] = [$testNation, 1 / $testNation['cnt']];
}
if($randVals){
if ($randVals) {
$destNation = Util::choiceRandomUsingWeightPair($randVals);
}
}
$logger = $general->getLogger();
if(!$destNation){
if (!$destNation) {
//임관 가능한 국가가 없다!
$logger->pushGeneralActionLog("임관 가능한 국가가 없습니다. <1>$date</>");
$logger->pushGeneralActionLog("임관 가능한 국가가 없습니다. <1>$date</>");
$this->alternative = new che_요양($general, $this->env, null);
return false;
}
@@ -236,7 +244,7 @@ class che_랜덤임관 extends Command\GeneralCommand{
$destNationID = $destNation['nation'];
$destNationName = $destNation['name'];
$talkList = [
'어쩌다 보니',
@@ -257,10 +265,9 @@ class che_랜덤임관 extends Command\GeneralCommand{
$logger->pushGeneralHistoryLog("<D><b>{$destNationName}</b></>에 랜덤 임관");
$logger->pushGlobalActionLog("<Y>{$generalName}</>{$josaYi} {$randomTalk} <D><b>{$destNationName}</b></>에 <S>임관</>했습니다.");
if($gennum < GameConst::$initialNationGenLimit) {
if ($gennum < GameConst::$initialNationGenLimit) {
$exp = 700;
}
else {
} else {
$exp = 100;
}
@@ -268,22 +275,21 @@ class che_랜덤임관 extends Command\GeneralCommand{
$general->setVar('officer_level', 1);
$general->setVar('officer_city', 0);
$general->setVar('belong', 1);
if($this->destGeneralObj !== null){
if ($this->destGeneralObj !== null) {
$general->setVar('city', $this->destGeneralObj->getCityID());
}
else{
} else {
$targetCityID = $db->queryFirstField('SELECT city FROM general WHERE nation = %i AND officer_level=12', $destNationID);
$general->setVar('city', $targetCityID);
}
$db->update('nation', [
'gennum'=>$db->sqleval('gennum + 1')
'gennum' => $db->sqleval('gennum + 1')
], 'nation=%i', $destNationID);
$relYear = $env['year'] - $env['startyear'];
if($general->getNPCType() == 1 || $relYear >= 3){
$joinedNations = $general->getAuxVar('joinedNations')??[];
if ($general->getNPCType() == 1 || $relYear >= 3) {
$joinedNations = $general->getAuxVar('joinedNations') ?? [];
$joinedNations[] = $destNationID;
$general->setAuxVar('joinedNations', $joinedNations);
}
@@ -296,5 +302,4 @@ class che_랜덤임관 extends Command\GeneralCommand{
return true;
}
}
}
@@ -15,7 +15,8 @@ use function \sammo\{
getDomesticExpLevelBonus,
CriticalRatioDomestic,
CriticalScoreEx,
tryUniqueItemLottery
tryUniqueItemLottery,
updateMaxDomesticCritical
};
use \sammo\Constraint\Constraint;
@@ -165,6 +166,13 @@ class che_상업투자 extends Command\GeneralCommand{
$exp = $score * 0.7;
$ded = $score * 1.0;
if($pick == 'success'){
updateMaxDomesticCritical($general, $score);
}
else{
$general->setAuxVar('max_domestic_critical', 0);
}
$scoreText = number_format($score, 0);
$josaUl = JosaUtil::pick(static::$actionName, '을');
@@ -203,6 +211,4 @@ class che_상업투자 extends Command\GeneralCommand{
return true;
}
}
+6 -1
View File
@@ -202,6 +202,8 @@ class che_임관 extends Command\GeneralCommand{
$nationList[$nationID]['scoutmsg'] = $scoutMsg;
}
$hiddenItems = [];
foreach($nationList as &$nation){
if($env['year'] < $env['startyear']+3 && $nation['gennum'] >= GameConst::$initialNationGenLimit){
$nation['availableJoin'] = false;
@@ -217,7 +219,9 @@ class che_임관 extends Command\GeneralCommand{
$nation['availableJoin'] = false;
}
if(Util::starts_with($nation['name'], 'ⓤ')){
$hiddenItems[$nation['nation']] = $nation['nation'];
}
}
unset($nation);
ob_start();
@@ -228,6 +232,7 @@ class che_임관 extends Command\GeneralCommand{
임관할 국가를 목록에서 선택하세요.<br>
<select class='formInput' name="destNationID" id="destNationID" size='1' style='color:white;background-color:black;'>
<?php foreach($nationList as $nation): ?>
<?php if(key_exists($nation['nation'], $hiddenItems)){ continue; } ?>
<option
value='<?=$nation['nation']?>'
style='<?=$nation['availableJoin']?'':'background-color:red;'?>'
@@ -209,7 +209,10 @@ class che_장수대상임관 extends Command\GeneralCommand{
$nationList[$nationID]['scoutmsg'] = $scoutMsg;
}
$hiddenItems = [];
foreach($nationList as &$nation){
$nation['hideen'] = false;
if($env['year'] < $env['startyear']+3 && $nation['gennum'] >= GameConst::$initialNationGenLimit){
$nation['availableJoin'] = false;
}
@@ -224,7 +227,9 @@ class che_장수대상임관 extends Command\GeneralCommand{
$nation['availableJoin'] = false;
}
if(Util::starts_with($nation['name'], 'ⓤ')){
$hiddenItems[$nation['nation']] = $nation['nation'];
}
}
unset($nation);
ob_start();
@@ -235,6 +240,7 @@ class che_장수대상임관 extends Command\GeneralCommand{
임관할 국가를 목록에서 선택하세요.<br>
<select class='formInput' name="destGeneralID" id="destGeneralID" size='1' style='color:white;background-color:black;'>
<?php foreach($generalList as $targetGeneral): ?>
<?php if(key_exists($targetGeneral['nation'], $hiddenItems)){ continue; } ?>
<option value='<?=$targetGeneral['no']?>'><?=getColoredName($targetGeneral['name'],$targetGeneral['npc'])?>【<?=getNationStaticInfo($targetGeneral['nation'])['name']??'재야'?>】</option>
<?php endforeach; ?>
</select>
+10 -2
View File
@@ -15,7 +15,8 @@ use function \sammo\{
getDomesticExpLevelBonus,
CriticalRatioDomestic,
CriticalScoreEx,
tryUniqueItemLottery
tryUniqueItemLottery,
updateMaxDomesticCritical
};
use \sammo\Constraint\Constraint;
@@ -150,6 +151,13 @@ class che_정착장려 extends Command\GeneralCommand{
$exp = $score * 0.7;
$ded = $score * 1.0;
if($pick == 'success'){
updateMaxDomesticCritical($general, $score);
}
else{
$general->setAuxVar('max_domestic_critical', 0);
}
$score *= 10;
$scoreText = number_format($score, 0);
@@ -187,5 +195,5 @@ class che_정착장려 extends Command\GeneralCommand{
return true;
}
}
@@ -15,7 +15,8 @@ use function \sammo\{
getDomesticExpLevelBonus,
CriticalRatioDomestic,
CriticalScoreEx,
tryUniqueItemLottery
tryUniqueItemLottery,
updateMaxDomesticCritical
};
use \sammo\Constraint\Constraint;
@@ -150,6 +151,13 @@ class che_주민선정 extends Command\GeneralCommand{
$exp = $score * 0.7;
$ded = $score * 1.0;
if($pick == 'success'){
updateMaxDomesticCritical($general, $score);
}
else{
$general->setAuxVar('max_domestic_critical', 0);
}
$score /= 10;
$scoreText = number_format($score, 1);
+12 -8
View File
@@ -9,16 +9,17 @@ use \sammo\{
LastTurn,
GameUnitConst,
Command,
Json,
KVStorage,
StringUtil
};
use function \sammo\{
getDomesticExpLevelBonus,
CriticalRatioDomestic,
CriticalScoreEx,
getAllNationStaticInfo,
getNationStaticInfo
};
use function \sammo\getDomesticExpLevelBonus;
use function \sammo\CriticalRatioDomestic;
use function \sammo\CriticalScoreEx;
use function \sammo\getAllNationStaticInfo;
use function \sammo\getNationStaticInfo;
use \sammo\Constraint\Constraint;
use \sammo\Constraint\ConstraintHelper;
@@ -223,7 +224,10 @@ class che_물자원조 extends Command\NationCommand{
$destNationLogger = new ActionLogger(0, $destChiefID, $year, $month);
$destNationLogger->pushNationalHistoryLog("<D><b>{$nationName}</b></>{$josaRoSrc}부터 금<C>{$goldAmountText}</> 쌀<C>{$riceAmountText}</>을 지원 받음");
$destNationStor = KVStorage::getStorage(DB::db(), $destNationID, 'nation_env');
$destRecvAssist = $destNationStor->getValue('recv_assist')??[];
$destRecvAssist["n{$nationID}"] = [$nationID, ($destRecvAssist["n{$nationID}"][1]??0) + $goldAmount + $riceAmount];
$destNationStor->setValue('recv_assist', $destRecvAssist);
$db->update('nation', [
'gold'=>$db->sqleval('gold - %i', $goldAmount),
@@ -2,33 +2,31 @@
namespace sammo\Command\Nation;
use\sammo\{
DB,
Util,
JosaUtil,
General,
DummyGeneral,
ActionLogger,
GameConst,
LastTurn,
GameUnitConst,
Command,
MessageTarget,
DiplomaticMessage,
Message,
};
use \sammo\DB;
use \sammo\Util;
use \sammo\JosaUtil;
use \sammo\General;
use \sammo\DummyGeneral;
use \sammo\ActionLogger;
use \sammo\GameConst;
use \sammo\LastTurn;
use \sammo\GameUnitConst;
use \sammo\Command;
use \sammo\MessageTarget;
use \sammo\DiplomaticMessage;
use \sammo\Message;
use function\sammo\{
getDomesticExpLevelBonus,
CriticalRatioDomestic,
CriticalScoreEx,
getAllNationStaticInfo,
getNationStaticInfo,
GetImageURL
};
use function \sammo\getDomesticExpLevelBonus;
use function \sammo\CriticalRatioDomestic;
use function \sammo\CriticalScoreEx;
use function \sammo\getAllNationStaticInfo;
use function \sammo\getNationStaticInfo;
use function \sammo\GetImageUR;
use \sammo\Constraint\Constraint;
use \sammo\Constraint\ConstraintHelper;
use sammo\Json;
use sammo\KVStorage;
class che_불가침수락 extends Command\NationCommand
{
@@ -191,6 +189,13 @@ class che_불가침수락 extends Command\NationCommand
$destNationID = $destNation['nation'];
$destNationName = $destNation['name'];
$destNationStor = KVStorage::getStorage(DB::db(), $destNationID, 'nation_env');
$destRecvAssist = $destNationStor->getValue('recv_assist')??[];
$destRespAssist = $destNationStor->getValue('resp_assist')??[];
$destRespAssist["n{$nationID}"] = [$nationID, $destRecvAssist["n{$nationID}"][1]??0];
$destNationStor->setValue('resp_assist', $destRespAssist);
$year = $this->arg['year'];
$month = $this->arg['month'];
@@ -50,6 +50,11 @@ class AllowJoinDestNation extends Constraint{
return false;
}
if($this->general['npc']??2 < 2 && \sammo\Util::starts_with($this->destNation['name'], 'ⓤ')){
$this->reason = "유저장은 태수국에 임관할 수 없습니다.";
return false;
}
$joinedNations = $this->general['auxVar']['joinedNations']??[];
if(in_array($this->destNation['nation'], $joinedNations)){
$this->reason = "이미 임관했었던 국가입니다.";
+282
View File
@@ -0,0 +1,282 @@
<?php
namespace sammo\Event\Action;
use InvalidArgumentException;
use sammo\ActionLogger;
use sammo\CityConst;
use sammo\DB;
use sammo\GameConst;
use sammo\Json;
use sammo\KVStorage;
use sammo\Scenario\GeneralBuilder;
use sammo\Scenario\Nation;
use sammo\UniqueConst;
use sammo\Util;
use function sammo\GetNationColors;
use function sammo\getNationStaticInfo;
use function sammo\pickGeneralFromPool;
use function sammo\refreshNationStaticInfo;
class RaiseNPCNation extends \sammo\Event\Action
{
public function __construct()
{
}
const CITY_KEYS = ['pop', 'agri', 'comm', 'secu', 'def', 'wall'];
const MIN_DIST_USERNATION = 4;
const MIN_DIST_NPCNATION = 2;
private function calcAvgNationCity(array $cities)
{
if (count($cities) == 0) {
throw new InvalidArgumentException();
}
$targetCities = [];
foreach ($cities as $city) {
if ($city['nation'] == 0) {
continue;
}
$citySum = 0;
foreach (static::CITY_KEYS as $key) {
if ($key === 'pop') {
continue;
}
$citySum += $city[$key];
}
$city['sum'] = $citySum;
$targetCities[] = $city;
}
$cityCnt = count($targetCities);
if ($cityCnt == 0) {
$target = Util::choiceRandom($cities);
$randCity = [];
foreach (static::CITY_KEYS as $key) {
$randCity[$key] = $target["{$key}_max"];
}
return $randCity;
}
usort($targetCities, function (array $lhs, array $rhs) {
return $lhs['sum'] <=> $rhs['sum'];
});
//최소, 최대 도시 몇개를 뺀다. 정렬은 신경쓰지 않는다.
if ($cityCnt >= 3) {
$reduceCnt = Util::valueFit(Util::round($cityCnt / 6, 0), 1);
foreach (Util::range($reduceCnt) as $_idx) {
array_pop($targetCities);
}
foreach (Util::range($reduceCnt) as $idx) {
$targetCities[$idx] = array_pop($targetCities);
}
$cityCnt -= $reduceCnt * 2;
}
$avgCity = [];
foreach ($targetCities as $city) {
foreach (static::CITY_KEYS as $key) {
$avgCity[$key] = ($avgCity[$key] ?? 0) + $city[$key];
}
}
foreach (static::CITY_KEYS as $key) {
$avgCity[$key] = Util::toInt($avgCity[$key] / $cityCnt);
}
return $avgCity;
}
public function calcAvgNationGeneralCnt(): int
{
$nationUsers = [];
foreach (\sammo\getAllNationStaticInfo() as $nation) {
if ($nation['level'] == 0) {
continue;
}
$nationUsers[] = $nation['gennum'];
}
$nationCnt = count($nationUsers);
if ($nationCnt == 0) {
return GameConst::$initialNationGenLimit;
}
sort($nationUsers);
if ($nationCnt >= 3) {
$reduceCnt = Util::valueFit(Util::round($nationCnt / 6, 0), 1);
foreach (Util::range($reduceCnt) as $_idx) {
array_pop($nationUsers);
}
foreach (Util::range($reduceCnt) as $idx) {
$nationUsers[$idx] = array_pop($nationUsers);
}
$nationCnt -= $reduceCnt * 2;
}
return Util::round(array_sum($nationUsers) / $nationCnt);
}
public function calcAvgTech(): int
{
$techSum = 0;
$nationCnt = 0;
foreach (\sammo\getAllNationStaticInfo() as $nation) {
if ($nation['level'] == 0) {
continue;
}
$techSum += $nation['tech'];
$nationCnt += 1;
}
if ($nationCnt == 0) {
return 0;
}
return Util::toInt($techSum / $nationCnt);
}
private function buildNation(int $nationID, int $tech, array $baseCity, array $avgCity, int $genCnt, $env)
{
$db = DB::db();
$targetCity = [
'trust' => 100
];
foreach (static::CITY_KEYS as $key) {
$targetCity[$key] = min($baseCity["{$key}_max"], $avgCity[$key]);
}
$pickTypeList = ['무' => 1, '지' => 1];
$cityID = $baseCity['city'];
$cityName = $baseCity['name'];
$nationName = "{$cityName}";
$color = Util::choiceRandom(GetNationColors());
$nationObj = new Nation($nationID, $nationName, $color, 0, 2000, "우리도 할 수 있다! {$cityName}", $tech, null, 2, [$cityName]);
$nationObj->build($env);
$ruler = (new GeneralBuilder("{$cityName}태수", false, null, $nationID))
->setOfficerLevel(12)
->setCityID($cityID)
->setNPCType(6)
->setGoldRice(1000, 1000)
->fillRandomStat($pickTypeList)
->setKillturn(240)
->fillRemainSpecAsZero($env);
$ruler->build($env);
$nationObj->addGeneral($ruler);
$birthYear = $env['year'] - 20;
$deadYearMin = $env['year'] + 10;
foreach (pickGeneralFromPool(DB::db(), 0, $genCnt - 1) as $pickedNPC) {
//대충 10년후부터 6년마다 절반?
$deadYear = $deadYearMin + Util::toInt(60 * (1 - log(Util::randRange(1, 1024), 2)/10));
$newNPC = $pickedNPC->getGeneralBuilder();
$newNPC->setNationID($nationID)
->setCityID($cityID)
->setNPCType(6)
->setGoldRice(1000, 1000)
->setLifeSpan($birthYear, $deadYear)
->fillRandomStat($pickTypeList)
->fillRemainSpecAsZero($env);
$newNPC->build($env);
$pickedNPC->occupyGeneralName();
$nationObj->addGeneral($newNPC);
}
$nationObj->postBuild($env);
$db->update('city', $targetCity, 'city = %i', $cityID);
refreshNationStaticInfo();
}
public function run(array $env)
{
$db = DB::db();
$allCities = $db->query('SELECT * FROM city WHERE 5 <= level AND level <= 6'); //소, 중 성만 선택
$emptyCities = [];
$occupiedCities = [];
$npcCities = [];
$avgCity = $this->calcAvgNationCity($allCities);
foreach ($allCities as $city) {
if ($city['nation'] == 0) {
$emptyCities[$city['city']] = $city;
} else {
$occupiedCities[$city['city']] = $city;
}
}
$avgGenCnt = $this->calcAvgNationGeneralCnt();
$serverID = UniqueConst::$serverID;
$lastNationID = max(
$db->queryFirstField("SELECT max(`nation`) FROM `nation`"),
$db->queryFirstField("SELECT max(`nation`) FROM `ng_old_nations` WHERE server_id = %s", $serverID),
);
$avgTech = $this->calcAvgTech();
Util::shuffle_assoc($emptyCities);
$occupiedCitiesID = array_keys($occupiedCities);
$npcCitiesID = [];
refreshNationStaticInfo();
foreach ($emptyCities as $emptyCity) {
$cityID = $emptyCity['city'];
$minDistance = 999;
foreach ($occupiedCitiesID as $targetCityID) {
$minDistance = min(\sammo\calcCityDistance($cityID, $targetCityID, null), $minDistance);
if ($minDistance < static::MIN_DIST_USERNATION) {
break;
}
}
if ($minDistance < static::MIN_DIST_USERNATION) {
continue;
}
$minDistance = 999;
foreach ($npcCitiesID as $targetCityID) {
$minDistance = min(\sammo\calcCityDistance($cityID, $targetCityID, null), $minDistance);
if ($minDistance < static::MIN_DIST_NPCNATION) {
break;
}
}
if ($minDistance < static::MIN_DIST_NPCNATION) {
continue;
}
//TODO: 거리 측정
$lastNationID += 1;
$this->buildNation($lastNationID, $avgTech, $emptyCity, $avgCity, $avgGenCnt, $env);
$npcCities[$cityID] = $emptyCity;
$npcCitiesID[] = $cityID;
}
if (count($npcCities) > 0) {
$logger = new ActionLogger(0, 0, $env['year'], $env['month']);
$logger->pushGlobalHistoryLog("<L><b>【공지】</b></>공백지에 임의의 국가가 생성되었습니다.");
$logger->flush();
}
return [__CLASS__, count($npcCities)];
}
}
+5 -1
View File
@@ -1,6 +1,8 @@
<?php
namespace sammo\Event\Condition;
use sammo\Event\Condition;
class Logic extends \sammo\Event\Condition{
private $mode = 'and';
@@ -24,7 +26,9 @@ class Logic extends \sammo\Event\Condition{
}
$this->mode = $mode;
$this->conditions = $conditions;
$this->conditions = array_map(function($condition){
return Condition::build($condition);
}, $conditions);
}
public function eval($env=null){
+207 -3
View File
@@ -50,6 +50,20 @@ class General implements iAction{
'occupied'=>1,
];
const INHERITANCE_KEY = [
'previous'=>[true, 1, '기존 포인트'],
'lived_month'=>[true, 1, '생존'],
'max_belong'=>[false, 10, '최대 임관년 수'],
'max_domestic_critical'=>[true, 1, '최대 연속 내정 성공'],
'snipe_combat'=>[true, 10, '병종 상성 우위 횟수'],
'combat'=>[['rank', 'warnum'], 5, '전투 횟수'],
'sabotage'=>[['rank', 'firenum'], 20, '계략 성공 횟수'],
'unifier'=>[true, 1, '천통 기여'],
'dex'=>[false, 0.001, '숙련도'],
'tournament'=>[true, 1, '토너먼트'],
'betting'=>[false, 10, '베팅 당첨'],
];
const TURNTIME_FULL_MS = -1;
const TURNTIME_FULL = 0;
const TURNTIME_HMS = 1;
@@ -539,6 +553,8 @@ class General implements iAction{
$generalName = $this->getName();
$this->mergeTotalInheritancePoint();
// 군주였으면 유지 이음
$officerLevel = $this->getVar('officer_level');
if($officerLevel == 12) {
@@ -592,6 +608,12 @@ class General implements iAction{
$generalName = $this->getName();
$ownerID = $this->getVar('owner');
if($ownerID){
$this->mergeTotalInheritancePoint();
resetInheritanceUser($ownerID);
}
$this->multiplyVarWithLimit('leadership', 0.85, 10);
$this->multiplyVarWithLimit('strength', 0.85, 10);
$this->multiplyVarWithLimit('intel', 0.85, 10);
@@ -919,19 +941,19 @@ class General implements iAction{
static public function mergeQueryColumn(?array $reqColumns=null, int $constructMode=2):array{
$minimumColumn = ['no', 'name', 'city', 'nation', 'officer_level', 'officer_city'];
$defaultEventColumn = [
'no', 'name', 'city', 'nation', 'officer_level', 'officer_city',
'no', 'name', 'owner', 'city', 'nation', 'officer_level', 'officer_city',
'special', 'special2', 'personal',
'horse', 'weapon', 'book', 'item', 'last_turn'
];
$fullColumn = [
'no', 'name', 'owner_name', 'picture', 'imgsvr', 'nation', 'city', 'troop', 'injury', 'affinity',
'no', 'name', 'owner', 'owner_name', 'picture', 'imgsvr', 'nation', 'city', 'troop', 'injury', 'affinity',
'leadership', 'leadership_exp', 'strength', 'strength_exp', 'intel', 'intel_exp', 'weapon', 'book', 'horse', 'item',
'experience', 'dedication', 'officer_level', 'officer_city', 'gold', 'rice', 'crew', 'crewtype', 'train', 'atmos', 'turntime',
'makelimit', 'killturn', 'block', 'dedlevel', 'explevel', 'age', 'startage', 'belong',
'personal', 'special', 'special2', 'defence_train', 'tnmt', 'npc', 'npc_org', 'deadyear', 'npcmsg',
'dex1', 'dex2', 'dex3', 'dex4', 'dex5', 'betray',
'recent_war', 'last_turn', 'myset',
'specage', 'specage2', 'con', 'connect', 'owner', 'aux', 'lastrefresh',
'specage', 'specage2', 'con', 'connect', 'aux', 'lastrefresh',
];
if($reqColumns === null){
@@ -1141,4 +1163,186 @@ class General implements iAction{
}
return $result;
}
/**
* @return int|float
*/
public function getInheritancePoint(string $key, &$aux=null, bool $forceCalc=false){
$inheritType = static::INHERITANCE_KEY[$key]??null;
if($inheritType === null){
throw new \OutOfRangeException("{$key}는 유산 타입이 아님");
}
$ownerID = $this->getVar('owner');
if(!$ownerID){
return 0;
}
if($this->getVar('npc') != 0){
return 0;
}
[$storeType, $multiplier, ] = $inheritType;
$gameStor = KVStorage::getStorage(DB::db(), 'game_env');
if($storeType === true || ($gameStor->isunited != 0 && !$forceCalc)){
$inheritStor = KVStorage::getStorage(DB::db(), "inheritance_{$ownerID}");
[$value, $aux] = $inheritStor->getValue($key);
return $value;
}
if(is_array($storeType)){
[$storSubType, $storSubKey] = $storeType;
if($storSubType === 'rank'){
return $this->getRankVar($storSubKey) * $multiplier;
}
if($storSubType === 'raw'){
return $this->getVar($storSubKey) * $multiplier;
}
if($storSubType === 'aux'){
return ($this->getAuxVar($storSubKey)??0) * $multiplier;
}
throw new \InvalidArgumentException("{$storSubType}은 참조 할 수 없는 유산 세부키임");
}
if($storeType !== false){
throw new \InvalidArgumentException("{$storeType}은 올바르지 않은 유산 키임");
}
$extractFn = function(){ return [0, null];};
switch($key){
case 'dex':
$extractFn = function() use ($multiplier){
$totalDex = 0;
foreach(array_keys(GameUnitConst::allType()) as $armType){
$totalDex += $this->getVar("dex{$armType}");
}
return [$totalDex * $multiplier, null];
};
break;
case 'betting':
$extractFn = function() use ($multiplier){
$betWin = $this->getRankVar('betwin');
$betWinRate = $this->getRankVar('betwingold')/max(1, $this->getRankVar('betgold'));
return [$betWin * $multiplier * pow($betWinRate, 2), null];
};
break;
case 'max_belong':
$extractFn = function() use ($multiplier){
$maxBelong = max($this->getVar('belong'), $this->getAuxVar('max_belong')??0);
return [$maxBelong * $multiplier, null];
};
break;
default:
throw new \InvalidArgumentException("{$key}는 유산 추출기를 보유하고 있지 않음");
}
[$value, $aux] = ($extractFn)();
return $value;
}
public function setInheritancePoint(string $key, $value, $aux=null){
if(!is_int($value) && !is_float($value)){
throw new \InvalidArgumentException("{$value}는 숫자가 아님");
}
$inheritType = static::INHERITANCE_KEY[$key]??null;
if($inheritType === null){
throw new \OutOfRangeException("{$key}는 유산 타입이 아님");
}
[$storeType, $multiplier, ] = $inheritType;
if($storeType !== true){
throw new \InvalidArgumentException("{$key}는 직접 저장형 유산 포인트가 아님");
}
if($multiplier != 1 && $value != 0){
throw new \InvalidArgumentException("{$key}는 1:1 유산 포인트가 아님");
}
$ownerID = $this->getVar('owner');
if(!$ownerID){
return;
}
if($this->getVar('npc') != 0){
return;
}
$gameStor = KVStorage::getStorage(DB::db(), 'game_env');
if($gameStor->isunited != 0){
return;
}
$inheritStor = KVStorage::getStorage(DB::db(), "inheritance_{$ownerID}");
$inheritStor->setValue($key, [$value, $aux]);
}
public function increaseInheritancePoint(string $key, $value, $aux=null){
if(!is_int($value) && !is_float($value)){
throw new \InvalidArgumentException("{$value}는 숫자가 아님");
}
$inheritType = static::INHERITANCE_KEY[$key]??null;
if($inheritType === null){
throw new \OutOfRangeException("{$key}는 유산 타입이 아님");
}
[$storeType, $multiplier, ] = $inheritType;
if($storeType !== true){
throw new \InvalidArgumentException("{$key}는 직접 저장형 유산 포인트가 아님");
}
$ownerID = $this->getVar('owner');
if(!$ownerID){
return;
}
if($this->getVar('npc') != 0){
return;
}
$gameStor = KVStorage::getStorage(DB::db(), 'game_env');
if($gameStor->isunited != 0){
return;
}
$inheritStor = KVStorage::getStorage(DB::db(), "inheritance_{$ownerID}");
[$oldValue, $oldAux] = $inheritStor->getValue($key)??[0, null];
if($oldAux !== $aux){
$oldValue = 0;
}
$newValue = $oldValue + $value * $multiplier;
$inheritStor->setValue($key, [$newValue, $aux]);
}
public function mergeTotalInheritancePoint(){
$ownerID = $this->getVar('owner');
if(!$ownerID){
return;
}
if($this->getVar('npc') != 0){
return;
}
$inheritStor = KVStorage::getStorage(DB::db(), "inheritance_{$ownerID}");
$inheritStor->cacheAll();
foreach(static::INHERITANCE_KEY as $key=>[$storType, ,]){
$aux = null;
$point = $this->getInheritancePoint($key, $aux, true);
if($storType === true){
continue;
}
$inheritStor->setValue($key, [$point, $aux]);
}
$oldInheritStor = KVStorage::getStorage(DB::db(), "inheritance_result");
$gameStor = KVStorage::getStorage(DB::db(), 'game_env');
$serverID = UniqueConst::$serverID;
$year = $gameStor->year;
$month = $gameStor->month;
$oldInheritStor->setValue("{$serverID}_{$ownerID}_{$this->getID()}_{$year}_{$month}", $inheritStor->getAll(true));
}
}
+86 -2
View File
@@ -1712,6 +1712,88 @@ class GeneralAI
return $cmd;
}
// 군주 행동
protected function do불가침제의(LastTurn $lastTurn): ?NationCommand
{
$general = $this->general;
if($general->getVar('officer_level') < 12){
return null;
}
$nation = $this->nation;
$nationID = $nation['nation'];
$nationStor = KVStorage::getStorage(DB::db(), $nationID, 'nation_env');
$recvAssist = $nationStor->getValue('recv_assist')??[];
$respAssist = $nationStor->getValue('resp_assist')??[];
$respAssistTry = $nationStor->getValue('resp_assist_try')??[];
$yearMonth = Util::joinYearMonth($this->env['year'], $this->env['month']);
$candidateList = [];
foreach($recvAssist as [$candNationID, $amount]){
$amount -= $respAssist["n{$candNationID}"][1]??0;
if($amount <= 0){
continue;
}
if(key_exists($candNationID, $this->warTargetNation)){
continue;
}
if(($respAssistTry["n{$candNationID}"][1]??0) >= $yearMonth - 8){
continue;
}
$candidateList[$candNationID] = $amount;
}
if(!$candidateList){
return null;
}
$cityList = $this->supplyCities;
if (!$cityList) {
return null;
}
$goldIncome = getGoldIncome($nation['nation'], $nation['level'], 15, $nation['capital'], $nation['type'], $cityList);
$riceIncome = getRiceIncome($nation['nation'], $nation['level'], 15, $nation['capital'], $nation['type'], $cityList);
$wallIncome = getWallIncome($nation['nation'], $nation['level'], 15, $nation['capital'], $nation['type'], $cityList);
$income = $goldIncome + $riceIncome + $wallIncome;
arsort($candidateList);
$destNationID = null;
$diplomatMonth = 0;
foreach($candidateList as $candNationID => $amount){
if($amount * 4 < $income){
break;
}
$destNationID = $candNationID;
$diplomatMonth = 24 * $amount / $income;
break;
}
if($destNationID === null){
return null;
}
[$targetYear, $targetMonth] = Util::parseYearMonth($yearMonth + $diplomatMonth);
$cmd = buildNationCommandClass('che_불가침제의', $this->general, $this->env, $lastTurn, [
'destNationID' => $destNationID,
'year' => $targetYear,
'month' => $targetMonth,
]);
if(!$cmd->hasFullConditionMet()){
return null;
}
$respAssistTry["n{$destNationID}"] = [$destNationID, $yearMonth];
$nationStor->setValue('resp_assist_try', $respAssistTry);
return $cmd;
}
// 군주 행동
protected function do선전포고(LastTurn $lastTurn): ?NationCommand
@@ -3067,6 +3149,9 @@ class GeneralAI
if($general->getVar('makelimit')){
return null;
}
if($general->getNPCType() > 2){
return null;
}
if(!$this->generalPolicy->can건국){
return null;
}
@@ -3167,8 +3252,7 @@ class GeneralAI
return null;
}
if (Util::randBool(pow(1 / $nationCnt / pow($notFullNationCnt, 3), 1 / 4))) {
//국가가 1개일 경우에는 '임관하지 않음'
if (Util::randBool(pow(1 / ($nationCnt + 1) / pow($notFullNationCnt, 3), 1 / 4))) {
return null;
}
}
+4 -1
View File
@@ -76,7 +76,10 @@ class Nation{
$capital = 0;
}
if(strpos($this->type, '_') === FALSE){
if($this->type === null){
$type = Util::choiceRandom(GameConst::$availableNationType);
}
else if(strpos($this->type, '_') === FALSE){
$type = 'che_'.$this->type;
}
else{
+2
View File
@@ -257,6 +257,8 @@ class TurnExecutionHelper
$autorunMode = false;
$ai = null;
$general->increaseInheritancePoint('lived_month', 1);
$turnObj->preprocessCommand($env);
$generalCommandObj = $general->getReservedTurn(0, $env);
+13 -1
View File
@@ -5,5 +5,17 @@
],
"const": {
"joinRuinedNPCProp":0
}
},
"events":[
[
["or", ["Date", "==", null, 12], ["Date", "==", null, 6]],
["CreateManyNPC", 30],
["DeleteEvent"]
],
[
["Date", "==", 181, 1],
["RaiseNPCNation"],
["DeleteEvent"]
]
]
}
+13 -1
View File
@@ -8,5 +8,17 @@
],
"const": {
"joinRuinedNPCProp":0
}
},
"events":[
[
["or", ["Date", "==", null, 12], ["Date", "==", null, 6]],
["CreateManyNPC", 30],
["DeleteEvent"]
],
[
["Date", "==", 181, 1],
["RaiseNPCNation"],
["DeleteEvent"]
]
]
}
+6 -1
View File
@@ -15,9 +15,14 @@
},
"events":[
[
["Date", "==", null, 12],
["or", ["Date", "==", null, 12], ["Date", "==", null, 6]],
["CreateManyNPC", 30],
["DeleteEvent"]
],
[
["Date", "==", 181, 1],
["RaiseNPCNation"],
["DeleteEvent"]
]
]
}
+1 -1
View File
@@ -16,7 +16,7 @@
<a href='b_diplomacy.php'><button type="button" class='commandButton' style='background-color:<?=$bgColor?>;color:<?=$fgColor?>;'>중원 정보</button></a>
<a href='b_currentCity.php'><button type="button" class='commandButton' style='background-color:<?=$bgColor?>;color:<?=$fgColor?>;'>현재 도시</button></a>
<a href='b_battleCenter.php' target='_blank'><button type="button" class='commandButton' <?=$showSecret?'':'disabled'?> style='background-color:<?=$bgColor?>;color:<?=$fgColor?>;'>감 찰 부</button></a>
<button type="button" style="visibility:hidden;" class='commandButton'>빈칸</button>
<a href='b_inheritPoint.php'><button type="button" class='commandButton' style='background-color:<?=$bgColor?>;color:<?=$fgColor?>;'>유산 관리</button></a>
<a href='b_myPage.php'><button type="button" class='commandButton' style='background-color:<?=$bgColor?>;color:<?=$fgColor?>;'>내 정보&amp;설정</button></a>
<a href='b_auction.php' target='_blank'><button type="button" class='commandButton' style='background-color:<?=$bgColor?>;color:<?=$fgColor?>;'>거 래 장</button></a>
<a href='b_betting.php' target='_blank'><button type="button" class='commandButton' style='background-color:<?=$bgColor?>;color:<?=$fgColor?>;'>베 팅 장</button></a>
+38
View File
@@ -0,0 +1,38 @@
import { sum } from "lodash";
import { unwrap } from "./util";
declare global {
interface Window {
formStart: ()=>void;
items: {[name: string]:number};
helpText: {[name: string]:string};
}
}
export {}
function formStart() {
const dSum = unwrap(document.querySelector('#inherit_sum_value')) as HTMLInputElement ;
const dOld = unwrap(document.querySelector('#inherit_previous_value')) as HTMLInputElement;
const dNew = unwrap(document.querySelector('#inherit_new_value')) as HTMLInputElement;
const sumPoint = Math.floor(sum(Object.values(window.items)));
const oldPoint = Math.floor(window.items['previous']);
const sumNewPoint = sumPoint - oldPoint;
dSum.value = sumPoint.toLocaleString();
dOld.value = oldPoint.toLocaleString();
dNew.value = sumNewPoint.toLocaleString();
for(const [key, val] of Object.entries(window.items)){
const dItem = unwrap(document.querySelector(`#inherit_${key}_value`)) as HTMLInputElement ;
dItem.value = Math.floor(val).toLocaleString();
}
for(const [key, text] of Object.entries(window.helpText)){
const dText = unwrap(document.querySelector(`#inherit_${key} small.form-text`)) as HTMLElement;
dText.innerHTML = text;
}
}
window.formStart = formStart;
+35
View File
@@ -0,0 +1,35 @@
type ErrType<T> = { new(msg?: string): T }
type Nullable<T> = T | null | undefined
export class RuntimeError extends Error {
public name = 'RuntimeError';
constructor(public message: string = '') {
super(message);
}
toString(): string {
if (this.message) {
return this.name + ': ' + this.message;
}
else {
return this.name;
}
}
}
export class NotNullExpected extends RuntimeError {
public name = 'NotNullExpected';
}
export function unwrap<T>(result: Nullable<T>): T {
if (result === null || result === undefined) {
throw new NotNullExpected();
}
return result;
}
export function unwrap_err<T, ErrT extends Error>(result: Nullable<T>, errType: ErrType<ErrT>, errMsg?: string): T {
if (result === null || result === undefined) {
throw new errType(errMsg);
}
return result;
}
+39
View File
@@ -0,0 +1,39 @@
{
"name": "hidche_lib",
"version": "1.0.0",
"description": "",
"main": "js/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack",
"lint": "eslint hwe/ts ts"
},
"repository": {
"type": "git",
"url": "ssh://git@storage.hided.net:2525/devsam/core.git"
},
"author": "Hide_D",
"license": "MIT",
"dependencies": {
"@types/lodash": "^4.14.172",
"lodash": "^4.17.21"
},
"devDependencies": {
"@babel/cli": "^7.14.8",
"@babel/core": "^7.15.0",
"@babel/preset-env": "^7.15.0",
"@babel/preset-typescript": "^7.15.0",
"@typescript-eslint/eslint-plugin": "^4.29.1",
"@typescript-eslint/parser": "^4.29.1",
"babel-loader": "^8.2.2",
"babel-plugin-lodash": "^3.3.4",
"eslint": "^7.32.0",
"pre-commit": "^1.2.2",
"typescript": "^4.3.5",
"webpack": "^5.49.0",
"webpack-cli": "^4.7.2"
},
"pre-commit": [
"lint"
]
}
+7
View File
@@ -0,0 +1,7 @@
interface Window {
pathConfig: {
root: string,
sharedIcon: string,
gameImage: string,
}
}
+35
View File
@@ -0,0 +1,35 @@
type ErrType<T> = { new(msg?: string): T }
type Nullable<T> = T | null | undefined
export class RuntimeError extends Error {
public name = 'RuntimeError';
constructor(public message: string = '') {
super(message);
}
toString(): string {
if (this.message) {
return this.name + ': ' + this.message;
}
else {
return this.name;
}
}
}
export class NotNullExpected extends RuntimeError {
public name = 'NotNullExpected';
}
export function unwrap<T>(result: Nullable<T>): T {
if (result === null || result === undefined) {
throw new NotNullExpected();
}
return result;
}
export function unwrap_err<T, ErrT extends Error>(result: Nullable<T>, errType: ErrType<ErrT>, errMsg?: string): T {
if (result === null || result === undefined) {
throw new errType(errMsg);
}
return result;
}
+72
View File
@@ -0,0 +1,72 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
"target": "esnext", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
"module": "ESNext", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
// "outDir": "../js", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */
"noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
"isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */
/* Module Resolution Options */
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
"rootDirs": ["./ts", "./hwe/ts"], /* List of root folders whose combined content represents the structure of the project at runtime. */
//"typeRoots": ["./typings"], /* List of folders to include type definitions from. */
//"types": ["ts/common_path.d.ts"], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
/* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
/* Advanced Options */
"skipLibCheck": true, /* Skip type checking of declaration files. */
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
}
}
+64
View File
@@ -0,0 +1,64 @@
const path = require('path');
module.exports = [
{
name: 'ingame',
resolve: {
extensions: [".js", ".ts", ".tsx"]
},
entry: {
inheritPoint: './hwe/ts/inheritPoint.ts',
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'hwe/js'),
},
mode: 'production',
devtool: 'source-map',
module: {
rules: [{
test: /\.(ts|tsx)$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env',
'@babel/preset-typescript'
]
}
}
}]
},
},
{
name: 'gateway',
resolve: {
extensions: [".js", ".ts", ".tsx"]
},
entry: {
//test: './ts/test.ts',
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'js'),
},
mode: 'none',
devtool: 'source-map',
module: {
rules: [{
test: /\.(ts|tsx)$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env',
'@babel/preset-typescript'
]
}
}
}]
},
},
]