self::NEAR_CITY, 'name' => self::NEAR_CITY, 'nation' => self::NEAR_CITY, 'npc' => self::NEAR_CITY, 'injury' => self::NEAR_CITY, 'leadership' => self::NEAR_CITY, 'strength' => self::NEAR_CITY, 'intel' => self::NEAR_CITY, 'picture' => self::NEAR_CITY, 'imgsvr' => self::NEAR_CITY, 'city' => self::NEAR_CITY, 'officer_level' => self::NEAR_CITY, 'officer_city' => self::NEAR_CITY, 'defence_train' => self::OUR_NATION, 'crewtype' => self::OUR_NATION, 'crew' => self::ON_CITY, 'train' => self::OUR_NATION, 'atmos' => self::OUR_NATION, ]; static $columnRemap = [ 'nation' => 'nationID', ]; static $customViewColumns = [ 'officerLevel' => self::NEAR_CITY, 'officerLevelText' => self::NEAR_CITY, 'lbonus' => self::NEAR_CITY, 'reservedCommand' => self::OUR_NATION, 'isOurNation' => self::NEAR_CITY, ]; public function validateArgs(): ?string { $v = new Validator($this->args); $v->rule('int', 'troopID'); if (!$v->validate()) { return $v->errorStr(); } return null; } public function getRequiredSessionMode(): int { return static::REQ_GAME_LOGIN | static::REQ_READ_ONLY; } private function getOfficerLevel($rawGeneral) { $level = $rawGeneral['officer_level']; return $level; } private function filterCity(array $rawCity, int $showLevel) { $cityID = $rawCity['city']; $db = DB::db(); $officerList = $db->query( 'SELECT no, npc, name, nation, picture, imgsvr FROM general WHERE city = %i AND 2 <= officer_level AND officer_level <= 4', $cityID ); $filteredCity = [ 'city' => $cityID, 'name' => $rawCity['name'], 'level' => $rawCity['level'], 'nation' => $rawCity['nation'], 'supply' => $rawCity['supply'], 'pop_max' => $rawCity['pop_max'], 'agri_max' => $rawCity['agri_max'], 'comm_max' => $rawCity['comm_max'], 'secu_max' => $rawCity['secu_max'], 'trade' => $rawCity['trade'], 'def_max' => $rawCity['def_max'], 'wall_max' => $rawCity['wall_max'], 'region' => $rawCity['region'], 'officerList' => $officerList, ]; if ($showLevel >= self::ON_CITY) { $filteredCity = array_merge($filteredCity, [ 'pop' => $rawCity['pop'], 'agri' => $rawCity['agri'], 'comm' => $rawCity['comm'], 'secu' => $rawCity['secu'], 'def' => $rawCity['def'], 'wall' => $rawCity['wall'], 'officer_set' => $rawCity['officer_set'], ]); } return $filteredCity; } private function getGeneralList(int $cityID, int $nationID, int $showLevel) { if ($showLevel == self::FAR_CITY) { return [ 'column' => [], 'list' => [] ]; } $db = DB::db(); $nationStaticList = getAllNationStaticInfo(); $rawGeneralList = Util::convertArrayToDict($db->query('SELECT %l from general WHERE city = %i ORDER BY turntime ASC', Util::formatListOfBackticks(array_keys(static::$viewColumns)), $cityID), 'no'); $ourNationGeneralIDList = new Set(); foreach ($rawGeneralList as $rawGeneral) { if ($rawGeneral['nation'] == $nationID) { $ourNationGeneralIDList->add($rawGeneral['no']); } } $reservedCommand = []; $reservedCommandTargetGeneralIDList = []; foreach ($rawGeneralList as $rawGeneral) { if ($rawGeneral['nation'] != $nationID) { continue; } if ($rawGeneral['npc'] < 2) { $reservedCommandTargetGeneralIDList[$rawGeneral['no']] = $rawGeneral['no']; } } if ($reservedCommandTargetGeneralIDList) { $rawTurnList = $db->query( 'SELECT general_id, turn_idx, action, arg, brief FROM general_turn WHERE general_id IN %li AND turn_idx < 5 ORDER BY general_id asc, turn_idx asc', array_values($reservedCommandTargetGeneralIDList) ); foreach ($rawTurnList as $rawTurn) { [ 'general_id' => $generalID, 'action' => $action, 'arg' => $arg, 'brief' => $brief, ] = $rawTurn; if (!key_exists($generalID, $reservedCommand)) { $reservedCommand[$generalID] = []; } $reservedCommand[$generalID][] = [ 'action' => $action, 'arg' => $arg, 'brief' => $brief ]; } } $specialViewFilter = [ 'officerLevel' => fn ($rawGeneral) => $this->getOfficerLevel($rawGeneral), 'officerLevelText' => fn ($rawGeneral) => getOfficerLevelText($this->getOfficerLevel($rawGeneral), $nationStaticList[$rawGeneral['nation']]['level']), 'lbonus' => fn ($rawGeneral) => calcLeadershipBonus($rawGeneral['officer_level'], $nationStaticList[$rawGeneral['nation']]['level']), 'reservedCommand' => fn ($rawGeneral) => $reservedCommand[$rawGeneral['no']] ?? null, 'isOurNation' => fn ($rawGeneral) => $rawGeneral['nation'] == $nationID, ]; $showFullColumn = $showLevel == self::OUR_NATION || $ourNationGeneralIDList->count() > 0; $resultColumns = []; foreach (static::$viewColumns as $column => $reqShowLevel) { if (!$showFullColumn && $reqShowLevel > $showLevel) { continue; } if (key_exists($column, static::$columnRemap)) { $newColumn = static::$columnRemap[$column]; if ($newColumn !== null) { $resultColumns[$newColumn] = $column; } } else { $resultColumns[$column] = $column; } } foreach (static::$customViewColumns as $column => $reqShowLevel) { if (!$showFullColumn && $reqShowLevel > $showLevel) { continue; } $resultColumns[$column] = $column; } $generalList = []; foreach ($rawGeneralList as $rawGeneral) { $item = []; foreach ($resultColumns as $column) { $reqShowLevel = static::$viewColumns[$column]; if ($rawGeneral['nation'] != $nationID && $reqShowLevel == self::OUR_NATION) { $item[] = null; continue; } if (key_exists($column, $specialViewFilter)) { $value = $specialViewFilter[$column]($rawGeneral); } else { $value = $rawGeneral[$column]; } $item[] = $value; } $generalList[] = $item; } return [ 'column' => array_keys($resultColumns), 'list' => $generalList, ]; } private function calcShowLevel(int $cityID, int $currentCityID, int $nationID, Set $cityList, bool $isSpyPresent): int { $db = DB::db(); if ($cityList->contains($currentCityID)) { return self::OUR_NATION; } if ($cityID == $currentCityID) { return self::ON_CITY; } if ($isSpyPresent) { return self::ON_CITY; } $existsGeneralID = $db->queryFirstField( 'SELECT no FROM general WHERE city = %i AND nation = %i LIMIT 1', $cityID, $nationID ); if ($existsGeneralID) { return self::ON_CITY; } $cityConstObj = CityConst::byID($cityID); $nearCityIDList = array_keys($cityConstObj->path); foreach ($nearCityIDList as $nearCityID) { if ($cityList->contains($nearCityID)) { return self::NEAR_CITY; } } return self::FAR_CITY; } public function launch(Session $session, ?\DateTimeInterface $modifiedSince, ?string $reqEtag): null | string | array | APIRecoveryType { increaseRefresh("도시정보", 1); $db = DB::db(); $gameStor = \sammo\KVStorage::getStorage($db, 'game_env'); $lastExecuted = $gameStor->getValue('turntime'); $me = $db->queryFirstRow('SELECT con, turntime, belong, city, nation, officer_level, permission, penalty FROM general WHERE owner=%i', $session->getUserID()); if (key_exists('cityID', $this->args)) { $cityID = (int)$this->args['cityID']; } else { $cityID = $me['city']; } $con = checkLimit($me['con']); if ($con >= 2) { return '접속 제한중입니다. 1턴 이내에 너무 많은 갱신을 하셨습니다.'; } $nationID = $me['nation']; $currentCityID = $me['city']; //TODO: 조건 조사 $cityList = new Set($db->queryFirstColumn('SELECT city FROM city WHERE nation=%i', $nationID)); $spyList = JSON::decode($db->queryFirstField('SELECT spy FROM nation WHERE nation=%i', $nationID)); $showLevel = $this->calcShowLevel($cityID, $currentCityID, $nationID, $cityList, key_exists($cityID, $spyList)); $rawCity = $db->queryFirstRow('SELECT * FROM city WHERE city=%i', $cityID); $filteredCity = $this->filterCity($rawCity, $showLevel); $result = [ 'result' => true, 'lastExcuted' => $lastExecuted, 'cityInfo' => $filteredCity, 'spyList' => $spyList, 'nationCityList' => $cityList->toArray(), 'myGeneralID' => $session->generalID, 'currentCityID' => $currentCityID, 'showLevel' => $showLevel, 'cityGeneralList' => $this->getGeneralList($cityID, $nationID, $showLevel) ]; return $result; } }