From f93148b7d77fdf13ab24c0789d0d790cd3e470d9 Mon Sep 17 00:00:00 2001 From: hide_d Date: Mon, 8 Oct 2018 03:36:43 +0900 Subject: [PATCH] =?UTF-8?q?NPC=20=EA=B5=B0=EC=A3=BC=ED=84=B4=20=EA=B9=8C?= =?UTF-8?q?=EC=A7=80=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/func_npc.php | 3 +- hwe/j_set_general_command.php | 2 +- hwe/sammo/GeneralAI.php | 295 +++++++++++++++++++++++++++++++++- hwe/sql/schema.sql | 3 +- 4 files changed, 293 insertions(+), 10 deletions(-) diff --git a/hwe/func_npc.php b/hwe/func_npc.php index 575d1446..ae9c869b 100644 --- a/hwe/func_npc.php +++ b/hwe/func_npc.php @@ -392,8 +392,6 @@ function processAI(General $generalObj, $command, $arg):array{ return []; } - //TODO:여기부터 다시 구현 - $rulerCommand = 0; //군주가 할일 if($general['level'] == 12) { @@ -462,6 +460,7 @@ function processAI(General $generalObj, $command, $arg):array{ MYDB_query($query, $connect) or Error("processAI09 ".MYDB_error($connect),""); return; } + //NOTE: 여기까지 구현함 } else { //외교 평시에 선포 diff --git a/hwe/j_set_general_command.php b/hwe/j_set_general_command.php index 358c404b..ad4ca561 100644 --- a/hwe/j_set_general_command.php +++ b/hwe/j_set_general_command.php @@ -24,7 +24,7 @@ $defaultCheck = [ ], 'integer'=>[ 'destGeneralID', 'destCityID', 'destNationID', - 'amountMoney', 'amountCrew', 'colorType' + 'amountMoney', 'amountCrew', 'colorType', 'nationType' ], 'boolean'=>[ 'isGold' diff --git a/hwe/sammo/GeneralAI.php b/hwe/sammo/GeneralAI.php index bbbd1f65..ce81711a 100644 --- a/hwe/sammo/GeneralAI.php +++ b/hwe/sammo/GeneralAI.php @@ -31,7 +31,7 @@ class GeneralAI{ public function __construct(General $general){ $db = DB::db(); $gameStor = KVStorage::getStorage($db, 'game_env'); - $this->env = $gameStor->getValues(['startyear','year','month','turnterm','scenario','gold_rate','rice_rate']); + $this->env = $gameStor->getValues(['startyear','year','month','turnterm','killturn','scenario','gold_rate','rice_rate']); $this->general = $general; if($general->getRawCity() === null){ $city = $db->queryFirstRow('SELECT * FROM city WHERE city = %i', $general->getCityID()); @@ -311,7 +311,7 @@ class GeneralAI{ $type = GameUnitConst::DEFAULT_CREWTYPE; } - + //NOTE: 훈련과 사기진작은 '금만 사용한다'는 가정을 하고 있음 $obj훈련 = buildGeneralCommandClass('che_훈련', $general, $env); $obj사기진작 = buildGeneralCommandClass('che_사기진작', $general, $env); @@ -367,9 +367,14 @@ class GeneralAI{ return ['che_견문', null]; } - switch(Util::choiceRandomUsingWeight([11.4, 40, 20, 28.6])) { + switch(Util::choiceRandomUsingWeight([ + '임관'=>11.4, + '거병_견문'=>40, + '이동'=>20, + '기타'=>28.6 + ])) { //임관 - case 0: + case '임관': $available = true; @@ -395,7 +400,7 @@ class GeneralAI{ $arg = ['destNationID'=>99]; } break; - case 1: //거병이나 견문 + case '거병_견문': //거병이나 견문 // 초반이면서 능력이 좋은놈 위주로 1.4%확률로 거병 $prop = Util::randF() * (GameConst::$defaultStatNPCMax + GameConst::$chiefStatMin) / 2; $ratio = ($general->getVar('leader') + $general->getVar('power') + $general->getVar('intel')) / 3; @@ -407,7 +412,7 @@ class GeneralAI{ $command = 'che_견문'; } break; - case 2: //이동 + case '이동': //이동 $paths = array_keys(CityConst::byID($city['city'])::$path); $command = 'che_이동'; @@ -434,6 +439,9 @@ class GeneralAI{ $power = $this->power; $intel = $this->intel; + $year = $env['year']; + $month = $env['month']; + $db = DB::db(); if($general->getVar('npc') == 5){ @@ -499,5 +507,280 @@ class GeneralAI{ } } + if($general->getVar('level') == 12){ + $turn = $this->processLordTurn(); + if($turn !== null){ + return $turn; + } + } + } + + protected function processLordTurn():?array{ + $general = $this->getGeneralObj(); + $city = $this->city; + $nation = $this->nation; + $env = $this->env; + + $year = $env['year']; + $month = $env['month']; + + if($general->getVar('npc') == 9 && $this->dipState == self::d평화 && !$this->attackable){ + if($nation['level'] == 0){ + return ['che_해산', null]; + } + else{ + return ['che_방랑', null]; + } + } + + if(in_array($month, [1,4,7,10])){ + $this->calcPromotion(); + } + else if($month == 12){ + $this->calcTexRate(); + $this->calcGoldBillRate(); + } + else if($month == 6){ + $this->calcTexRate(); + $this->calcRiceBillRate(); + } + + if($nation['level'] == 0){ + if($env['startyear']+1 <= $admin['year']){ + return ['che_해산', null]; + } + else if($city['nation'] == 0 && ($city['level'] == 5 || $city['level'] == 6)) { + $nationType = Util::choiceRandom(array_keys(getNationTypeList())); + $nationColor = Util::choiceRandom(array_keys(GetNationColors())); + return ['che_건국', [ + 'nationName'=>"㉿".mb_substr($general->getName(), 1), + 'nationType'=>$nationType, + 'nationColor'=>$nationColor + ]]; + + } + else{ + //모든 공백지 조사 + $lordCities = $db->queryFirstColumn('SELECT city.city FROM general LEFT JOIN city ON general.city = city.city WHERE general.level = 12 AND city.nation != 0'); + $nationCities = $db->queryFirstColumn('SELECT city FROM city WHERE nation != 0'); + + $occupiedCities = []; + foreach($lordCities as $tCityId){ + $occupiedCities[$tCityId] = 2; + } + foreach($nationCities as $tCityId){ + $occupiedCities[$tCityId] = 1; + } + + $targetCity = []; + //NOTE: 최단 거리가 현재 도시에서 '어떻게 가야' 가장 짧은지 알 수가 없으므로, 한칸 간 다음 계산하기로 + foreach(CityConst::byID($general->getVar('city'))->path as $nearCityID){ + $targetCity[$nearCityID] = 2.0; + + $nearCities = searchDistance($nearCityID, 4, true); + foreach($nearCities as $distance => $distCities){ + foreach($distCities as $distCity){ + if(key_exists($distCity, $occupiedCities)){ + continue; + } + if(CistConst::byID($distCity)->level < 4){ + continue; + } + + $targetCity[$nearCityID] += 1 / (2 ** $distance); + } + } + } + + return ['che_이동', ['destCityID'=>Util::choiceRandomUsingWeight($targetCity)]]; + } + } + + return null; + } + + protected function calcPromotion(){ + $db = DB::db(); + + $nation = $this->nation; + $nationID = $nation['nation']; + $minChiefLevel = getNationChiefLevel($nation['level']); + + $minKillturn = $this->env['killturn'] - Util::toInt(30 / $this->env['turnterm']); + $chiefCandidate = []; + + //이 함수를 부르는건 군주 AI이므로, 군주는 세지 않아도 됨 + $userChief = []; + + foreach($db->query( + 'SELECT no, npc, level, killturn FROM general WHERE nation = %i AND 12 > level AND level > 4', $nationID + ) as $chief){ + + if($chief['npc'] < 2 && $chief['killturn'] < $minKillturn ){ + $chiefCandidate[$cheif['level']] = $chief['no']; + $userChief[$chief['no']] = $chief['level']; + } + } + + $db->update('general', [ + 'level'=>1 + ], 'level < 12 AND level > 4 AND nation = %i', $nationID); + + $maxBelong = $db->queryFirstField('SELECT max(belong) FROM `general` WHERE nation=%i', $nationID); + $maxBelong = min($maxBelong - 1, 3); + + if(!$userChief){ + $candUserChief = $db->queryFirstField( + 'SELECT no FROM general WHERE nation = %i AND level = 1 AND killturn > %i AND npc < 2 AND belong >= %i ORDER BY leader DESC LIMIT 1', + $nationID, + $minKillturn, + $maxBelong + ); + if($candUserChief){ + $userChief[$candUserChief] = 11; + $chiefCandidate[11] = $candUserChief; + } + } + + $promoted = $userChief; + + if(!key_exists(11, $chiefCandidate)){ + $candChiefHead = $db->queryFirstField( + 'SELECT no FROM general WHERE nation = %i AND level = 1 AND npc >= 2 AND belong >= %i ORDER BY leader DESC LIMIT 1', + $nationID, + $maxBelong + ); + if($candChiefHead){ + $chiefCandidate[11] = $candChiefHead; + $promoted[$candChiefHead] = 11; + } + } + + if($minChiefLevel < 11){ + //무장 수뇌 후보 + $candChiefPower = $db->queryFirstColumn( + 'SELECT no FROM general WHERE nation = %i AND power >= %i AND level = 1 AND belong >= %i ORDER BY power DESC LIMIT %i', + $nationID, + GameConst::$chiefStatMin, + $maxBelong, 12 - $minChiefLevel + ); + //지장 수뇌 후보 + $candChiefIntel = $db->queryFirstColumn( + 'SELECT no FROM general WHERE nation = %i AND intel >= %i AND level = 1 AND belong >= %i ORDER BY intel DESC LIMIT %i', + $nationID, + GameConst::$chiefStatMin, + $maxBelong, + 12 - $minChiefLevel + ); + //무력, 지력이 모두 높은 장수를 고려하여.. + + $iterCandChiefPower = new \ArrayIterator($candChiefPower); + $iterCandChiefIntel = new \ArrayIterator($candChiefIntel); + + foreach(range(10, $minChiefLevel, -1) as $cheifLevel){ + if(key_exists($cheifLevel, $chiefCandidate)){ + continue; + } + + /** @var \ArrayIterator $iterCurrentType */ + $iterCurrentType = ($cheifLevel % 2 == 0)?$iterCandChiefPower:$iterCandChiefIntel; + $candidate = $iterCurrentType->current(); + + while(key_exist($candidate, $promoted)){ + $iterCurrentType->next(); + } + + $chiefCandidate[$cheifLevel] = $candidate; + $promoted[$candidate] = $cheifLevel; + } + + foreach($chiefCandidate as $chiefLevel=>$chiefID){ + $db->update('general', [ + 'level'=>$cheifLevel + ], 'no=%i',$chiefID); + } + } + + } + + protected function calcTexRate():int{ + $db = DB::db(); + $nation = $this->nation; + $env = $this->env; + + $nationID = $nation['nation']; + + //도시 + $cityCount = $db->queryFirstField('SELECT count(*) FROM city WHERE nation = %i', $nationID); + + if($cityCount == 0) { + $db->update('nation', [ + 'war'=>0, + 'rate'=>15 + ], 'nation=%i', $nationID); + return 15; + } else { + $devRate = $db->queryFirstRow( + 'select sum(pop)/sum(pop2)*100 as pop,(sum(agri)+sum(comm)+sum(secu)+sum(def)+sum(wall))/(sum(agri2)+sum(comm2)+sum(secu2)+sum(def2)+sum(wall2))*100 as all from city where nation=%i', + $nationID + ); + + $avg = ($devRate['pop'] + $devRate['all']) / 2; + + if($avg > 95) $rate = 25; + elseif($avg > 70) $rate = 20; + elseif($avg > 50) $rate = 15; + else $rate = 10; + + $db->update('nation', [ + 'war'=>0, + 'rate'=>$rate + ]); + return $rate; + } + } + + protected function calcGoldBillRate():int{ + $db = DB::db(); + $nation = $this->nation; + $env = $this->env; + + $nationID = $nation['nation']; + + $incomeList = getGoldIncome($nation['nation'], $nation['rate'], $env['gold_rate'], $env['type']); + $income = $gold + $incomeList[0] + $incomeList[1]; + $outcome = getGoldOutcome($nation['nation'], 100); // 100%의 지급량 + $bill = intval($income / $outcome * 80); // 수입의 80% 만 지급 + + if($bill < 20) { $bill = 20; } + if($bill > 200) { $bill = 200; } + + $db->update('nation', [ + 'bill'=>$bill, + ], 'nation=%i', $nationID); + + return $bill; + } + + protected function caclRiceBillRate():int{ + $db = DB::db(); + $nation = $this->nation; + $env = $this->env; + + $nationID = $nation['nation']; + + $incomeList = getRiceIncome($nation['nation'], $nation['rate'], $env['gold_rate'], $env['type']); + $income = $rice + $incomeList[0] + $incomeList[1]; + $outcome = getRiceOutcome($nation['nation'], 100); // 100%의 지급량 + $bill = intval($income / $outcome * 80); // 수입의 80% 만 지급 + + if($bill < 20) { $bill = 20; } + if($bill > 200) { $bill = 200; } + + $db->update('nation', [ + 'bill'=>$bill, + ], 'nation=%i', $nationID); + + return $bill; } } \ No newline at end of file diff --git a/hwe/sql/schema.sql b/hwe/sql/schema.sql index 38885bea..2f0d34a4 100644 --- a/hwe/sql/schema.sql +++ b/hwe/sql/schema.sql @@ -134,7 +134,8 @@ CREATE TABLE `general` ( INDEX `no_member` (`owner`), INDEX `npc` (`npc`), INDEX `npcid` (`npcid`), - INDEX `troop` (`troop`, `turntime`) + INDEX `troop` (`troop`, `turntime`), + INDEX `level` (`nation`, `level`) ) DEFAULT CHARSET=utf8mb4 ENGINE=MyISAM;