choice($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, $nationCnt] = DB::db()->queryFirstList('SELECT sum(tech), COUNT(tech) FROM nation WHERE level > 0') ?? [0, 0]; if ($nationCnt == 0) { return 0; } return Util::toInt($techSum / $nationCnt); } private function buildNation(RandUtil $rng, 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 = $rng->choice(GetNationColors()); $nationObj = new Nation($rng, $nationID, $nationName, $color, 0, 2000, "우리도 할 수 있다! {$cityName}군", $tech, null, 2, [$cityName]); $nationObj->build($env); $ruler = (new GeneralBuilder($rng, "{$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(), $rng, 0, $genCnt - 1) as $pickedNPC) { //대충 10년후부터 6년마다 절반? $deadYear = $deadYearMin + Util::toInt(60 * (1 - log($rng->nextRange(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 = []; $rng = new RandUtil(new LiteHashDRBG(Util::simpleSerialize( UniqueConst::$hiddenSeed, 'RaiseNPCNation', $env['year'], $env['month'] ))); $avgCity = $this->calcAvgNationCity($rng, $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($rng, $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("【공지】공백지에 임의의 국가가 생성되었습니다."); $logger->flush(); } return [__CLASS__, count($npcCities)]; } }