forked from devsam/core
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3762d9050c
|
||
|
|
75d88d48b6
|
||
|
|
633defcb21
|
||
|
|
9e997a7ee2
|
||
|
|
9a8f70d87b
|
||
|
|
7132aeab0c
|
||
|
|
994c73adb2
|
||
|
|
9206b74500
|
||
|
|
5294aefbbd
|
||
|
|
8f0f586af9
|
||
|
|
9121533b93
|
||
|
|
90bff7b210
|
||
|
|
55f7b87ced
|
@@ -1,6 +1,6 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2022 Hide_D, 62che
|
||||
Copyright (c) 2023 Hide_D, 62che
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -423,8 +423,6 @@ function postUpdateMonthly(RandUtil $rng)
|
||||
if ($admin['year'] >= $admin['startyear'] + 2) {
|
||||
checkWander($rng);
|
||||
}
|
||||
// 작위 업데이트
|
||||
updateNationState();
|
||||
updateGeneralNumber();
|
||||
refreshNationStaticInfo();
|
||||
// 천통여부 검사
|
||||
@@ -467,209 +465,6 @@ function checkWander(RandUtil $rng)
|
||||
}
|
||||
}
|
||||
|
||||
function updateNationState()
|
||||
{
|
||||
$db = DB::db();
|
||||
$gameStor = KVStorage::getStorage($db, 'game_env');
|
||||
|
||||
$admin = $gameStor->getValues(['killturn', 'year', 'month', 'fiction', 'startyear', 'show_img_level', 'turnterm', 'turntime']);
|
||||
$year = $admin['year'];
|
||||
$month = $admin['month'];
|
||||
$startYear = $admin['startyear'];
|
||||
|
||||
|
||||
$nationCityCounts = [];
|
||||
foreach($db->queryAllLists('SELECT nation, count(*) FROM city WHERE LEVEL>=4 GROUP BY nation') as [$nationID, $cityCnt]){
|
||||
$nationCityCounts[$nationID] = $cityCnt;
|
||||
}
|
||||
|
||||
|
||||
$nationLevelByCityCnt = [
|
||||
0, //방랑군
|
||||
1, //호족
|
||||
2, //군벌
|
||||
5, //주자사
|
||||
8, //주목
|
||||
11, //공
|
||||
16, //왕
|
||||
21, //황제
|
||||
];
|
||||
|
||||
foreach ($db->query('SELECT nation,name,level,tech,aux FROM nation') as $nation) {
|
||||
//TODO: level이 진관수이소중대특 체계를 벗어날 수 있음
|
||||
$nationID = $nation['nation'];
|
||||
$cityCnt = $nationCityCounts[$nationID] ?? 0;
|
||||
|
||||
/** @var int */
|
||||
$nationlevel = 0;
|
||||
foreach($nationLevelByCityCnt as $cmpNationLevel => $cmpCityCnt){
|
||||
if($cityCnt < $cmpCityCnt){
|
||||
break;
|
||||
}
|
||||
$nationlevel = $cmpNationLevel;
|
||||
}
|
||||
|
||||
if ($nationlevel > $nation['level']) {
|
||||
$levelDiff = $nationlevel - $nation['level'];
|
||||
$oldLevel = $nation['level'];
|
||||
$nation['level'] = $nationlevel;
|
||||
|
||||
$updateVals = [
|
||||
'level' => $nationlevel,
|
||||
'gold' => $db->sqleval('gold + %i', $nationlevel * 1000),
|
||||
'rice' => $db->sqleval('rice + %i', $nationlevel * 1000),
|
||||
];
|
||||
|
||||
$nationName = $nation['name'];
|
||||
$lordName = $db->queryFirstField('SELECT name FROM general WHERE nation = %i AND officer_level = 12', $nationID);
|
||||
|
||||
$oldNationLevelText = getNationLevel($oldLevel);
|
||||
$nationLevelText = getNationLevel($nationlevel);
|
||||
|
||||
$logger = new ActionLogger(0, $nationID, $year, $month, false);
|
||||
$josaYi = JosaUtil::pick($lordName, '이');
|
||||
|
||||
switch ($nationlevel) {
|
||||
case 7: //황제
|
||||
$josaRo = JosaUtil::pick($nationLevelText, '로');
|
||||
$logger->pushGlobalHistoryLog("<Y><b>【작위】</b></><D><b>{$nationName}</b></> {$oldNationLevelText} <Y>{$lordName}</>{$josaYi} <C>{$nationLevelText}</>{$josaRo} 옹립되었습니다.");
|
||||
$logger->pushNationalHistoryLog("<D><b>{$nationName}</b></> {$oldNationLevelText} <Y>{$lordName}</>{$josaYi} <C>{$nationLevelText}</>{$josaRo} 옹립");
|
||||
$auxVal = Json::decode($nation['aux']);
|
||||
$auxVal['can_국기변경'] = 1;
|
||||
$auxVal['can_국호변경'] = 1;
|
||||
$updateVals['aux'] = Json::encode($auxVal);
|
||||
break;
|
||||
case 6: //왕
|
||||
$josaRo = JosaUtil::pick($nationLevelText, '로');
|
||||
$logger->pushGlobalHistoryLog("<Y><b>【작위】</b></><D><b>{$nationName}</b></>의 <Y>{$lordName}</>{$josaYi} <C>$nationLevelText</>{$josaRo} 책봉되었습니다.");
|
||||
$logger->pushNationalHistoryLog("<D><b>{$nationName}</b></>의 <Y>{$lordName}</>{$josaYi} <C>$nationLevelText</>{$josaRo} 책봉");
|
||||
break;
|
||||
case 5: //공
|
||||
case 4: //주목
|
||||
case 3: //주자사
|
||||
$josaRo = JosaUtil::pick($nationLevelText, '로');
|
||||
$logger->pushGlobalHistoryLog("<Y><b>【작위】</b></><D><b>{$nationName}</b></>의 <Y>{$lordName}</>{$josaYi} <C>$nationLevelText</>{$josaRo} 임명되었습니다.");
|
||||
$logger->pushNationalHistoryLog("<D><b>{$nationName}</b></>의 <Y>{$lordName}</>{$josaYi} <C>$nationLevelText</>{$josaRo} 임명됨");
|
||||
break;
|
||||
case 2: //군벌
|
||||
$josaRa = JosaUtil::pick($nationName, '라');
|
||||
$josaRo = JosaUtil::pick($nationLevelText, '로');
|
||||
$logger->pushGlobalHistoryLog("<Y><b>【작위】</b></><Y>{$lordName}</>{$josaYi} 독립하여 <D><b>{$nationName}</b></>{$josaRa}는 <C>$nationLevelText</>{$josaRo} 나섰습니다.");
|
||||
$logger->pushNationalHistoryLog("<Y>{$lordName}</>{$josaYi} 독립하여 <D><b>{$nationName}</b></>{$josaRa}는 <C>$nationLevelText</>{$josaRo} 나서다");
|
||||
break;
|
||||
}
|
||||
|
||||
$db->update('nation', $updateVals, 'nation=%i', $nation['nation']);
|
||||
$logger->flush();
|
||||
|
||||
$turnRows = [];
|
||||
foreach (Util::range(getNationChiefLevel($nation['level']), 12) as $chiefLevel) {
|
||||
foreach (Util::range(GameConst::$maxChiefTurn) as $turnIdx) {
|
||||
$turnRows[] = [
|
||||
'nation_id' => $nation['nation'],
|
||||
'officer_level' => $chiefLevel,
|
||||
'turn_idx' => $turnIdx,
|
||||
'action' => '휴식',
|
||||
'arg' => null,
|
||||
'brief' => '휴식'
|
||||
];
|
||||
}
|
||||
}
|
||||
$db->insertIgnore('nation_turn', $turnRows);
|
||||
|
||||
if ($levelDiff) {
|
||||
//유니크 아이템 하나 돌리자
|
||||
$targetKillTurn = $admin['killturn'];
|
||||
$targetKillTurn -= 24 * 60 / $admin['turnterm'];
|
||||
$nationGenIDList = $db->queryFirstColumn(
|
||||
'SELECT no FROM general WHERE nation = %i AND killturn >= %i AND npc < 2',
|
||||
$nation['nation'],
|
||||
$targetKillTurn
|
||||
);
|
||||
$nationGenList = General::createGeneralObjListFromDB($nationGenIDList, ['belong', 'npc', 'aux'], 2);
|
||||
$chiefID = null;
|
||||
|
||||
$uniqueLotteryWeightList = [];
|
||||
|
||||
$relYear = $year - $startYear;
|
||||
$maxTrialCountByYear = 1;
|
||||
foreach (GameConst::$maxUniqueItemLimit as $tmpVals) {
|
||||
[$targetYear, $targetTrialCnt] = $tmpVals;
|
||||
if ($relYear < $targetYear) {
|
||||
break;
|
||||
}
|
||||
$maxTrialCountByYear = $targetTrialCnt;
|
||||
}
|
||||
foreach ($nationGenList as $nationGen) {
|
||||
if ($nationGen->getVar('officer_level') == 12) {
|
||||
$chiefID = $nationGen->getID();
|
||||
}
|
||||
$trialCnt = min($maxTrialCountByYear, count(GameConst::$allItems));
|
||||
|
||||
foreach ($nationGen->getItems() as $item) {
|
||||
if (!$item->isBuyable()) {
|
||||
$trialCnt -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ($trialCnt <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$score = $nationGen->getVar('belong') + 10;
|
||||
|
||||
if ($nationGen->getVar('officer_level') == 12) {
|
||||
$score += 60;
|
||||
} else if ($nationGen->getVar('officer_level') == 11) {
|
||||
$score += 30;
|
||||
} else if ($nationGen->getVar('officer_level') > 4) {
|
||||
$score += 15;
|
||||
}
|
||||
|
||||
$score *= 2 ** $trialCnt;
|
||||
|
||||
$uniqueLotteryWeightList[$nationGen->getID()] = [$nationGen, $score];
|
||||
}
|
||||
|
||||
$nationLevelUpRNG = new RandUtil(new LiteHashDRBG(Util::simpleSerialize(
|
||||
UniqueConst::$hiddenSeed,
|
||||
'nationLevelUp',
|
||||
$year,
|
||||
$month,
|
||||
$nationID
|
||||
)));
|
||||
|
||||
foreach (Util::range($levelDiff) as $idx) {
|
||||
if (!$uniqueLotteryWeightList) {
|
||||
break;
|
||||
}
|
||||
|
||||
/** @var General */
|
||||
$winnerObj = $nationLevelUpRNG->choiceUsingWeightPair($uniqueLotteryWeightList);
|
||||
unset($uniqueLotteryWeightList[$winnerObj->getID()]);
|
||||
|
||||
$givenUniqueRNG = new RandUtil(new LiteHashDRBG(Util::simpleSerialize(
|
||||
UniqueConst::$hiddenSeed,
|
||||
'givenUnique',
|
||||
$year,
|
||||
$month,
|
||||
$nationID,
|
||||
$winnerObj->getID(),
|
||||
)));
|
||||
giveRandomUniqueItem($givenUniqueRNG, $winnerObj, '작위보상');
|
||||
$winnerObj->applyDB($db);
|
||||
}
|
||||
|
||||
if ($chiefID) {
|
||||
$chiefObj = General::createGeneralObjFromDB($chiefID, ['belong', 'npc', 'aux'], 2);
|
||||
$chiefObj->increaseInheritancePoint(InheritanceKey::unifier, 250 * $levelDiff);
|
||||
$chiefObj->applyDB($db);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkStatistic()
|
||||
{
|
||||
$db = DB::db();
|
||||
|
||||
@@ -0,0 +1,332 @@
|
||||
<?php
|
||||
|
||||
namespace sammo\API\City;
|
||||
|
||||
use ArrayObject;
|
||||
use Ds\Set;
|
||||
use sammo\CityConst;
|
||||
use sammo\DB;
|
||||
use sammo\Enums\APIRecoveryType;
|
||||
use sammo\General;
|
||||
use sammo\Json;
|
||||
use sammo\Session;
|
||||
use sammo\Util;
|
||||
use sammo\Validator;
|
||||
|
||||
use function sammo\calcLeadershipBonus;
|
||||
use function sammo\checkLimit;
|
||||
use function sammo\checkSecretPermission;
|
||||
use function sammo\getAllNationStaticInfo;
|
||||
use function sammo\getBillByLevel;
|
||||
use function sammo\getDedLevelText;
|
||||
use function sammo\getHonor;
|
||||
use function sammo\getNationStaticInfo;
|
||||
use function sammo\getOfficerLevelText;
|
||||
use function sammo\increaseRefresh;
|
||||
|
||||
class GetCityInfo extends \sammo\BaseAPI
|
||||
{
|
||||
const FAR_CITY = 0;
|
||||
const NEAR_CITY = 1;
|
||||
const ON_CITY = 2;
|
||||
const OUR_NATION = 3;
|
||||
|
||||
static $viewColumns = [
|
||||
'no' => 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;
|
||||
}
|
||||
}
|
||||
@@ -569,6 +569,14 @@ class GetFrontInfo extends \sammo\BaseAPI
|
||||
|
||||
//TODO: 마지막 투표, 토너먼트, 베팅을 했는지 정보를 별도로 가져와야 함. aux?
|
||||
|
||||
$auxInfo = [];
|
||||
|
||||
if ($globalInfo['lastVote']) {
|
||||
$myLastVoteID = $db->queryFirstField('SELECT vote_id FROM vote WHERE general_id = %i ORDER BY vote_id DESC LIMIT 1', $generalID);
|
||||
if($myLastVoteID) {
|
||||
$auxInfo['myLastVote'] = $myLastVoteID;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'result' => true,
|
||||
@@ -577,6 +585,7 @@ class GetFrontInfo extends \sammo\BaseAPI
|
||||
'nation' => $nationInfo,
|
||||
'general' => $generalInfo,
|
||||
'city' => $cityInfo,
|
||||
'aux' => $auxInfo,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,10 +86,10 @@ class GetConst extends \sammo\BaseAPI
|
||||
if ($lastModified !== null) {
|
||||
$versionHash = "lt{$lastModified}";
|
||||
} else {
|
||||
$versionHash = VersionGit::getHash();
|
||||
$versionHash = VersionGit::getVersion();
|
||||
}
|
||||
} else {
|
||||
$versionHash = VersionGit::getHash();
|
||||
$versionHash = VersionGit::getVersion();
|
||||
}
|
||||
|
||||
$apiVersion = static::CONST_API_VERSION;
|
||||
|
||||
@@ -6,8 +6,10 @@ use sammo\Session;
|
||||
use DateTimeInterface;
|
||||
use sammo\DB;
|
||||
use sammo\Enums\APIRecoveryType;
|
||||
use sammo\Enums\GeneralStorKey;
|
||||
use sammo\Enums\MessageType;
|
||||
use sammo\Json;
|
||||
use sammo\KVStorage;
|
||||
use sammo\Message;
|
||||
use sammo\TimeUtil;
|
||||
use sammo\Util;
|
||||
@@ -140,9 +142,19 @@ class GetRecentMessage extends \sammo\BaseAPI
|
||||
array_pop($result[$lastType]);
|
||||
}
|
||||
|
||||
$generalStor = KVStorage::getStorage($db, "general_{$generalID}");
|
||||
[$latestReadDiplomacyMsg, $latestReadPrivateMsg] = $generalStor->getValuesAsArray([
|
||||
GeneralStorKey::latestReadDiplomacyMsg,
|
||||
GeneralStorKey::latestReadPrivateMsg
|
||||
]);
|
||||
|
||||
$result['sequence'] = $nextSequence;
|
||||
$result['nationID'] = $nationID;
|
||||
$result['generalName'] = $generalName;
|
||||
$result['latestRead'] = [
|
||||
'diplomacy' => $latestReadDiplomacyMsg ?? 0,
|
||||
'private' => $latestReadPrivateMsg ?? 0,
|
||||
];
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace sammo\API\Message;
|
||||
|
||||
use sammo\DB;
|
||||
use sammo\Enums\APIRecoveryType;
|
||||
use sammo\Enums\GeneralStorKey;
|
||||
use sammo\Enums\MessageType;
|
||||
use sammo\KVStorage;
|
||||
use sammo\Session;
|
||||
use sammo\Validator;
|
||||
|
||||
class ReadLatestMessage extends \sammo\BaseAPI
|
||||
{
|
||||
public function validateArgs(): ?string
|
||||
{
|
||||
//type이 개인 메시지 혹은 외교 메시지여야함
|
||||
$v = new Validator($this->args);
|
||||
$v->rule('required', ['type', 'msgID'])
|
||||
->rule('in', 'type', [MessageType::private->value, MessageType::diplomacy->value])
|
||||
->rule('int', 'msgID');
|
||||
if (!$v->validate()) {
|
||||
return $v->errorStr();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getRequiredSessionMode(): int
|
||||
{
|
||||
return static::REQ_GAME_LOGIN | static::REQ_READ_ONLY;
|
||||
}
|
||||
|
||||
public function launch(Session $session, ?\DateTimeInterface $modifiedSince, ?string $reqEtag): null | string | array | APIRecoveryType
|
||||
{
|
||||
$db = DB::db();
|
||||
$generalID = $session->generalID;
|
||||
$generalStor = KVStorage::getStorage($db, "general_{$generalID}");
|
||||
$type = MessageType::from($this->args['type']);
|
||||
$msgID = $this->args['msgID'];
|
||||
|
||||
$storKey = GeneralStorKey::latestReadPrivateMsg;
|
||||
if($type == MessageType::diplomacy){
|
||||
$storKey = GeneralStorKey::latestReadDiplomacyMsg;
|
||||
}
|
||||
$oldValue = $generalStor->getValue($storKey);
|
||||
if($oldValue === null || $oldValue < $msgID){
|
||||
$generalStor->setValue($storKey, $msgID);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
namespace sammo\Enums;
|
||||
|
||||
enum GeneralStorKey: string{
|
||||
case latestReadPrivateMsg = 'latestReadPrivateMsg';
|
||||
case latestReadDiplomacyMsg = 'latestReadDiplomacyMsg';
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
<?php
|
||||
|
||||
namespace sammo\Event\Action;
|
||||
|
||||
use sammo\ActionLogger;
|
||||
use sammo\CityConst;
|
||||
use sammo\DB;
|
||||
use sammo\Enums\InheritanceKey;
|
||||
use sammo\GameConst;
|
||||
use sammo\General;
|
||||
use sammo\JosaUtil;
|
||||
use sammo\Json;
|
||||
use sammo\KVStorage;
|
||||
use sammo\LiteHashDRBG;
|
||||
use sammo\RandUtil;
|
||||
use sammo\UniqueConst;
|
||||
use sammo\Util;
|
||||
|
||||
use function sammo\getNationChiefLevel;
|
||||
use function sammo\getNationLevel;
|
||||
use function sammo\giveRandomUniqueItem;
|
||||
|
||||
class UpdateNationLevel extends \sammo\Event\Action
|
||||
{
|
||||
public function run(array $env)
|
||||
{
|
||||
$db = DB::db();
|
||||
|
||||
$year = $env['year'];
|
||||
$month = $env['month'];
|
||||
$startYear = $env['startyear'];
|
||||
|
||||
|
||||
$nationCityCounts = [];
|
||||
foreach ($db->queryAllLists('SELECT nation, count(*) FROM city WHERE LEVEL>=4 GROUP BY nation') as [$nationID, $cityCnt]) {
|
||||
$nationCityCounts[$nationID] = $cityCnt;
|
||||
}
|
||||
|
||||
|
||||
$nationLevelByCityCnt = [
|
||||
0, //방랑군
|
||||
1, //호족
|
||||
2, //군벌
|
||||
5, //주자사
|
||||
8, //주목
|
||||
11, //공
|
||||
16, //왕
|
||||
21, //황제
|
||||
];
|
||||
|
||||
foreach ($db->query('SELECT nation,name,level,tech,aux FROM nation') as $nation) {
|
||||
//TODO: level이 진관수이소중대특 체계를 벗어날 수 있음
|
||||
$nationID = $nation['nation'];
|
||||
$cityCnt = $nationCityCounts[$nationID] ?? 0;
|
||||
|
||||
/** @var int */
|
||||
$nationlevel = 0;
|
||||
foreach ($nationLevelByCityCnt as $cmpNationLevel => $cmpCityCnt) {
|
||||
if ($cityCnt < $cmpCityCnt) {
|
||||
break;
|
||||
}
|
||||
$nationlevel = $cmpNationLevel;
|
||||
}
|
||||
|
||||
if ($nationlevel > $nation['level']) {
|
||||
$levelDiff = $nationlevel - $nation['level'];
|
||||
$oldLevel = $nation['level'];
|
||||
$nation['level'] = $nationlevel;
|
||||
|
||||
$updateVals = [
|
||||
'level' => $nationlevel,
|
||||
'gold' => $db->sqleval('gold + %i', $nationlevel * 1000),
|
||||
'rice' => $db->sqleval('rice + %i', $nationlevel * 1000),
|
||||
];
|
||||
|
||||
$nationName = $nation['name'];
|
||||
$lordName = $db->queryFirstField('SELECT name FROM general WHERE nation = %i AND officer_level = 12', $nationID);
|
||||
|
||||
$oldNationLevelText = getNationLevel($oldLevel);
|
||||
$nationLevelText = getNationLevel($nationlevel);
|
||||
|
||||
$logger = new ActionLogger(0, $nationID, $year, $month, false);
|
||||
$josaYi = JosaUtil::pick($lordName, '이');
|
||||
|
||||
switch ($nationlevel) {
|
||||
case 7: //황제
|
||||
$josaRo = JosaUtil::pick($nationLevelText, '로');
|
||||
$logger->pushGlobalHistoryLog("<Y><b>【작위】</b></><D><b>{$nationName}</b></> {$oldNationLevelText} <Y>{$lordName}</>{$josaYi} <C>{$nationLevelText}</>{$josaRo} 옹립되었습니다.");
|
||||
$logger->pushNationalHistoryLog("<D><b>{$nationName}</b></> {$oldNationLevelText} <Y>{$lordName}</>{$josaYi} <C>{$nationLevelText}</>{$josaRo} 옹립");
|
||||
$auxVal = Json::decode($nation['aux']);
|
||||
$auxVal['can_국기변경'] = 1;
|
||||
$auxVal['can_국호변경'] = 1;
|
||||
$updateVals['aux'] = Json::encode($auxVal);
|
||||
break;
|
||||
case 6: //왕
|
||||
$josaRo = JosaUtil::pick($nationLevelText, '로');
|
||||
$logger->pushGlobalHistoryLog("<Y><b>【작위】</b></><D><b>{$nationName}</b></>의 <Y>{$lordName}</>{$josaYi} <C>$nationLevelText</>{$josaRo} 책봉되었습니다.");
|
||||
$logger->pushNationalHistoryLog("<D><b>{$nationName}</b></>의 <Y>{$lordName}</>{$josaYi} <C>$nationLevelText</>{$josaRo} 책봉");
|
||||
break;
|
||||
case 5: //공
|
||||
case 4: //주목
|
||||
case 3: //주자사
|
||||
$josaRo = JosaUtil::pick($nationLevelText, '로');
|
||||
$logger->pushGlobalHistoryLog("<Y><b>【작위】</b></><D><b>{$nationName}</b></>의 <Y>{$lordName}</>{$josaYi} <C>$nationLevelText</>{$josaRo} 임명되었습니다.");
|
||||
$logger->pushNationalHistoryLog("<D><b>{$nationName}</b></>의 <Y>{$lordName}</>{$josaYi} <C>$nationLevelText</>{$josaRo} 임명됨");
|
||||
break;
|
||||
case 2: //군벌
|
||||
$josaRa = JosaUtil::pick($nationName, '라');
|
||||
$josaRo = JosaUtil::pick($nationLevelText, '로');
|
||||
$logger->pushGlobalHistoryLog("<Y><b>【작위】</b></><Y>{$lordName}</>{$josaYi} 독립하여 <D><b>{$nationName}</b></>{$josaRa}는 <C>$nationLevelText</>{$josaRo} 나섰습니다.");
|
||||
$logger->pushNationalHistoryLog("<Y>{$lordName}</>{$josaYi} 독립하여 <D><b>{$nationName}</b></>{$josaRa}는 <C>$nationLevelText</>{$josaRo} 나서다");
|
||||
break;
|
||||
}
|
||||
|
||||
$db->update('nation', $updateVals, 'nation=%i', $nation['nation']);
|
||||
$logger->flush();
|
||||
|
||||
$turnRows = [];
|
||||
foreach (Util::range(getNationChiefLevel($nation['level']), 12) as $chiefLevel) {
|
||||
foreach (Util::range(GameConst::$maxChiefTurn) as $turnIdx) {
|
||||
$turnRows[] = [
|
||||
'nation_id' => $nation['nation'],
|
||||
'officer_level' => $chiefLevel,
|
||||
'turn_idx' => $turnIdx,
|
||||
'action' => '휴식',
|
||||
'arg' => null,
|
||||
'brief' => '휴식'
|
||||
];
|
||||
}
|
||||
}
|
||||
$db->insertIgnore('nation_turn', $turnRows);
|
||||
|
||||
if ($levelDiff) {
|
||||
//유니크 아이템 하나 돌리자
|
||||
$targetKillTurn = $env['killturn'];
|
||||
$targetKillTurn -= 24 * 60 / $env['turnterm'];
|
||||
$nationGenIDList = $db->queryFirstColumn(
|
||||
'SELECT no FROM general WHERE nation = %i AND killturn >= %i AND npc < 2',
|
||||
$nation['nation'],
|
||||
$targetKillTurn
|
||||
);
|
||||
$nationGenList = General::createGeneralObjListFromDB($nationGenIDList, ['belong', 'npc', 'aux'], 2);
|
||||
$chiefID = null;
|
||||
|
||||
$uniqueLotteryWeightList = [];
|
||||
|
||||
$relYear = $year - $startYear;
|
||||
$maxTrialCountByYear = 1;
|
||||
foreach (GameConst::$maxUniqueItemLimit as $tmpVals) {
|
||||
[$targetYear, $targetTrialCnt] = $tmpVals;
|
||||
if ($relYear < $targetYear) {
|
||||
break;
|
||||
}
|
||||
$maxTrialCountByYear = $targetTrialCnt;
|
||||
}
|
||||
foreach ($nationGenList as $nationGen) {
|
||||
if ($nationGen->getVar('officer_level') == 12) {
|
||||
$chiefID = $nationGen->getID();
|
||||
}
|
||||
$trialCnt = min($maxTrialCountByYear, count(GameConst::$allItems));
|
||||
|
||||
foreach ($nationGen->getItems() as $item) {
|
||||
if (!$item->isBuyable()) {
|
||||
$trialCnt -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ($trialCnt <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$score = $nationGen->getVar('belong') + 10;
|
||||
|
||||
if ($nationGen->getVar('officer_level') == 12) {
|
||||
$score += 60;
|
||||
} else if ($nationGen->getVar('officer_level') == 11) {
|
||||
$score += 30;
|
||||
} else if ($nationGen->getVar('officer_level') > 4) {
|
||||
$score += 15;
|
||||
}
|
||||
|
||||
$score *= 2 ** $trialCnt;
|
||||
|
||||
$uniqueLotteryWeightList[$nationGen->getID()] = [$nationGen, $score];
|
||||
}
|
||||
|
||||
$nationLevelUpRNG = new RandUtil(new LiteHashDRBG(Util::simpleSerialize(
|
||||
UniqueConst::$hiddenSeed,
|
||||
'nationLevelUp',
|
||||
$year,
|
||||
$month,
|
||||
$nationID
|
||||
)));
|
||||
|
||||
foreach (Util::range($levelDiff) as $idx) {
|
||||
if (!$uniqueLotteryWeightList) {
|
||||
break;
|
||||
}
|
||||
|
||||
/** @var General */
|
||||
$winnerObj = $nationLevelUpRNG->choiceUsingWeightPair($uniqueLotteryWeightList);
|
||||
unset($uniqueLotteryWeightList[$winnerObj->getID()]);
|
||||
|
||||
$givenUniqueRNG = new RandUtil(new LiteHashDRBG(Util::simpleSerialize(
|
||||
UniqueConst::$hiddenSeed,
|
||||
'givenUnique',
|
||||
$year,
|
||||
$month,
|
||||
$nationID,
|
||||
$winnerObj->getID(),
|
||||
)));
|
||||
giveRandomUniqueItem($givenUniqueRNG, $winnerObj, '작위보상');
|
||||
$winnerObj->applyDB($db);
|
||||
}
|
||||
|
||||
if ($chiefID) {
|
||||
$chiefObj = General::createGeneralObjFromDB($chiefID, ['belong', 'npc', 'aux'], 2);
|
||||
$chiefObj->increaseInheritancePoint(InheritanceKey::unifier, 250 * $levelDiff);
|
||||
$chiefObj->applyDB($db);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -486,6 +486,7 @@ class GameConstBase
|
||||
[
|
||||
"month", 1000,
|
||||
true,
|
||||
["UpdateNationLevel"],
|
||||
["ProvideNPCTroopLeader"]
|
||||
],
|
||||
[
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
|
||||
@include media-1000px{
|
||||
#container {
|
||||
#app {
|
||||
width: 1000px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
@@ -28,7 +28,7 @@
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
#container {
|
||||
#app {
|
||||
width: 500px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@@ -72,14 +72,9 @@
|
||||
</BContainer>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
declare const queryValues: {
|
||||
generalID: number | null;
|
||||
};
|
||||
</script>
|
||||
<script lang="ts" setup>
|
||||
import { BContainer, useToast, BFormSelect, BFormSelectOption, BButton } from "bootstrap-vue-next";
|
||||
import { onMounted, provide, ref, watch } from "vue";
|
||||
import { onMounted, provide, ref, toRef, watch } from "vue";
|
||||
import { getGameConstStore, type GameConstStore } from "./GameConstStore";
|
||||
import TopBackBar from "@/components/TopBackBar.vue";
|
||||
import BottomBar from "@/components/BottomBar.vue";
|
||||
@@ -98,6 +93,10 @@ import { parseTime } from "./util/parseTime";
|
||||
|
||||
const toasts = unwrap(useToast());
|
||||
|
||||
const props = defineProps<{
|
||||
generalID?: number;
|
||||
}>();
|
||||
|
||||
const generalList = ref(new Map<number, GeneralListItemP1>());
|
||||
const textMap = {
|
||||
recent_war: [
|
||||
@@ -118,7 +117,10 @@ const orderedInvGeneralKeyIndex = ref(new Map<number, number>());
|
||||
|
||||
const orderBy = ref<keyof typeof textMap>("turntime");
|
||||
const targetGeneral = ref<GeneralListItemP1>();
|
||||
const targetGeneralID = ref(queryValues.generalID ?? undefined);
|
||||
const targetGeneralID = ref(props.generalID);
|
||||
watch(()=>props.generalID, (generalID)=>{
|
||||
targetGeneralID.value = generalID;
|
||||
})
|
||||
|
||||
type GeneralLogs = {
|
||||
[key in GeneralLogType]: Map<number, string>;
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<div>
|
||||
<div>도시 정보</div>
|
||||
<div>{{cityID}}</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
</script>
|
||||
<script lang="ts" setup>
|
||||
import { toRef } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
cityID?: number
|
||||
}>()
|
||||
const cityID = toRef(props, 'cityID');
|
||||
</script>
|
||||
@@ -447,19 +447,19 @@ watch(refreshCounter, async () => {
|
||||
title: "갱신 완료",
|
||||
body: `동향 변경이 있습니다.`,
|
||||
},
|
||||
{ delay: 1000 * 3 }
|
||||
{ delay: 100 * 15 }
|
||||
);
|
||||
} else {
|
||||
toasts.success(
|
||||
{
|
||||
title: "갱신 완료",
|
||||
},
|
||||
{ delay: 1000 * 3 }
|
||||
{ delay: 100 * 5 }
|
||||
);
|
||||
}
|
||||
|
||||
const lastVoteID = response.global.lastVoteID;
|
||||
if (lastVoteID > lastVoteState.value) {
|
||||
if (lastVoteID > lastVoteState.value && lastVoteID > (response.aux.myLastVote ?? 0)) {
|
||||
lastVoteState.value = lastVoteID;
|
||||
toasts.warning(
|
||||
{
|
||||
|
||||
+10
-6
@@ -29,7 +29,7 @@ import type {
|
||||
GetFrontInfoResponse,
|
||||
GetHistoryResponse,
|
||||
GetMenuResponse,
|
||||
GetRecentRecordResponse,
|
||||
GetRecentRecordResponse,
|
||||
} from "./defs/API/Global";
|
||||
import type { CachedMapResult, CommandTableResponse, GeneralListResponse, ItemTypeKey, MapResult } from "./defs";
|
||||
import type { VoteDetailResult, VoteListResult } from "./defs/API/Vote";
|
||||
@@ -71,12 +71,12 @@ const apiRealPath = {
|
||||
amount: number;
|
||||
}>,
|
||||
GetUniqueItemAuctionDetail: GET as APICallT<{
|
||||
auctionID: number;
|
||||
auctionID: number;
|
||||
}, UniqueItemAuctionDetail>,
|
||||
GetUniqueItemAuctionList: GET as APICallT<undefined, UniqueItemAuctionList>,
|
||||
OpenUniqueAuction: POST as APICallT<{
|
||||
itemID: string,
|
||||
amount: number,
|
||||
itemID: string,
|
||||
amount: number,
|
||||
}, OpenAuctionResponse>,
|
||||
},
|
||||
Betting: {
|
||||
@@ -199,7 +199,11 @@ const apiRealPath = {
|
||||
SendMessage: POST as APICallT<{
|
||||
mailbox: number;
|
||||
text: string;
|
||||
}, ValidResponse & {msgID: number, msgType: MsgType}>
|
||||
}, ValidResponse & { msgID: number, msgType: MsgType }>,
|
||||
ReadLatestMessage: PATCH as APICallT<{
|
||||
type: 'diplomacy' | 'private';
|
||||
msgID: number;
|
||||
}>
|
||||
},
|
||||
Misc: {
|
||||
UploadImage: POST as APICallT<
|
||||
@@ -273,7 +277,7 @@ const apiRealPath = {
|
||||
troopID: number;
|
||||
troopName: string;
|
||||
}>,
|
||||
GetNationInfo: GET as APICallT<{full?: boolean}, NationInfoResponse>,
|
||||
GetNationInfo: GET as APICallT<{ full?: boolean }, NationInfoResponse>,
|
||||
},
|
||||
Troop: {
|
||||
NewTroop: POST as APICallT<{
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"v_board": "v_board.ts",
|
||||
"v_cachedMap": "v_cachedMap",
|
||||
"v_chiefCenter": "v_chiefCenter.ts",
|
||||
"v_cityInfo": "v_cityInfo.ts",
|
||||
"v_NPCControl": "v_NPCControl.ts",
|
||||
"v_join": "v_join.ts",
|
||||
"v_main": "v_main.ts",
|
||||
|
||||
@@ -255,21 +255,8 @@ const messageIndexedList: Record<MsgType, Ref<MsgItem[]>> = {
|
||||
diplomacy: messageDiplomacy,
|
||||
};
|
||||
|
||||
function generateLatestMsgState(msgType: MsgType) {
|
||||
const storageKey = `state.${serverID}.latestReadMsg.${msgType}`;
|
||||
const latestReadMsgID = parseInt(localStorage.getItem(storageKey) ?? "0");
|
||||
const obj = ref<[string | undefined, number, number]>([undefined, latestReadMsgID, latestReadMsgID]);
|
||||
watch(obj, ([, newMsgID], [, oldMsgID]) => {
|
||||
if (newMsgID == oldMsgID) {
|
||||
return;
|
||||
}
|
||||
localStorage.setItem(storageKey, newMsgID.toString());
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
|
||||
const latestPrivateMsgToastInfo = generateLatestMsgState("private");
|
||||
const latestDiplomacyMsgToastInfo = generateLatestMsgState("diplomacy");
|
||||
const latestPrivateMsgToastInfo = ref<[string | undefined, number, number]>([undefined, 0, 0]);
|
||||
const latestDiplomacyMsgToastInfo = ref<[string | undefined, number, number]>([undefined, 0, 0]);
|
||||
|
||||
function readLatestMsg(msgType: MsgType) {
|
||||
const targetMap = {
|
||||
@@ -288,6 +275,13 @@ function readLatestMsg(msgType: MsgType) {
|
||||
if (toastID) {
|
||||
toasts.remove(toastID);
|
||||
}
|
||||
|
||||
if(msgType == "private" || msgType == "diplomacy"){
|
||||
void SammoAPI.Message.ReadLatestMessage({
|
||||
type: msgType,
|
||||
msgID: lastestReceivedID,
|
||||
});
|
||||
}
|
||||
target.value = [undefined, lastestReceivedID, lastestReceivedID];
|
||||
}
|
||||
|
||||
@@ -403,6 +397,27 @@ function updateMsgResponse(response: MsgResponse) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(response.latestRead){
|
||||
const newDiplomacyIdx = response.latestRead.diplomacy;
|
||||
const [diplomacyToastID, diplomacyLatestMsgID] = latestDiplomacyMsgToastInfo.value;
|
||||
if(diplomacyLatestMsgID < newDiplomacyIdx){
|
||||
if(diplomacyToastID){
|
||||
toasts.remove(diplomacyToastID);
|
||||
}
|
||||
latestDiplomacyMsgToastInfo.value = [undefined, newDiplomacyIdx, newDiplomacyIdx];
|
||||
}
|
||||
|
||||
const newPrivateIdx = response.latestRead.private;
|
||||
const [privateToastID, privateLatestMsgID] = latestPrivateMsgToastInfo.value;
|
||||
if(privateLatestMsgID < newPrivateIdx){
|
||||
if(privateToastID){
|
||||
toasts.remove(privateToastID);
|
||||
}
|
||||
latestPrivateMsgToastInfo.value = [undefined, newPrivateIdx, newPrivateIdx];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
lastSequence.value = Math.max(lastSequence.value, response.sequence);
|
||||
|
||||
for (const msgType of Object.keys(messageIndexedList) as (keyof typeof messageIndexedList)[]) {
|
||||
|
||||
@@ -221,6 +221,9 @@ export type GetFrontInfoResponse = {
|
||||
} | null
|
||||
>;
|
||||
};
|
||||
aux: {
|
||||
myLastVote?: number;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -58,6 +58,10 @@ export type MsgResponse = {
|
||||
nationID: number;
|
||||
generalName: string;
|
||||
sequence: number;
|
||||
latestRead?: {
|
||||
private: number;
|
||||
diplomacy: number;
|
||||
}
|
||||
};
|
||||
|
||||
export type MailboxItem = {
|
||||
|
||||
@@ -40,6 +40,7 @@ export type GeneralListItemP0 = {
|
||||
|
||||
autorun_limit: number;
|
||||
|
||||
nationID?: number;
|
||||
city: number;
|
||||
troop: number;
|
||||
};
|
||||
|
||||
@@ -10,11 +10,13 @@ import { insertCustomCSS } from "./util/customCSS";
|
||||
import { installVue3Components } from "./util/installVue3Components";
|
||||
|
||||
|
||||
|
||||
declare const query:{
|
||||
generalID?: number;
|
||||
};
|
||||
|
||||
auto500px();
|
||||
|
||||
htmlReady(() => {
|
||||
insertCustomCSS();
|
||||
});
|
||||
installVue3Components(createApp(PageBattleCenter)).mount('#app');
|
||||
installVue3Components(createApp(PageBattleCenter, query)).mount('#app');
|
||||
@@ -1,7 +1,6 @@
|
||||
import "@scss/board.scss";
|
||||
import { createApp } from 'vue'
|
||||
import PageBoard from '@/PageBoard.vue';
|
||||
import BootstrapVueNext from 'bootstrap-vue-next'
|
||||
import { setAxiosXMLHttpRequest } from '@util/setAxiosXMLHttpRequest';
|
||||
import { auto500px } from "./util/auto500px";
|
||||
import { insertCustomCSS } from "./util/customCSS";
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { createApp } from 'vue'
|
||||
import { auto500px } from './util/auto500px';
|
||||
import { htmlReady } from './util/htmlReady';
|
||||
import { insertCustomCSS } from './util/customCSS';
|
||||
import PageCityInfo from './PageCityInfo.vue';
|
||||
import { installVue3Components } from './util/installVue3Components';
|
||||
auto500px();
|
||||
|
||||
declare const query: {
|
||||
cityID?: number
|
||||
};
|
||||
|
||||
htmlReady(() => {
|
||||
insertCustomCSS();
|
||||
});
|
||||
installVue3Components(createApp(PageCityInfo, query)).mount('#app');
|
||||
@@ -1,8 +1,6 @@
|
||||
import '@scss/processing.scss';
|
||||
|
||||
import { unwrap } from "@util/unwrap";
|
||||
import BootstrapVueNext from 'bootstrap-vue-next'
|
||||
import Multiselect from 'vue-multiselect';
|
||||
import { commandMap as GeneralActions } from "@/processing/General";
|
||||
import { commandMap as NationActions } from '@/processing/Nation';
|
||||
import { type App, createApp } from 'vue';
|
||||
@@ -14,6 +12,7 @@ import { StoredActionsHelper } from './util/StoredActionsHelper';
|
||||
import type { ReserveCommandResponse } from './defs/API/Command';
|
||||
import { htmlReady } from './util/htmlReady';
|
||||
import { insertCustomCSS } from './util/customCSS';
|
||||
import { installVue3Components } from './util/installVue3Components';
|
||||
|
||||
declare const staticValues: {
|
||||
serverNick: string,
|
||||
@@ -100,13 +99,13 @@ if (app === undefined) {
|
||||
console.error(`모듈이 지정되지 않음`, entryInfo);
|
||||
}
|
||||
else {
|
||||
const div = unwrap(document.querySelector('#container'));
|
||||
const div = unwrap(document.querySelector('#app'));
|
||||
div.addEventListener('customSubmit', (e: Event) => {
|
||||
const { detail } = e as unknown as CustomEvent<Args>;
|
||||
void submitCommand(entryInfo[0] == 'Nation', turnList, entryInfo[1], detail);
|
||||
}, true);
|
||||
|
||||
app.use(BootstrapVueNext).component('v-multiselect', Multiselect).mount('#container');
|
||||
installVue3Components(app).mount('#app');
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ $gen = Util::getReq('gen', 'int');
|
||||
'mapName' => GameConst::$mapName,
|
||||
'unitSet' => GameConst::$unitSet,
|
||||
],
|
||||
'queryValues' => [
|
||||
'query' => [
|
||||
'generalID' => $gen,
|
||||
]
|
||||
], false) ?>
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace sammo;
|
||||
|
||||
include "lib.php";
|
||||
include "func.php";
|
||||
|
||||
$session = Session::requireGameLogin()->setReadOnly();
|
||||
|
||||
$cityID = Util::getReq('cityID', 'int');
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="color-scheme" content="dark">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=500" />
|
||||
<title><?= UniqueConst::$serverName ?>: 도시 정보</title>
|
||||
<?= WebUtil::printStaticValues([
|
||||
'staticValues' => [
|
||||
'serverName' => UniqueConst::$serverName,
|
||||
'serverNick' => DB::prefix(),
|
||||
'serverID' => UniqueConst::$serverID,
|
||||
'mapName' => GameConst::$mapName,
|
||||
'unitSet' => GameConst::$unitSet,
|
||||
],
|
||||
'query' => [
|
||||
'cityID' => $cityID,
|
||||
]
|
||||
], false) ?>
|
||||
<?= WebUtil::printJS('../d_shared/common_path.js') ?>
|
||||
<?= WebUtil::printCSS('../d_shared/common.css') ?>
|
||||
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/static/pretendard.css" />
|
||||
<?= WebUtil::printDist('vue', 'v_cityInfo', true) ?>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -101,7 +101,7 @@ if (!$commandObj->hasPermissionToReserve()) {
|
||||
</head>
|
||||
|
||||
<body class="img_back">
|
||||
<div id="container"></div>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -137,8 +137,8 @@ foreach (ServConfig::getServerList() as $setting) {
|
||||
</div>
|
||||
|
||||
<div id="bottom_box">
|
||||
<div class="container"><a href="terms.2.html">개인정보처리방침</a> & <a href="terms.1.html">이용약관</a><br>© 2022 • HideD
|
||||
<br>크롬과 파이어폭스에 최적화되어있습니다.
|
||||
<div class="container"><a href="terms.2.html">개인정보처리방침</a> & <a href="terms.1.html">이용약관</a><br>© 2023 • HideD
|
||||
<br>크롬, 엣지, 파이어폭스에 최적화되어있습니다.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user