diff --git a/hwe/func.php b/hwe/func.php index 19301732..ef9fd43f 100644 --- a/hwe/func.php +++ b/hwe/func.php @@ -1811,6 +1811,71 @@ function searchDistance(int $from, int $maxDist=99, bool $distForm = false) { } } +/** + * $from 으로 지정한 도시의 인접 도시와 $to 도시의 최단 거리를 계산해 줌 + * @param $from 기준 도시 코드 + * @param $to 대상 도시 코드 + * @param $linkNationList 경로에 해당하는 국가 리스트 + * @return array $dist=>[cityID, $nation] 배열 가장 앞이 가장 가까움 + */ +function searchDistanceListToDest(int $from, int $to, array $linkNationList) { + $queue = new \SplQueue(); + + $cities = []; + + $db = DB::db(); + + //TODO: 도시-국가 캐싱이 있으면 쓸모 있지 않을까 + $allowedCityList = []; + foreach($db->queryAllLists( + 'SELECT city, nation FROM city WHERE nation IN %li', + $linkNationList + ) as [$cityID, $nationID]){ + $allowedCityList[$cityID] = $nationID; + } + + $remainFromCities = []; + foreach(array_keys(CityConst::byID($from)->path) as $fromCityID){ + if(key_exists($fromCityID, $allowedCityList)){ + $remainFromCities[$fromCityID] = true; + } + } + + if(!key_exists($to, $allowedCityList)){ + return []; + } + + $result = []; + $queue->enqueue([$to, 0]); + + while(!empty($remainFromCities) && !$queue->isEmpty()){ + list($cityID, $dist) = $queue->dequeue(); + if(key_exists($cityID, $cities)){ + continue; + } + + $cities[$cityID] = $dist; + + if(key_exists($cityID, $remainFromCities)){ + unset($remainFromCities[$cityID]); + $result[$dist][] = [$cityID, $allowedCityList[$cityID]]; + } + + foreach(array_keys(CityConst::byID($cityID)->path) as $connCityID){ + if($allowedCityList !== null && !key_exists($connCityID, $allowedCityList)){ + continue; + } + if(key_exists($connCityID, $cities)){ + continue; + } + $queue->enqueue([$connCityID, $dist+1]); + + } + } + + return $result; +} + function isNeighbor(int $nation1, int $nation2, bool $includeNoSupply=true) { if($nation1 === $nation2){ return false; diff --git a/hwe/sammo/Command/General/che_증여.php b/hwe/sammo/Command/General/che_증여.php index d8593224..bcd45ca8 100644 --- a/hwe/sammo/Command/General/che_증여.php +++ b/hwe/sammo/Command/General/che_증여.php @@ -74,7 +74,7 @@ class che_증여 extends Command\GeneralCommand{ $this->setCity(); $this->setNation(); - $destGeneral = General::createGeneralObjFromDB($this->arg['destGeneralID'], ['gold', 'nation'], 1); + $destGeneral = General::createGeneralObjFromDB($this->arg['destGeneralID'], ['gold', 'rice', 'nation'], 1); $this->setDestGeneral($destGeneral); $this->runnableConstraints=[ diff --git a/hwe/sammo/Command/General/che_출병.php b/hwe/sammo/Command/General/che_출병.php index fd08f0e4..be30fa10 100644 --- a/hwe/sammo/Command/General/che_출병.php +++ b/hwe/sammo/Command/General/che_출병.php @@ -55,13 +55,14 @@ class che_출병 extends Command\GeneralCommand{ $this->runnableConstraints=[ ConstraintHelper::NotOpeningPart($relYear), - ConstraintHelper::NearCity(1), + ConstraintHelper::NotSameDestCity(), ConstraintHelper::NotBeNeutral(), ConstraintHelper::OccupiedCity(), ConstraintHelper::ReqGeneralCrew(), ConstraintHelper::ReqGeneralRice($reqRice), ConstraintHelper::AllowWar(), - ConstraintHelper::BattleGroundCity(true), + ConstraintHelper::HasRoute(), + //ConstraintHelper::BattleGroundCity(true), ]; } @@ -108,18 +109,73 @@ class che_출병 extends Command\GeneralCommand{ $defenderNationID = $this->destCity['nation']; $date = $general->getTurnTime($general::TURNTIME_HM); - $defenderCityName = $this->destCity['name']; - $defenderCityID = $this->destCity['city']; - $josaRo = JosaUtil::pick($defenderCityName, '로'); + $attackerCityID = $general->getCityID(); + + + $finalTargetCityID = $this->destCity['city']; + $finalTargetCityName = $this->destCity['name']; + $logger = $general->getLogger(); + $allowedNationList = $db->queryFirstColumn('SELECT you FROM diplomacy WHERE state = 0 AND me = %i', $attackerNationID); + $allowedNationList[] = $attackerNationID; + $allowedNationList[] = 0; + + $distanceList = \sammo\searchDistanceListToDest($attackerCityID, $finalTargetCityID, $allowedNationList); + + $candidateCities = []; + + $minDist = Util::array_first_key($distanceList); + do { + //1: 최단 거리 도시 중 공격 대상이 있는가 확인 + //2: 최단 거리 + 1 도시 중 공격 대상이 있는가 확인 + foreach($distanceList as $dist => $distCitiesInfo){ + if($dist > $minDist + 1){ + break; + } + $currDist = $dist; + foreach($distCitiesInfo as [$distCityID, $distCityNation]){ + if($distCityNation !== $attackerNationID){ + $candidateCities[] = $distCityID; + } + } + + if($candidateCities){ + break 2; + } + } + + //3: 최단 거리 도시 중 아군 도시 선택 + foreach($distanceList[$minDist] as [$distCityID, $distCityNation]){ + if($distCityNation === $attackerNationID){ + $candidateCities[] = $distCityID; + } + } + }while(false); + + $defenderCityID = (int)Util::choiceRandom($candidateCities); + $defenderCityName = $this->destCity['name']; + $this->setDestCity($defenderCityID, null); + $josaRo = JosaUtil::pick($defenderCityName, '로'); + if($attackerNationID == $defenderNationID){ $logger->pushGeneralActionLog("본국입니다. {$defenderCityName}{$josaRo} 으로 이동합니다. <1>$date"); - $this->alternative = new che_이동($general, $this->env, $this->arg); + $this->alternative = new che_이동($general, $this->env, ['destCityID'=>$defenderCityID]); return false; } + if($finalTargetCityID !== $defenderCityID){ + $josaRo = JosaUtil::pick($finalTargetCityName, '로'); + $josaUl = JosaUtil::pick($defenderCityName, '을'); + if($minDist == $currDist){ + $logger->pushGeneralActionLog("{$finalTargetCityName}{$josaRo} 가기 위해 {$defenderCityName}{$josaUl} 거쳐야 합니다. <1>$date"); + } + else{ + $logger->pushGeneralActionLog("{$finalTargetCityName}{$josaRo} 가는 도중 {$defenderCityName}{$josaUl} 거치기로 합니다. <1>$date"); + } + } + $db->update('city', [ 'state'=>43, 'term'=>3 @@ -144,5 +200,29 @@ class che_출병 extends Command\GeneralCommand{ return true; } + public function getJSFiles(): array + { + return [ + 'js/defaultSelectCityByMap.js' + ]; + } + + public function getForm(): string + { + $srcCityName = \sammo\CityConst::byID($this->generalObj->getCityID())->name; + ob_start(); +?> +
+선택된 도시를 향해 침공을 합니다.
+침공 경로에 적군의 도시가 있다면 전투를 벌입니다.
+목록을 선택하거나 도시를 클릭하세요.
+ =>
+
+general)){ + if(!$throwExeception){return false; } + throw new \InvalidArgumentException("require city in general"); + } + + if(!key_exists('nation', $this->general)){ + if(!$throwExeception){return false; } + throw new \InvalidArgumentException("require nation in general"); + } + + return true; + } + + public function test():bool{ + $this->checkInputValues(); + $this->tested = true; + + $db = DB::db(); + + $allowedNationList = $db->queryFirstColumn('SELECT you FROM diplomacy WHERE state = 0 AND me = %i', $this->general['nation']); + $allowedNationList[] = $this->general['nation']; + $allowedNationList[] = 0; + + $distanceList = \sammo\searchDistanceListToDest($this->general['city'], $this->destCity['city'], $allowedNationList); + if(!$distanceList){ + $this->reason = "경로에 도달할 방법이 없습니다."; + return false; + } + + return true; + } +} \ No newline at end of file