Compare commits

..
Author SHA1 Message Date
Hide_D 41ec80c96b fix: complete logical clock wall-time isolation 2026-08-03 16:09:35 +00:00
Hide_D ed3c84aa8e feat: replace game schedules with logical ticks 2026-08-03 05:13:47 +00:00
Hide_D f4a1e2258d merge: preserve S100 generated NPC stat floor 2026-07-31 05:06:38 +00:00
Hide_D 4ffd7b5bc0 fix: preserve S100 generated NPC stat floor 2026-07-31 05:06:15 +00:00
Hide_D e7c773709d merge: apply S100 cooldown and growth fixes 2026-07-31 01:24:41 +00:00
Hide_D 1e30659bdd test: verify S100 final-growth reselection 2026-07-31 01:19:23 +00:00
Hide_D 162f198f08 fix: stabilize S100 cooldowns and generated NPC growth 2026-07-31 01:11:47 +00:00
Hide_D 4aa09a294e perf: reduce MariaDB turn queue round trips 2026-07-30 17:12:22 +00:00
Hide_D 191abf83c0 test: isolate S100 inheritance browser logs 2026-07-29 08:51:51 +00:00
Hide_D edfeae5f0f fix: block S100 inheritance stat resets 2026-07-29 08:42:38 +00:00
Hide_D 424184a51a fix: define mixed S100 dex conversion ownership 2026-07-29 07:29:43 +00:00
Hide_D 03a321f95d fix: prevent S100 dex conversion refills 2026-07-29 06:36:33 +00:00
Hide_D e3615fa6bb fix: cap S100 NPC dex growth at forty percent 2026-07-29 05:20:53 +00:00
Hide_D 32991f736d fix: expose S100 event traits to frontend 2026-07-29 05:11:19 +00:00
Hide_D 8b2748d3d4 feat: 재선택 현재 능력치 기준 표시 2026-07-28 16:56:02 +00:00
Hide_D 883080e275 feat: 100기 NPC 동조 및 후보 추첨 기능 개선과 초기 능력치 계산 로직 추가 2026-07-28 16:28:13 +00:00
Hide_D ce807e2564 유니크 획득 확률 조금 더 증가 2026-07-28 16:11:24 +00:00
Hide_D 9a2ac82373 시나리오 문제점 수정 2026-07-28 16:07:21 +00:00
Hide_D 3c35539c16 CreateManyNPC 추가 2026-07-28 16:04:50 +00:00
Hide_D 5ab08403c6 feat: 100기 NPC 동조와 후보 추첨 보완 2026-07-28 16:02:14 +00:00
Hide_D c1ce478d22 feat: exclude prior event seasons from all-star pool 2026-07-28 00:43:46 +00:00
Hide_D d8bf8ba922 feat: expand 100th-season pool through phase 99 2026-07-28 00:29:25 +00:00
Hide_D 95f66e4c39 feat: add 100th-season all-star growth event 2026-07-25 10:46:23 +00:00
Hide_D 4de7ebec17 요양 깃수 추가 2026-03-04 14:16:08 +00:00
Hide_D d43dff35fb fix: 디버그 코드 복구 2026-02-18 18:54:46 +00:00
Hide_D 5b13f8bd30 ip 기록 2026-02-17 22:31:08 +09:00
Hide_D e51bc6f711 fix: 턴 데몬에 cookie jar 적용 2026-02-17 20:35:15 +09:00
Hide_D 1b08aa0c58 wip: 디버그 코드 추가 2026-02-17 19:12:28 +09:00
Hide_D f2547fe207 dbg 2026-02-17 18:32:04 +09:00
류화영 cec7726731 fix: 방랑군이 빈번하게 해산하는 문제 해결 시도 2026-01-19 22:50:11 +09:00
Hide_D d679a6fed8 fix: InheritancePointManager 버그 수정 2025-11-01 17:38:02 +00:00
Hide_D 6fe156eb7c fix: composer uninstall 누락 2025-10-29 13:00:46 +00:00
Hide_D 663ddbe76a fix: inheritance_result 작성 문제 2025-10-28 20:24:25 +00:00
Hide_D 30297454f7 refac: inheritance_result로 데이터 이관 2025-10-28 20:15:05 +00:00
Hide_D bc602722bf feat: schema에 inheritance_result 추가
- storage에 의미없이 쌓이는 데이터 이관
2025-10-28 20:09:30 +00:00
Hide_D be8f147216 dep: brandonwamboldt/utilphp 제거 2025-10-28 19:56:02 +00:00
Hide_D b9495fb64a refac: 턴 데몬 전체를 async로 변경경 2025-10-07 20:05:55 +09:00
Hide_D 07b217b290 fix: 서버 오픈 핸들러를 00초에 맞추도록 변경경 2025-10-07 19:54:24 +09:00
Hide_D 42fac46062 refac: brandonwamboldt/utilphp 의존 코드 수정
- 실제 의존성 제거는 이후에에
2025-10-07 19:26:22 +09:00
Hide_D 24bc8985d0 __filename 2025-10-04 11:39:35 +00:00
Hide_D 76a2d5ff63 daemon typescript 버전 추가 2025-10-04 11:29:03 +00:00
Hide_D 148f32f78b fix: execute engine updated 2025-10-04 11:04:36 +00:00
Hide_D 62f5b6597e proc.php 일부 수정
- api.php 미사용 경로 대비
2025-10-04 10:59:56 +00:00
Hide_D 7e810b0279 LogText 끄기 2025-09-07 06:01:04 +00:00
Hide_D 2336b29b65 fix: ?? 2025-08-30 06:15:25 +00:00
Hide_D 185c0e16a0 fix: AI 일부 key 접근 문제 수정 2025-08-30 06:11:37 +00:00
Hide_D 656baeb9ee fix: 토너먼트 분노 공격 확률 오류 수정 (#263)
Reviewed-on: https://storage.hided.net/gitea/devsam/core/pulls/263
2025-08-23 10:08:13 +09:00
류화영 3e06304dc7 fix: 토너먼트 분노 공격 확률 오류 수정 2025-08-23 09:23:00 +09:00
Hide_D 4bb940c8c5 fix: ?? 연산자 우선순위 2025-08-20 12:59:40 +00:00
Hide_D 2b376b6aab fix: logError 버그 수정 2025-07-06 06:15:18 +00:00
Hide_D 04df26ff83 misc: 군웅할거 손견 국명 손책으로 변경
- 서버 시작 시점에서 손견은 맞으나, 땅따 시작점에는 손책임
2025-06-28 12:37:16 +00:00
Hide_D aca4240b92 game: 강서유서 월드로 명칭 변경 2025-06-26 18:37:42 +00:00
Hide_D faf26fa8a2 game: 강서유서 소설 월드(비세력) 수정 2025-06-26 18:36:19 +00:00
Hide_D 030ab1e7ea game: 군웅축록 연도 수정 2025-06-26 18:25:14 +00:00
Hide_D 7f2c1d4782 game: 역사모드4-2 황제 원술
- 제작자: 강서유서
- 유비가 서주를 가지고 있어야 원술을 치지 않나?
2025-06-26 18:23:36 +00:00
Hide_D 9132138a99 game: 군웅축록 외교 2025-06-26 17:54:32 +00:00
Hide_D 84875d60bd game: 역사모드2-2 반동탁연합 결성(정사)
- 제작자: 강서유서
2025-06-26 17:51:39 +00:00
Hide_D 3cbaf76626 game: 역사모드3-2 군웅축록
- 제작자: 강서유서
2025-06-26 17:26:26 +00:00
Hide_D f4846d0d66 game: 무한대흥 NPC 능력치 감소 2025-06-25 15:07:52 +00:00
Hide_D f183dcc72b frontend: 기술 제한 표기 수정 2025-06-25 14:16:19 +00:00
Hide_D 63cc5d8e88 game: 무한대흥 초기 성수 3천 2025-06-25 13:58:47 +00:00
Hide_D f858c32fdf feat: 점령 후 일반 도시 성벽 수치 조정 가능 2025-06-25 13:58:29 +00:00
Hide_D 60eb127d71 game: 천지비급 상인 등장 2025-06-25 13:54:47 +00:00
Hide_D ede33f3a37 game: ban 메시지 빈도 감소 2025-06-25 13:53:36 +00:00
Hide_D ffd53a2390 game: 무한대흥 스탯 2025-06-25 13:20:21 +00:00
Hide_D 2591fbde67 game: 무한대흥 시나리오 2025-06-25 13:12:41 +00:00
Hide_D 922d3f1938 feat: 기술 등급 제한 수치 유연화 2025-06-25 13:12:32 +00:00
Hide_D 64522ba8a6 fix: ChangeCity에서 _max 키 입력 가능하게 2025-06-25 13:09:24 +00:00
Hide_D 7b1eedb574 feat: ChangeCity에 최대 내정 수치도 연산자로 바꿀 수 있도록 2025-06-25 12:31:29 +00:00
Hide_D 7ec6ca605f run_daemon timeout 2025-05-28 13:55:30 +00:00
Hide_D 5a5c613814 game: 랜임 확률 보정 완화
- 강국의 디버프 감소소
2025-03-05 17:35:08 +00:00
Hide_D 1a3ef20a35 fix: 다병종 밸런스 누락분 수정정 2025-02-08 13:54:01 +00:00
Hide_D fba966177d fix: 고급모드 연구턴 커맨드 창 넘치는 문제 2025-01-25 19:52:28 +00:00
Hide_D 62c72dfdbe fix: 척사 버그 수정 2025-01-24 15:26:56 +09:00
Hide_D 1ae1ff9b72 refac,fix: reqTech, reqRegions, reqCities 간이 호출 추가 2025-01-24 15:23:13 +09:00
Hide_D c1e12c2737 fix: 버그 수정 2025-01-24 15:19:23 +09:00
Hide_D de09a66d6f fix: 병종 연구 커맨드에서 금쌀 검새 안하는 문제 해결 2025-01-24 10:07:56 +09:00
Hide_D 3c2012da46 fix: 사령턴에서 불가능한 턴이 별도 강조가 없는 문제 수정 2025-01-23 17:32:36 +00:00
Hide_D d55be54320 fix: 연구커맨드 검사 조건 2025-01-23 17:27:20 +00:00
Hide_D dcbefa1a96 fix: 연구 커맨드에 금쌀 표기 없음 2025-01-23 17:20:10 +00:00
Hide_D be8f1fff96 fix: 버그 수정 2025-01-23 17:17:37 +00:00
Hide_D 6117dbdcc0 fix: 이벤트 다병종 화시병 위치 2025-01-23 17:14:35 +00:00
Hide_D 0a39ba1023 fix: NPC AI가 현재 병종 기술등급을 고려하지 않는 문제 수정
- 아직 사용하지 않는 징병 페이지 요구 기술 항목도 수정
2025-01-23 17:13:11 +00:00
Hide_D dae5f65baa fix: 특성초토화시에만 did_특성초토화 증가 2025-01-23 17:08:50 +00:00
Hide_D 080bb30103 fix: 버그수정2 2025-01-22 19:42:43 +00:00
Hide_D 9e647c7ce5 fix: 도시 규모조건 징병 불가 2025-01-22 19:41:20 +00:00
Hide_D 73606b0fb4 feat: 초토화시 내부 카운트 추가 2025-01-22 18:54:23 +00:00
Hide_D e62eb36cc2 feat: event 연구 커맨드 추가 2025-01-22 18:53:08 +00:00
Hide_D 6b81f744d4 feat: event용 연구 추가 2025-01-22 18:41:24 +00:00
Hide_D 1a9b3b78cb fix: 화륜차 조건 추가 2025-01-22 18:40:59 +00:00
Hide_D c3cbf956d0 fix: 병종 조건에서 같은 키가 여러개인 경우 대비 2025-01-22 18:40:49 +00:00
Hide_D 91188c555d fix: 커맨드의 aux값값 2025-01-22 18:16:37 +00:00
Hide_D 913028dd6c fix: 병종 특성관련 발생 버그 수정 2025-01-22 18:08:18 +00:00
Hide_D dfb3f6478d game(wip): 다병종 시나리오에 병종 지정 2025-01-22 17:56:17 +00:00
Hide_D 6afc6221d3 feat: 특정 등급 이상 도시 보유 조건 추가 2025-01-22 17:48:49 +00:00
Hide_D 7b5a02487e misc: ReqCitiesWithCityLevel 설명 보완 2025-01-22 17:36:17 +00:00
Hide_D 533b79762e game(wip): 다병종 깃수 준비 2025-01-22 17:29:58 +00:00
Hide_D 53b7dbfdd0 feat: 병종 징병 조건 추가
- 군주 및 수뇌
- 군주 및 수뇌 아님
- 도시의 규모 요구
- 특정 Nation Aux 값 요구
2025-01-22 17:17:30 +00:00
Hide_D fd14c5403b feat: NationAuxKey Enum 2025-01-22 16:57:23 +00:00
Hide_D dc476e73a1 feat: 병종 검사에서 $nation['aux']를 정식 입력 2025-01-22 16:54:27 +00:00
Hide_D e60150705e refac: Constraint의 generalObj destGeneralObj로 General 객체 입력 2025-01-22 16:53:39 +00:00
Hide_D 2869ba5c42 feat,refac: GameUnitConstraint 개념 추가
- GameUnitDetail의 요구 기술, 지역, 도시, 연도를 GameUnitConstraint로 통합
- GameUnitConstraint는 조건 검사, 설명 제공
- 병종 정의를 GameUnitConstraint 이용 재정의
- test 함수에 General 객체 요구,
- test 함수의 ownCities에 단순 도시 외 도시 정보 추가
- 변경된 값에 맞게 호출 함수 변경
2025-01-22 16:26:15 +00:00
229 changed files with 128314 additions and 1245 deletions
-1
View File
@@ -12,7 +12,6 @@
} }
}, },
"require": { "require": {
"brandonwamboldt/utilphp": "^1.1",
"sergeytsalkov/meekrodb": "^2.5", "sergeytsalkov/meekrodb": "^2.5",
"league/plates": "^3.5", "league/plates": "^3.5",
"vlucas/valitron": "^1.4", "vlucas/valitron": "^1.4",
Generated
+2 -52
View File
@@ -4,58 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "deed9afdc95bc42f84784df67d295223", "content-hash": "7edae9b6be35e66ffd0d180f5fac6679",
"packages": [ "packages": [
{
"name": "brandonwamboldt/utilphp",
"version": "1.1.0",
"source": {
"type": "git",
"url": "https://github.com/brandonwamboldt/utilphp.git",
"reference": "36c32efc4f0679c05163464a550f45c8d83fe683"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/brandonwamboldt/utilphp/zipball/36c32efc4f0679c05163464a550f45c8d83fe683",
"reference": "36c32efc4f0679c05163464a550f45c8d83fe683",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"satooshi/php-coveralls": "dev-master"
},
"type": "library",
"autoload": {
"psr-0": {
"utilphp\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Brandon Wamboldt",
"email": "brandon.wamboldt@gmail.com"
}
],
"description": "util.php is a collection of useful functions and snippets that you need or could use every day, designed to avoid conflicts with existing projects",
"homepage": "https://github.com/brandonwamboldt/utilphp",
"keywords": [
"collection",
"helpers",
"php",
"utility"
],
"support": {
"issues": "https://github.com/brandonwamboldt/utilphp/issues",
"source": "https://github.com/brandonwamboldt/utilphp/tree/master"
},
"time": "2015-02-02T17:56:14+00:00"
},
{ {
"name": "brick/math", "name": "brick/math",
"version": "0.12.1", "version": "0.12.1",
@@ -5721,5 +5671,5 @@
"platform-overrides": { "platform-overrides": {
"php": "8.3" "php": "8.3"
}, },
"plugin-api-version": "2.3.0" "plugin-api-version": "2.6.0"
} }
+4 -1
View File
@@ -99,7 +99,10 @@ function logError(string $err, string $errstr, string $errpath, array $trace)
$err = str_replace(ROOT, '{ROOT}', $err); $err = str_replace(ROOT, '{ROOT}', $err);
$errstr = str_replace(ROOT, '{ROOT}', $errstr); $errstr = str_replace(ROOT, '{ROOT}', $errstr);
$errpath = str_replace(ROOT, '{ROOT}', $errpath); $errpath = str_replace(ROOT, '{ROOT}', $errpath);
$trace = array_map(function (string $text) { $trace = array_map(function (string|array $text) {
if (is_array($text)) {
$text = Json::encode($text);
}
return str_replace(ROOT, '{ROOT}', $text); return str_replace(ROOT, '{ROOT}', $text);
}, $trace); }, $trace);
+5 -2
View File
@@ -13,6 +13,9 @@ $db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env'); $gameStor = KVStorage::getStorage($db, 'game_env');
[$turntime, $tnmt_time] = $gameStor->getValuesAsArray(['turntime','tnmt_time']); [$turntime, $tnmt_time] = $gameStor->getValuesAsArray(['turntime','tnmt_time']);
$clock = GameClock::fromStorage($gameStor);
$turntimeDisplay = $clock->formatTick(Util::toInt($turntime), true);
$tnmtTimeDisplay = $tnmt_time === null ? '-' : $clock->formatTick(Util::toInt($tnmt_time), true);
$plock = $db->queryFirstField('SELECT plock FROM plock WHERE `type` ="GAME"'); $plock = $db->queryFirstField('SELECT plock FROM plock WHERE `type` ="GAME"');
?> ?>
@@ -30,8 +33,8 @@ $plock = $db->queryFirstField('SELECT plock FROM plock WHERE `type` ="GAME"');
</head> </head>
<body> <body>
<form action=_119_b.php method=post> <form action=_119_b.php method=post>
시간조정 : <input type=text size=3 name=minute><input type=submit name=btn value='분당김'><input type=submit name=btn value='분지연'> 최종갱신 : <?=$turntime?><br> 시간조정 : <input type=text size=3 name=minute><input type=submit name=btn value='분당김'><input type=submit name=btn value='분지연'> 최종갱신 : <?=$turntimeDisplay?> (tick <?=$turntime?>)<br>
시간조정 : <input type=text size=3 name=minute2><input type=submit name=btn value='토너분당김'><input type=submit name=btn value='토너분지연'> 토너먼트 : <?=$tnmt_time?><br> 시간조정 : <input type=text size=3 name=minute2><input type=submit name=btn value='토너분당김'><input type=submit name=btn value='토너분지연'> 토너먼트 : <?=$tnmtTimeDisplay?> (tick <?=$tnmt_time?>)<br>
봉급지급 : <input type=submit name=btn value='금지급'><input type=submit name=btn value='쌀지급'><br> 봉급지급 : <input type=submit name=btn value='금지급'><input type=submit name=btn value='쌀지급'><br>
락 풀 기 : <input type=submit name=btn value='락걸기'><input type=submit name=btn value='락풀기'> 현재 : <?=$plock>0?"동결중":"가동중"?><br> 락 풀 기 : <input type=submit name=btn value='락걸기'><input type=submit name=btn value='락풀기'> 현재 : <?=$plock>0?"동결중":"가동중"?><br>
</form> </form>
+9 -36
View File
@@ -38,21 +38,9 @@ switch ($btn) {
usleep(500000); usleep(500000);
} }
$gameStor->cacheValues(['turntime', 'starttime', 'tnmt_time']); $clock = GameClock::fromStorage($gameStor);
$turntime = (new \DateTimeImmutable($gameStor->turntime))->sub(new \DateInterval("PT{$minute}M")); // 스케줄 전체를 벽시계에서 빼지 않고 논리 현재 tick만 앞으로 이동합니다.
$starttime = (new \DateTimeImmutable($gameStor->starttime))->sub(new \DateInterval("PT{$minute}M")); $clock->advance($gameStor, $clock->ticksFromMinutes($minute));
$tnmt_time = (new \DateTimeImmutable($gameStor->tnmt_time))->sub(new \DateInterval("PT{$minute}M"));
$gameStor->turntime = $turntime->format('Y-m-d H:i:s.u');
$gameStor->starttime = $starttime->format('Y-m-d H:i:s');
$gameStor->tnmt_time = $tnmt_time->format('Y-m-d H:i:s');
$db->update('general', [
'turntime' => $db->sqleval('DATE_SUB(turntime, INTERVAL %i MINUTE)', $minute)
], true);
$db->update('ng_auction', [
'close_date' => $db->sqleval('DATE_SUB(close_date, INTERVAL %i MINUTE)', $minute)
], 'finished = 0');
if ($locked) { if ($locked) {
unlock(); unlock();
} }
@@ -66,34 +54,19 @@ switch ($btn) {
} }
usleep(500000); usleep(500000);
} }
$gameStor->cacheValues(['turntime', 'starttime', 'tnmt_time']); $clock = GameClock::fromStorage($gameStor);
$turntime = (new \DateTimeImmutable($gameStor->turntime))->add(new \DateInterval("PT{$minute}M")); $clock->advance($gameStor, -$clock->ticksFromMinutes($minute));
$starttime = (new \DateTimeImmutable($gameStor->starttime))->add(new \DateInterval("PT{$minute}M"));
$tnmt_time = (new \DateTimeImmutable($gameStor->tnmt_time))->add(new \DateInterval("PT{$minute}M"));
$gameStor->turntime = $turntime->format('Y-m-d H:i:s.u');
$gameStor->starttime = $starttime->format('Y-m-d H:i:s');
$gameStor->tnmt_time = $tnmt_time->format('Y-m-d H:i:s');
$db->update('general', [
'turntime' => $db->sqleval('DATE_ADD(turntime, INTERVAL %i MINUTE)', $minute)
], true);
$db->update('ng_auction', [
'close_date' => $db->sqleval('DATE_ADD(close_date, INTERVAL %i MINUTE)', $minute)
], 'finished = 0');
if ($locked) { if ($locked) {
unlock(); unlock();
} }
break; break;
case "토너분당김": case "토너분당김":
$tnmt_time = new \DateTime($gameStor->tnmt_time); $clock = GameClock::fromStorage($gameStor);
$tnmt_time->sub(new \DateInterval("PT{$minute2}M")); $gameStor->tnmt_time = Util::toInt($gameStor->tnmt_time) - $clock->ticksFromMinutes($minute2);
$gameStor->tnmt_time = $tnmt_time->format('Y-m-d H:i:s');
break; break;
case "토너분지연": case "토너분지연":
$tnmt_time = new \DateTimeImmutable($gameStor->tnmt_time); $clock = GameClock::fromStorage($gameStor);
$tnmt_time->add(new \DateInterval("PT{$minute2}M")); $gameStor->tnmt_time = Util::toInt($gameStor->tnmt_time) + $clock->ticksFromMinutes($minute2);
$gameStor->tnmt_time = $tnmt_time->format('Y-m-d H:i:s');
break; break;
case "금지급": case "금지급":
processGoldIncome(); processGoldIncome();
+2 -2
View File
@@ -40,7 +40,7 @@ $admin = getAdmin();
</tr> </tr>
<tr> <tr>
<td width=110 align=right>시작시간변경</td> <td width=110 align=right>시작시간변경</td>
<td width=285><input type=text size=20 maxlength=20 style=color:white;background-color:black;text-align:right; name=starttime value='<?=substr($admin['starttime'], 0, 19)?>'><input type=submit name=btn value=변경1></td> <td width=285><input type=text size=20 maxlength=20 style=color:white;background-color:black;text-align:right; name=starttime value='<?=substr($admin['starttime_display'], 0, 19)?>'><input type=submit name=btn value=변경1></td>
</tr> </tr>
<tr> <tr>
<td width=110 align=right>최대 장수</td> <td width=110 align=right>최대 장수</td>
@@ -52,7 +52,7 @@ $admin = getAdmin();
<td width=110 align=right>시작 년도</td> <td width=110 align=right>시작 년도</td>
<td width=285><input type=text size=3 maxlength=3 style=color:white;background-color:black;text-align:right; name=startyear value='<?=$admin['startyear']?>'><input type=submit name=btn value=변경4></td> <td width=285><input type=text size=3 maxlength=3 style=color:white;background-color:black;text-align:right; name=startyear value='<?=$admin['startyear']?>'><input type=submit name=btn value=변경4></td>
<td width=110 align=right>최근 갱신 시간</td> <td width=110 align=right>최근 갱신 시간</td>
<td width=285>&nbsp;<?=$admin['turntime']?></td> <td width=285>&nbsp;<?=$admin['turntime_display']?> (tick <?=$admin['turntime']?>)</td>
</tr> </tr>
<tr> <tr>
<td width=110 align=right>턴시간</td> <td width=110 align=right>턴시간</td>
+9 -3
View File
@@ -25,13 +25,15 @@ if (!$v->validate()) {
$msg = Util::getPost('msg'); $msg = Util::getPost('msg');
$btn = Util::getPost('btn'); $btn = Util::getPost('btn');
$log = Util::getPost('log'); $log = Util::getPost('log');
$starttime = Util::getPost('starttime', 'string', (new \DateTime())->format('Y-m-d H:i:s')); $starttime = Util::getPost('starttime', 'string', null);
$maxgeneral = Util::getPost('maxgeneral', 'int', GameConst::$defaultMaxGeneral); $maxgeneral = Util::getPost('maxgeneral', 'int', GameConst::$defaultMaxGeneral);
$maxnation = Util::getPost('maxnation', 'int', GameConst::$defaultMaxNation); $maxnation = Util::getPost('maxnation', 'int', GameConst::$defaultMaxNation);
$startyear = Util::getPost('startyear', 'int', GameConst::$defaultStartYear); $startyear = Util::getPost('startyear', 'int', GameConst::$defaultStartYear);
$db = DB::db(); $db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env'); $gameStor = KVStorage::getStorage($db, 'game_env');
$clock = GameClock::fromStorage($gameStor);
$starttime ??= $clock->formatTick($clock->nowTick());
$admin = getAdmin(); $admin = getAdmin();
@@ -43,7 +45,11 @@ switch ($btn) {
pushGlobalHistoryLog(["<R>★</><S>{$log}</>"]); pushGlobalHistoryLog(["<R>★</><S>{$log}</>"]);
break; break;
case "변경1": case "변경1":
$gameStor->starttime = (new \DateTime($starttime))->format('Y-m-d H:i:s'); $gameStor->clock_base_time = TimeUtil::format(GameClock::baseTimeForProjection(
new \DateTimeImmutable($starttime),
Util::toInt($gameStor->starttime),
$clock->getTurnTermMinutes(),
), true);
break; break;
case "변경2": case "변경2":
$gameStor->maxgeneral = $maxgeneral; $gameStor->maxgeneral = $maxgeneral;
@@ -78,4 +84,4 @@ switch ($btn) {
break; break;
} }
header('location:_admin1.php'); header('location:_admin1.php');
+9 -9
View File
@@ -99,7 +99,7 @@ switch ($btn) {
], '`no` IN %li', $genlist); ], '`no` IN %li', $genlist);
break; break;
case "강제 사망": case "강제 사망":
$date = TimeUtil::now(true); $date = GameClock::fromStorage($gameStor)->nowTick();
$db->update('general', [ $db->update('general', [
'killturn' => 0, 'killturn' => 0,
'turntime' => $date, 'turntime' => $date,
@@ -113,7 +113,7 @@ switch ($btn) {
case "경험치1000": case "경험치1000":
$text = $btn . " 지급!"; $text = $btn . " 지급!";
foreach ($genlist as $generalID) { foreach ($genlist as $generalID) {
$msg = new Message(MessageType::private, $src, MessageTarget::buildQuick($generalID), $text, new \DateTime(), new \DateTime('9999-12-31'), []); $msg = new Message(MessageType::private, $src, MessageTarget::buildQuick($generalID), $text, Message::gameNow(), new \DateTime('9999-12-31'), []);
$msg->send(true); $msg->send(true);
} }
$db->update('general', [ $db->update('general', [
@@ -124,7 +124,7 @@ switch ($btn) {
case "공헌치1000": case "공헌치1000":
$text = $btn . " 지급!"; $text = $btn . " 지급!";
foreach ($genlist as $generalID) { foreach ($genlist as $generalID) {
$msg = new Message(MessageType::private, $src, MessageTarget::buildQuick($generalID), $text, new \DateTime(), new \DateTime('9999-12-31'), []); $msg = new Message(MessageType::private, $src, MessageTarget::buildQuick($generalID), $text, Message::gameNow(), new \DateTime('9999-12-31'), []);
$msg->send(true); $msg->send(true);
} }
$db->update('general', [ $db->update('general', [
@@ -135,7 +135,7 @@ switch ($btn) {
case "보숙10000": case "보숙10000":
$text = "보병숙련도+10000 지급!"; $text = "보병숙련도+10000 지급!";
foreach ($genlist as $generalID) { foreach ($genlist as $generalID) {
$msg = new Message(MessageType::private, $src, MessageTarget::buildQuick($generalID), $text, new \DateTime(), new \DateTime('9999-12-31'), []); $msg = new Message(MessageType::private, $src, MessageTarget::buildQuick($generalID), $text, Message::gameNow(), new \DateTime('9999-12-31'), []);
$msg->send(true); $msg->send(true);
} }
$db->update('general', [ $db->update('general', [
@@ -145,7 +145,7 @@ switch ($btn) {
case "궁숙10000": case "궁숙10000":
$text = "궁병숙련도+10000 지급!"; $text = "궁병숙련도+10000 지급!";
foreach ($genlist as $generalID) { foreach ($genlist as $generalID) {
$msg = new Message(MessageType::private, $src, MessageTarget::buildQuick($generalID), $text, new \DateTime(), new \DateTime('9999-12-31'), []); $msg = new Message(MessageType::private, $src, MessageTarget::buildQuick($generalID), $text, Message::gameNow(), new \DateTime('9999-12-31'), []);
$msg->send(true); $msg->send(true);
} }
$db->update('general', [ $db->update('general', [
@@ -156,7 +156,7 @@ switch ($btn) {
$src = MessageTarget::buildQuick($session->generalID); $src = MessageTarget::buildQuick($session->generalID);
$text = "기병숙련도+10000 지급!"; $text = "기병숙련도+10000 지급!";
foreach ($genlist as $generalID) { foreach ($genlist as $generalID) {
$msg = new Message(MessageType::private, $src, MessageTarget::buildQuick($generalID), $text, new \DateTime(), new \DateTime('9999-12-31'), []); $msg = new Message(MessageType::private, $src, MessageTarget::buildQuick($generalID), $text, Message::gameNow(), new \DateTime('9999-12-31'), []);
$msg->send(true); $msg->send(true);
} }
$db->update('general', [ $db->update('general', [
@@ -167,7 +167,7 @@ switch ($btn) {
$src = MessageTarget::buildQuick($session->generalID); $src = MessageTarget::buildQuick($session->generalID);
$text = "귀병숙련도+10000 지급!"; $text = "귀병숙련도+10000 지급!";
foreach ($genlist as $generalID) { foreach ($genlist as $generalID) {
$msg = new Message(MessageType::private, $src, MessageTarget::buildQuick($generalID), $text, new \DateTime(), new \DateTime('9999-12-31'), []); $msg = new Message(MessageType::private, $src, MessageTarget::buildQuick($generalID), $text, Message::gameNow(), new \DateTime('9999-12-31'), []);
$msg->send(true); $msg->send(true);
} }
$db->update('general', [ $db->update('general', [
@@ -178,7 +178,7 @@ switch ($btn) {
$src = MessageTarget::buildQuick($session->generalID); $src = MessageTarget::buildQuick($session->generalID);
$text = "차병숙련도+10000 지급!"; $text = "차병숙련도+10000 지급!";
foreach ($genlist as $generalID) { foreach ($genlist as $generalID) {
$msg = new Message(MessageType::private, $src, MessageTarget::buildQuick($generalID), $text, new \DateTime(), new \DateTime('9999-12-31'), []); $msg = new Message(MessageType::private, $src, MessageTarget::buildQuick($generalID), $text, Message::gameNow(), new \DateTime('9999-12-31'), []);
$msg->send(true); $msg->send(true);
} }
$db->update('general', [ $db->update('general', [
@@ -198,7 +198,7 @@ switch ($btn) {
case "메세지 전달": case "메세지 전달":
$text = $msg ?? ''; $text = $msg ?? '';
foreach ($genlist as $generalID) { foreach ($genlist as $generalID) {
$msg = new Message(MessageType::private, $src, MessageTarget::buildQuick($generalID), $text, new \DateTime(), new \DateTime('9999-12-31'), []); $msg = new Message(MessageType::private, $src, MessageTarget::buildQuick($generalID), $text, Message::gameNow(), new \DateTime('9999-12-31'), []);
$msg->send(true); $msg->send(true);
} }
break; break;
+4 -3
View File
@@ -31,7 +31,7 @@ $queryMap = [
]; ];
if ($reqQueryType === null || !key_exists($reqQueryType, $queryMap)) { if ($reqQueryType === null || !key_exists($reqQueryType, $queryMap)) {
$reqQueryType = Util::array_first_key($queryMap); $reqQueryType = array_key_first($queryMap);
} }
//로그인 검사 //로그인 검사
@@ -42,6 +42,7 @@ if ($session->userGrade < 6) {
} }
$db = DB::db(); $db = DB::db();
$clock = GameClock::fromStorage(KVStorage::getStorage($db, 'game_env'));
if ($btn == '정렬하기') { if ($btn == '정렬하기') {
$gen = 0; $gen = 0;
@@ -111,7 +112,7 @@ $generalObj = General::createObjFromDB($gen, null, GeneralQueryMode::FullWithAcc
대상장수 : 대상장수 :
<select name=gen size=1> <select name=gen size=1>
<?php foreach ($generalBasicList as $general) : ?> <?php foreach ($generalBasicList as $general) : ?>
<option <?= $gen == $general['no'] ? 'selected' : '' ?> value='<?= $general['no'] ?>'><?= $general['name'] ?> (<?= substr($general['turntime'], 14, 5) ?>)</option> <option <?= $gen == $general['no'] ? 'selected' : '' ?> value='<?= $general['no'] ?>'><?= $general['name'] ?> (<?= substr($clock->formatTick(Util::toInt($general['turntime'])), 14, 5) ?>)</option>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
<input type=submit name=btn value='조회하기'> <input type=submit name=btn value='조회하기'>
@@ -179,4 +180,4 @@ $generalObj = General::createObjFromDB($gen, null, GeneralQueryMode::FullWithAcc
</table> </table>
</body> </body>
</html> </html>
+1 -3
View File
@@ -100,8 +100,6 @@ $sel[$type] = "selected";
throw new \Exception("알 수 없는 외교 상태: {$dip['state']}"); throw new \Exception("알 수 없는 외교 상태: {$dip['state']}");
} }
$date = TimeUtil::now();
echo " echo "
<tr> <tr>
<td align=center style=color:" . newColor($nationColor[$me]) . ";background-color:{$nationColor[$me]};>$nationName[$me]</td> <td align=center style=color:" . newColor($nationColor[$me]) . ";background-color:{$nationColor[$me]};>$nationName[$me]</td>
@@ -122,4 +120,4 @@ $sel[$type] = "selected";
</table> </table>
</body> </body>
</html> </html>
+5 -2
View File
@@ -243,11 +243,14 @@ $templates = new \League\Plates\Engine(__DIR__ . '/templates');
"SELECT nation,no,name,owner_name as ownerName, owner, picture, imgsvr, "SELECT nation,no,name,owner_name as ownerName, owner, picture, imgsvr,
experience, dedication, experience, dedication,
dex1, dex2, dex3, dex4, dex5, dex1, dex2, dex3, dex4, dex5,
horse, weapon, book, item horse, weapon, book, item, aux
FROM general WHERE %l", FROM general WHERE %l",
$btn == "NPC 보기" ? "npc>=2" : "npc<2" $btn == "NPC 보기" ? "npc>=2" : "npc<2"
) as $general) { ) as $general) {
$generalID = $general['no']; $generalID = $general['no'];
foreach (['dex1', 'dex2', 'dex3', 'dex4', 'dex5'] as $dexKey) {
$general[$dexKey] = CentennialAllStarGrowthService::recordableRawValue($general, $dexKey);
}
$general['bgColor'] = $nationColor[$general['nation']] ?? GameConst::$basecolor4; $general['bgColor'] = $nationColor[$general['nation']] ?? GameConst::$basecolor4;
$general['fgColor'] = newColor($general['bgColor']); $general['fgColor'] = newColor($general['bgColor']);
$general['nationName'] = $nationName[$general['nation']]; $general['nationName'] = $nationName[$general['nation']];
@@ -427,4 +430,4 @@ $templates = new \League\Plates\Engine(__DIR__ . '/templates');
</div> </div>
</body> </body>
</html> </html>
+2 -1
View File
@@ -9,6 +9,7 @@ include "func.php";
$db = DB::db(); $db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env'); $gameStor = KVStorage::getStorage($db, 'game_env');
$clock = GameClock::fromStorage($gameStor);
increaseRefresh("갱신정보", 1); increaseRefresh("갱신정보", 1);
@@ -23,7 +24,7 @@ $recentTraffic[] = [
'month' => $admin['month'], 'month' => $admin['month'],
'refresh' => $admin['refresh'], 'refresh' => $admin['refresh'],
'online' => $curonline, 'online' => $curonline,
'date' => TimeUtil::now() 'date' => $clock->formatNow()
]; ];
if ($admin['maxrefresh'] == 0) { if ($admin['maxrefresh'] == 0) {
+2 -2
View File
@@ -16,7 +16,7 @@ $db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env'); $gameStor = KVStorage::getStorage($db, 'game_env');
increaseRefresh("현재도시", 1); increaseRefresh("현재도시", 1);
$lastExecute = substr($gameStor->turntime, 5, 14); $lastExecute = substr(GameClock::fromStorage($gameStor)->formatTick(Util::toInt($gameStor->turntime)), 5, 14);
$me = $db->queryFirstRow('SELECT no,nation,officer_level,city from general where owner=%i', $userID); $me = $db->queryFirstRow('SELECT no,nation,officer_level,city from general where owner=%i', $userID);
$myNation = $db->queryFirstRow('SELECT nation,level,spy FROM nation WHERE nation=%i', $me['nation']) ?? [ $myNation = $db->queryFirstRow('SELECT nation,level,spy FROM nation WHERE nation=%i', $me['nation']) ?? [
@@ -537,4 +537,4 @@ $templates = new \League\Plates\Engine('templates');
</table> </table>
</body> </body>
</html> </html>
+3 -1
View File
@@ -129,6 +129,7 @@ $templates = new \League\Plates\Engine('templates');
); );
$generalTurnList = []; $generalTurnList = [];
$clock = GameClock::fromStorage($gameStor);
foreach ($db->queryAllLists( foreach ($db->queryAllLists(
'SELECT general_id, turn_idx, brief FROM general_turn WHERE general_id IN %li AND turn_idx < 5 ORDER BY general_id ASC, turn_idx ASC', 'SELECT general_id, turn_idx, brief FROM general_turn WHERE general_id IN %li AND turn_idx < 5 ORDER BY general_id ASC, turn_idx ASC',
@@ -142,6 +143,7 @@ $templates = new \League\Plates\Engine('templates');
$genCntEff = 0; $genCntEff = 0;
foreach ($generals as &$general) { foreach ($generals as &$general) {
$general['turntime'] = $clock->formatTick(Util::toInt($general['turntime']), true);
$general['cityText'] = CityConst::byID($general['city'])->name; $general['cityText'] = CityConst::byID($general['city'])->name;
$general['troopText'] = $troopName[$general['troop']] ?? '-'; $general['troopText'] = $troopName[$general['troop']] ?? '-';
@@ -284,4 +286,4 @@ $templates = new \League\Plates\Engine('templates');
</table> </table>
</body> </body>
</html> </html>
+30 -12
View File
@@ -19,10 +19,10 @@ $userID = Session::getUserID();
$generalID = $session->generalID; $generalID = $session->generalID;
$db = DB::db(); $db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env'); $gameStor = KVStorage::getStorage($db, 'game_env');
$gameStor->cacheValues(['turntime', 'opentime', 'autorun_user', 'npcmode']); $gameStor->cacheValues(['turntime', 'opentime', 'autorun_user', 'npcmode']);
$clock = GameClock::fromStorage($gameStor);
increaseRefresh("내정보", 1); $nowTick = $clock->nowTick();
$me = General::createObjFromDB($generalID, null, GeneralQueryMode::FullWithAccessLog); $me = General::createObjFromDB($generalID, null, GeneralQueryMode::FullWithAccessLog);
@@ -41,12 +41,30 @@ $lastRefresh = $db->queryFirstField(
$generalID $generalID
); );
$targetTime = addTurn($lastRefresh, $gameStor->turnterm, GameConst::$minTurnDieOnPrestart); $nextChange = $me->getAuxVar('next_change');
if ($gameStor->turntime <= $gameStor->opentime) { if (!is_int($nextChange)) {
//서버 가오픈시 할 수 있는 행동 $nextChange = null;
if ($me->getNPCType() == 0 && $me->getNationID() == 0) { }
$nextChangeDisplay = $nextChange === null ? null : $clock->formatTick($nextChange);
increaseRefresh("내정보", 1);
if ($gameStor->turntime <= $gameStor->opentime) {
$targetTime = $me->getAuxVar('prestart_delete_after');
if (!is_int($targetTime)) {
$targetTime = addTurn(
$lastRefresh === null ? $nowTick : Util::toInt($lastRefresh),
$gameStor->turnterm,
GameConst::$minTurnDieOnPrestart
);
$me->setAuxVar('prestart_delete_after', $targetTime);
$me->applyDB($db);
}
$targetTimeDisplay = $clock->formatTick($targetTime);
//서버 가오픈시 할 수 있는 행동
if ($me->getNPCType() == 0 && $me->getNationID() == 0) {
$showDieOnPrestartBtn = true; $showDieOnPrestartBtn = true;
if ($targetTime <= TimeUtil::now()) { if ($targetTime <= $nowTick) {
$availableDieOnPrestart = true; $availableDieOnPrestart = true;
} }
} }
@@ -159,7 +177,7 @@ $changeDefence999Atmos = $me->onCalcDomestic('changeDefenceTrain', "atmos999", $
<a href="b_myPage.php?detachNPC=1"><button type="button" style=background-color:<?= GameConst::$basecolor2 ?>;color:white;width:160px;height:30px;font-size:14px;>빙의 해체 요청</button></a>--> <a href="b_myPage.php?detachNPC=1"><button type="button" style=background-color:<?= GameConst::$basecolor2 ?>;color:white;width:160px;height:30px;font-size:14px;>빙의 해체 요청</button></a>-->
<?php if ($showDieOnPrestartBtn) : ?> <?php if ($showDieOnPrestartBtn) : ?>
가오픈 기간 내 장수 삭제 (<?= substr($targetTime, 0, 19) ?> 부터)<br> 가오픈 기간 내 장수 삭제 (<?= $targetTimeDisplay ?> 부터)<br>
<button type="button" id='dieOnPrestart' style=background-color:<?= GameConst::$basecolor2 ?>;color:white;width:160px;height:30px;font-size:14px;>장수 삭제</button><br><br> <button type="button" id='dieOnPrestart' style=background-color:<?= GameConst::$basecolor2 ?>;color:white;width:160px;height:30px;font-size:14px;>장수 삭제</button><br><br>
<?php endif; ?> <?php endif; ?>
@@ -174,7 +192,7 @@ $changeDefence999Atmos = $me->onCalcDomestic('changeDefenceTrain', "atmos999", $
<?php endif; ?> <?php endif; ?>
<?php if ($gameStor->npcmode == 2 && $me->getNPCType() == 0) : ?> <?php if ($gameStor->npcmode == 2 && $me->getNPCType() == 0) : ?>
다른 장수 선택 (<?= substr($me->getAuxVar('next_change') ?? TimeUtil::now(), 0, 19) ?> 부터)<br> 다른 장수 선택 (<?= $nextChangeDisplay ?? '지금' ?>부터)<br>
<a href="select_general_from_pool.php" id='select_general_from_pool'><button type="button" style=background-color:<?= GameConst::$basecolor2 ?>;color:white;width:160px;height:30px;font-size:14px;>다른 장수 선택</button></a><br><br> <a href="select_general_from_pool.php" id='select_general_from_pool'><button type="button" style=background-color:<?= GameConst::$basecolor2 ?>;color:white;width:160px;height:30px;font-size:14px;>다른 장수 선택</button></a><br><br>
<?php endif; ?> <?php endif; ?>
@@ -272,4 +290,4 @@ $changeDefence999Atmos = $me->onCalcDomestic('changeDefenceTrain', "atmos999", $
</div> </div>
</body> </body>
</html> </html>
+3 -3
View File
@@ -228,10 +228,10 @@ if($btn == "자동개최설정") {
$phase = 0; $phase = 0;
startBetting($admin['tnmt_type'], 720); startBetting($admin['tnmt_type'], 720);
} elseif($btn == "베팅마감") { } elseif($btn == "베팅마감") {
$dt = date("Y-m-d H:i:s", time() + 60); $clock = GameClock::fromStorage($gameStor);
$gameStor->tournament=7; $gameStor->tournament=7;
$gameStor->phase=0; $gameStor->phase=0;
$gameStor->tnmt_time = $dt; $gameStor->tnmt_time = $clock->nowTick() + $clock->ticksFromSeconds(60);
} elseif($btn == "16강") { } elseif($btn == "16강") {
finalFight($admin['tnmt_type'], $admin['tournament'], $admin['phase'], 16); finalFight($admin['tnmt_type'], $admin['tournament'], $admin['phase'], 16);
} elseif($btn == "8강") { } elseif($btn == "8강") {
@@ -248,4 +248,4 @@ if($btn == "자동개최설정") {
$gameStor->tnmt_msg = $msg; $gameStor->tnmt_msg = $msg;
} }
header('location:b_tournament.php'); header('location:b_tournament.php');
+17 -1
View File
@@ -51,6 +51,22 @@
display: none; display: none;
} }
.picture_choice {
margin: 8px auto;
}
.picture_choice label,
.event_picture label {
display: inline-flex;
align-items: center;
gap: 4px;
margin: 4px 8px;
}
.picture_choice img {
object-fit: cover;
}
.custom_picture { .custom_picture {
display: none; display: none;
} }
@@ -61,4 +77,4 @@
.custom_stat { .custom_stat {
display: none; display: none;
} }
+121 -122
View File
@@ -630,7 +630,11 @@ function generalInfo(General $generalObj)
$injury = "건강"; $injury = "건강";
} }
$remaining = (new \DateTimeImmutable($generalObj->getTurnTime()))->diff(new \DateTimeImmutable())->i; $clock = GameClock::fromStorage(KVStorage::getStorage(DB::db(), 'game_env'));
$remaining = max(0, intdiv(
$generalObj->getTurnTick() - $clock->nowTick(),
$clock->ticksFromMinutes(1),
));
if ($nation['color'] == "") { if ($nation['color'] == "") {
$nation['color'] = "#000000"; $nation['color'] = "#000000";
@@ -921,45 +925,37 @@ function banner()
); );
} }
function addTurn($date, int $turnterm, int $turn = 1, bool $withFraction = true) function addTurn(int $tick, int $turnterm, int $turn = 1, bool $withFraction = true): int
{ {
$date = new \DateTime($date); return $tick + GameClock::TICKS_PER_TURN * $turn;
$target = $turnterm * $turn; }
$date->add(new \DateInterval("PT{$target}M"));
if ($withFraction) { function subTurn(int $tick, int $turnterm, int $turn = 1, bool $withFraction = true): int
return $date->format('Y-m-d H:i:s.u'); {
} return $tick - GameClock::TICKS_PER_TURN * $turn;
return $date->format('Y-m-d H:i:s'); }
}
function cutTurn(int $tick, int $turnterm, bool $withFraction = true): int
function subTurn($date, int $turnterm, int $turn = 1, bool $withFraction = true) {
{ $remainder = $tick % GameClock::TICKS_PER_TURN;
$date = new \DateTime($date); if ($remainder < 0) {
$target = $turnterm * $turn; $remainder += GameClock::TICKS_PER_TURN;
$date->sub(new \DateInterval("PT{$target}M")); }
if ($withFraction) { return $tick - $remainder;
return $date->format('Y-m-d H:i:s.u'); }
}
return $date->format('Y-m-d H:i:s'); /** 시나리오 초기화 입력인 벽시계를 기존 01:00 기준 월 경계로 정렬합니다. */
} function cutTurnDateTime(string $date, int $turnterm, bool $withFraction = true): string
{
function cutTurn($date, int $turnterm, bool $withFraction = true) $dateObj = new \DateTime($date);
{ $baseDate = new \DateTime($dateObj->format('Y-m-d'));
$date = new \DateTime($date); $baseDate->sub(new \DateInterval('P1D'));
$baseDate->add(new \DateInterval('PT1H'));
$baseDate = new \DateTime($date->format('Y-m-d')); $diffMin = intdiv($dateObj->getTimestamp() - $baseDate->getTimestamp(), 60);
$baseDate->sub(new \DateInterval("P1D")); $diffMin -= $diffMin % $turnterm;
$baseDate->add(new \DateInterval("PT1H")); $baseDate->add(new \DateInterval("PT{$diffMin}M"));
return $baseDate->format($withFraction ? 'Y-m-d H:i:s.u' : 'Y-m-d H:i:s');
$diffMin = intdiv($date->getTimeStamp() - $baseDate->getTimeStamp(), 60); }
$diffMin -= $diffMin % $turnterm;
$baseDate->add(new \DateInterval("PT{$diffMin}M"));
if ($withFraction) {
return $baseDate->format('Y-m-d H:i:s.u');
}
return $baseDate->format('Y-m-d H:i:s');
}
function cutDay($date, int $turnterm, bool $withFraction = true) function cutDay($date, int $turnterm, bool $withFraction = true)
{ {
@@ -1001,11 +997,9 @@ function increaseRefresh($type = "", $cnt = 1)
$generalID = $session->generalID; $generalID = $session->generalID;
$userGrade = $session->userGrade; $userGrade = $session->userGrade;
$dateObj = new \DateTimeImmutable(); $db = DB::db();
$date = TimeUtil::format($dateObj, false); $gameStor = KVStorage::getStorage($db, 'game_env');
$date = GameClock::fromStorage($gameStor)->nowTick();
$db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env');
$isunited = $gameStor->isunited; $isunited = $gameStor->isunited;
$opentime = $gameStor->opentime; $opentime = $gameStor->opentime;
@@ -1047,7 +1041,7 @@ function increaseRefresh($type = "", $cnt = 1)
$logDB = FileDB::db($logPath, $serverPath . '/../f_install/sql/api_log.sql'); $logDB = FileDB::db($logPath, $serverPath . '/../f_install/sql/api_log.sql');
$ip = $_SERVER['REMOTE_ADDR'] ?? 'local'; $ip = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? 'local';
$date = date('Y-m-d H:i:s'); $date = date('Y-m-d H:i:s');
$logDB->insert('api_log', [ $logDB->insert('api_log', [
@@ -1088,12 +1082,12 @@ function updateTraffic()
if(count($recentTraffic) >= 5){ if(count($recentTraffic) >= 5){
array_shift($recentTraffic); array_shift($recentTraffic);
} }
$recentTraffic[] = [ $recentTraffic[] = [
'year'=>$admin['year'], 'year'=>$admin['year'],
'month' => $admin['month'], 'month' => $admin['month'],
'refresh' => $admin['refresh'], 'refresh' => $admin['refresh'],
'online' => $online, 'online' => $online,
'date' => TimeUtil::now(), 'date' => GameClock::fromStorage($gameStor)->formatNow(),
]; ];
$gameStor->recentTraffic = $recentTraffic; $gameStor->recentTraffic = $recentTraffic;
@@ -1145,18 +1139,19 @@ function unlock(): bool
return $db->affectedRows() > 0; return $db->affectedRows() > 0;
} }
function timeover(): bool function timeover(): bool
{ {
$db = DB::db(); $db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env'); $gameStor = KVStorage::getStorage($db, 'game_env');
list($turnterm, $turntime) = $gameStor->getValuesAsArray(['turnterm', 'turntime']); list($turnterm, $turntime) = $gameStor->getValuesAsArray(['turnterm', 'turntime']);
$diff = (new \DateTime())->getTimestamp() - (new \DateTime($turntime))->getTimestamp(); $clock = GameClock::fromStorage($gameStor);
$diff = $clock->nowTick() - Util::toInt($turntime);
$t = min($turnterm, 5); $t = min($turnterm, 5);
$term = $diff; $term = $clock->ticksFromSeconds($t);
if ($term >= $t || $term < 0) { if ($diff >= $term || $diff < 0) {
return true; return true;
} else { } else {
return false; return false;
@@ -1169,9 +1164,8 @@ function checkDelay()
$gameStor = KVStorage::getStorage($db, 'game_env'); $gameStor = KVStorage::getStorage($db, 'game_env');
//서버정보 //서버정보
$now = new \DateTimeImmutable(); $clock = GameClock::fromStorage($gameStor);
$turntime = new \DateTimeImmutable($gameStor->turntime); $timeMinDiff = intdiv($clock->nowTick() - Util::toInt($gameStor->turntime), $clock->ticksFromMinutes(1));
$timeMinDiff = intdiv($now->getTimestamp() - $turntime->getTimestamp(), 60);
// 1턴이상 갱신 없었으면 서버 지연 // 1턴이상 갱신 없었으면 서버 지연
$term = $gameStor->turnterm; $term = $gameStor->turnterm;
@@ -1185,20 +1179,17 @@ function checkDelay()
//지연 해야할 밀린 턴 횟수 //지연 해야할 밀린 턴 횟수
$iter = intdiv($timeMinDiff, $term); $iter = intdiv($timeMinDiff, $term);
if ($iter > $threshold) { if ($iter > $threshold) {
$minute = $iter * $term; $minute = $iter * $term;
$newTurntime = $turntime->add(new \DateInterval("PT{$minute}M")); $delayTick = $clock->ticksFromMinutes($minute);
$newNextTurntime = $turntime->add(new \DateInterval("PT{$term}M")); $gameStor->turntime = Util::toInt($gameStor->turntime) + $delayTick;
$gameStor->turntime = $newTurntime->format('Y-m-d H:i:s'); $gameStor->starttime = Util::toInt($gameStor->starttime) + $delayTick;
$gameStor->starttime = (new \DateTimeImmutable($gameStor->starttime))
->add(new \DateInterval("PT{$minute}M")) $db->update('general', [
->format('Y-m-d H:i:s'); 'turntime' => $db->sqleval('turntime + %i', $delayTick)
], true);
$db->update('general', [ $db->update('ng_auction', [
'turntime' => $db->sqleval('DATE_ADD(turntime, INTERVAL %i MINUTE)', $minute) 'close_tick' => $db->sqleval('close_tick + %i', $delayTick)
], 'turntime<=DATE_ADD(turntime, INTERVAL %i MINUTE)', $term); ], 'finished = 0');
$db->update('ng_auction', [
'close_date' => $db->sqleval('DATE_ADD(close_date, INTERVAL %i MINUTE)', $minute)
], 'finished = 0');
} }
} }
@@ -1247,17 +1238,15 @@ function updateOnline()
$gameStor->online_nation = join(', ', $onlineNation); $gameStor->online_nation = join(', ', $onlineNation);
} }
function turnDate($curtime) function turnDate($curtime)
{ {
$db = DB::db(); $db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env'); $gameStor = KVStorage::getStorage($db, 'game_env');
$admin = $gameStor->getValues(['startyear', 'starttime', 'turnterm', 'year', 'month']); $admin = $gameStor->getValues(['startyear', 'starttime', 'turnterm', 'year', 'month']);
$turn = $admin['starttime']; $turn = $admin['starttime'];
$curturn = cutTurn($curtime, $admin['turnterm']); $curturn = cutTurn(Util::toInt($curtime), $admin['turnterm']);
$term = $admin['turnterm']; $num = intdiv($curturn - Util::toInt($turn), GameClock::TICKS_PER_TURN);
$num = intdiv((strtotime($curturn) - strtotime($turn)), $term * 60);
$date = $admin['startyear'] * 12; $date = $admin['startyear'] * 12;
$date += $num; $date += $num;
@@ -1387,12 +1376,14 @@ function CheckHall($no)
return; return;
} }
$unitedDate = TimeUtil::now(); $clock = GameClock::fromStorage($gameStor);
$unitedDate = $clock->formatTick($clock->nowTick());
$nation = $generalObj->getStaticNation(); $nation = $generalObj->getStaticNation();
$serverCnt = $db->queryFirstField('SELECT count(*) FROM ng_games'); $serverCnt = $db->queryFirstField('SELECT count(*) FROM ng_games');
[$scenarioIdx, $scenarioName, $startTime] = $gameStor->getValuesAsArray(['scenario', 'scenario_text', 'starttime']); [$scenarioIdx, $scenarioName, $startTick] = $gameStor->getValuesAsArray(['scenario', 'scenario_text', 'starttime']);
$startTime = $clock->formatTick(Util::toInt($startTick));
$ownerName = $generalObj->getVar('owner_name'); $ownerName = $generalObj->getVar('owner_name');
if ($generalObj->getVar('owner')) { if ($generalObj->getVar('owner')) {
@@ -1401,8 +1392,11 @@ function CheckHall($no)
foreach ($types as [$typeName, $valueType]) { foreach ($types as [$typeName, $valueType]) {
if ($valueType === 'natural') { if ($valueType === 'natural') {
$value = $generalObj->getVar($typeName); $value = $generalObj->getVar($typeName);
if (in_array($typeName, ['dex1', 'dex2', 'dex3', 'dex4', 'dex5'], true)) {
$value = CentennialAllStarGrowthService::recordableValue($generalObj, $typeName);
}
} else if ($valueType === 'rank') { } else if ($valueType === 'rank') {
$value = $generalObj->getRankVar(RankColumn::from($typeName)); $value = $generalObj->getRankVar(RankColumn::from($typeName));
} else if ($valueType === 'calc') { } else if ($valueType === 'calc') {
@@ -1702,12 +1696,21 @@ function tryUniqueItemLottery(RandUtil $rng, General $general, string $acquireTy
return giveRandomUniqueItem($rng, $general, $acquireType); return giveRandomUniqueItem($rng, $general, $acquireType);
} }
function getAdmin() function getAdmin()
{ {
$db = DB::db(); $db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env'); $gameStor = KVStorage::getStorage($db, 'game_env');
return $gameStor->getAll(); $admin = $gameStor->getAll();
} $clock = GameClock::fromStorage($gameStor);
foreach (['turntime', 'starttime', 'opentime', 'tnmt_time'] as $key) {
if (($admin[$key] ?? null) !== null) {
$admin["{$key}_display"] = $clock->formatTick(Util::toInt($admin[$key]), true);
}
}
$admin['clock_now_tick'] = $clock->nowTick();
$admin['clock_now_display'] = $clock->formatTick($admin['clock_now_tick'], true);
return $admin;
}
/** @return General[] */ /** @return General[] */
function deleteNation(General $lord, bool $applyDB): array function deleteNation(General $lord, bool $applyDB): array
@@ -1785,11 +1788,13 @@ function deleteNation(General $lord, bool $applyDB): array
// 부대 삭제 // 부대 삭제
$db->delete('troop', 'nation=%i', $nationID); $db->delete('troop', 'nation=%i', $nationID);
// 국가 삭제 // 국가 삭제
$db->insert('ng_old_nations', [ $gameDate = GameClock::fromStorage(KVStorage::getStorage($db, 'game_env'))->formatNow();
'server_id' => UniqueConst::$serverID, $db->insert('ng_old_nations', [
'nation' => $nationID, 'server_id' => UniqueConst::$serverID,
'data' => Json::encode($nation) 'nation' => $nationID,
'date' => $gameDate,
'data' => Json::encode($nation)
]); ]);
$db->delete('nation', 'nation=%i', $nationID); $db->delete('nation', 'nation=%i', $nationID);
$db->delete('nation_turn', 'nation_id=%i', $nationID); $db->delete('nation_turn', 'nation_id=%i', $nationID);
@@ -2196,35 +2201,29 @@ function SabotageInjury(RandUtil $rng, array $cityGeneralList, string $reason):
return $injuryCount; return $injuryCount;
} }
function getRandTurn(RandUtil $rng, $term, ?\DateTimeInterface $baseDateTime = null) function getRandTurn(RandUtil $rng, int $term, ?int $baseTick = null): int
{ {
if ($baseDateTime === null) { $db = DB::db();
$baseDateTime = new \DateTimeImmutable(); $clock = GameClock::fromStorage(KVStorage::getStorage($db, 'game_env'));
} else if ($baseDateTime instanceof \DateTime) { $baseTick ??= $clock->nowTick();
$baseDateTime = \DateTimeImmutable::createFromMutable($baseDateTime);
} else if ($baseDateTime instanceof \DateTimeImmutable) { $randSecond = $rng->nextRangeInt(0, 60 * $term - 1);
//do Nothing $randMicrosecond = $rng->nextRangeInt(0, 999999); // 레거시 RNG 소비 6자리 유지
} else {
throw new MustNotBeReachedException(); return $baseTick
} + $clock->ticksFromSeconds($randSecond)
+ intdiv($randMicrosecond * $clock->ticksPerSecond(), 1_000_000);
$randSecond = $rng->nextRangeInt(0, 60 * $term - 1); }
$randFraction = $rng->nextRangeInt(0, 999999) / 1000000; //6자리 소수
function getRandTurn2(RandUtil $rng, int $term, ?int $baseTick = null): int
return TimeUtil::format($baseDateTime->add(TimeUtil::secondsToDateInterval($randSecond + $randFraction)), true); {
} $db = DB::db();
$clock = GameClock::fromStorage(KVStorage::getStorage($db, 'game_env'));
function getRandTurn2(RandUtil $rng, $term, ?\DateTimeInterface $baseDateTime = null) $baseTick ??= $clock->nowTick();
{ $randSecond = $rng->nextRangeInt(0, 60 * $term - 1);
if ($baseDateTime === null) { $randMicrosecond = $rng->nextRangeInt(0, 999999); // 레거시 RNG 소비 6자리 유지
$baseDateTime = new \DateTimeImmutable();
} else if ($baseDateTime instanceof \DateTime) { return $baseTick
$baseDateTime = \DateTimeImmutable::createFromMutable($baseDateTime); - $clock->ticksFromSeconds($randSecond)
} else { - intdiv($randMicrosecond * $clock->ticksPerSecond(), 1_000_000);
throw new MustNotBeReachedException(); }
}
$randSecond = $rng->nextRangeInt(0, 60 * $term - 1);
$randFraction = $rng->nextRangeInt(0, 999999) / 1000000; //6자리 소수
return $baseDateTime->sub(TimeUtil::secondsToDateInterval($randSecond + $randFraction))->format('Y-m-d H:i:s.u');
}
+4 -3
View File
@@ -62,11 +62,12 @@ function processAuction()
{ {
$db = DB::db(); $db = DB::db();
$now = TimeUtil::now(); $gameStor = KVStorage::getStorage($db, 'game_env');
$nowTick = GameClock::fromStorage($gameStor)->nowTick();
$auctionList = $db->queryAllLists( $auctionList = $db->queryAllLists(
'SELECT id, `type` FROM ng_auction WHERE `close_date` <= %s AND finished = 0', 'SELECT id, `type` FROM ng_auction WHERE `close_tick` <= %i AND finished = 0',
$now $nowTick
); );
if (!$auctionList) { if (!$auctionList) {
+65 -37
View File
@@ -42,15 +42,21 @@ function pushGeneralCommand(int $generalID, int $turnCnt=1){
$db = DB::db(); $db = DB::db();
$db->update('general_turn', [ $db->query(
'turn_idx'=>$db->sqleval('turn_idx + %i', $turnCnt) 'UPDATE general_turn AS dst
], 'general_id=%i ORDER BY turn_idx DESC', $generalID); LEFT JOIN general_turn AS src
$db->update('general_turn', [ ON src.general_id = dst.general_id
'turn_idx'=>$db->sqleval('turn_idx - %i', GameConst::$maxTurn), AND src.turn_idx = dst.turn_idx - %i
'action'=>'휴식', SET dst.action = IF(src.id IS NULL, %s, src.action),
'arg'=>'{}', dst.arg = IF(src.id IS NULL, %s, src.arg),
'brief'=>'휴식' dst.brief = IF(src.id IS NULL, %s, src.brief)
], 'general_id=%i AND turn_idx >= %i', $generalID, GameConst::$maxTurn); WHERE dst.general_id = %i',
$turnCnt,
'휴식',
'{}',
'휴식',
$generalID
);
} }
function pullGeneralCommand(int $generalID, int $turnCnt=1){ function pullGeneralCommand(int $generalID, int $turnCnt=1){
@@ -67,15 +73,21 @@ function pullGeneralCommand(int $generalID, int $turnCnt=1){
$db = DB::db(); $db = DB::db();
$db->update('general_turn', [ $db->query(
'turn_idx'=>$db->sqleval('turn_idx + %i', GameConst::$maxTurn), 'UPDATE general_turn AS dst
'action'=>'휴식', LEFT JOIN general_turn AS src
'arg'=>'{}', ON src.general_id = dst.general_id
'brief'=>'휴식' AND src.turn_idx = dst.turn_idx + %i
], 'general_id=%i AND turn_idx < %i', $generalID, $turnCnt); SET dst.action = IF(src.id IS NULL, %s, src.action),
$db->update('general_turn', [ dst.arg = IF(src.id IS NULL, %s, src.arg),
'turn_idx'=>$db->sqleval('turn_idx - %i', $turnCnt) dst.brief = IF(src.id IS NULL, %s, src.brief)
], 'general_id=%i ORDER BY turn_idx ASC', $generalID); WHERE dst.general_id = %i',
$turnCnt,
'휴식',
'{}',
'휴식',
$generalID
);
} }
function repeatGeneralCommand(int $generalId, int $turnCnt){ function repeatGeneralCommand(int $generalId, int $turnCnt){
@@ -126,15 +138,23 @@ function pushNationCommand(int $nationID, int $officerLevel, int $turnCnt=1){
$db = DB::db(); $db = DB::db();
$db->update('nation_turn', [ $db->query(
'turn_idx'=>$db->sqleval('turn_idx + %i', $turnCnt) 'UPDATE nation_turn AS dst
], 'nation_id=%i AND officer_level=%i ORDER BY turn_idx DESC', $nationID, $officerLevel); LEFT JOIN nation_turn AS src
$db->update('nation_turn', [ ON src.nation_id = dst.nation_id
'turn_idx'=>$db->sqleval('turn_idx - %i', GameConst::$maxChiefTurn), AND src.officer_level = dst.officer_level
'action'=>'휴식', AND src.turn_idx = dst.turn_idx - %i
'arg'=>'{}', SET dst.action = IF(src.id IS NULL, %s, src.action),
'brief'=>'휴식' dst.arg = IF(src.id IS NULL, %s, src.arg),
], 'nation_id=%i AND officer_level=%i AND turn_idx >= %i ORDER BY turn_idx ASC', $nationID, $officerLevel, GameConst::$maxChiefTurn); dst.brief = IF(src.id IS NULL, %s, src.brief)
WHERE dst.nation_id = %i AND dst.officer_level = %i',
$turnCnt,
'휴식',
'{}',
'휴식',
$nationID,
$officerLevel
);
} }
function pullNationCommand(int $nationID, int $officerLevel, int $turnCnt=1){ function pullNationCommand(int $nationID, int $officerLevel, int $turnCnt=1){
@@ -157,15 +177,23 @@ function pullNationCommand(int $nationID, int $officerLevel, int $turnCnt=1){
$db = DB::db(); $db = DB::db();
$db->update('nation_turn', [ $db->query(
'turn_idx'=>$db->sqleval('turn_idx + %i', GameConst::$maxChiefTurn), 'UPDATE nation_turn AS dst
'action'=>'휴식', LEFT JOIN nation_turn AS src
'arg'=>'{}', ON src.nation_id = dst.nation_id
'brief'=>'휴식', AND src.officer_level = dst.officer_level
], 'nation_id=%i AND officer_level=%i AND turn_idx < %i', $nationID, $officerLevel, $turnCnt); AND src.turn_idx = dst.turn_idx + %i
$db->update('nation_turn', [ SET dst.action = IF(src.id IS NULL, %s, src.action),
'turn_idx'=>$db->sqleval('turn_idx - %i', $turnCnt) dst.arg = IF(src.id IS NULL, %s, src.arg),
], 'nation_id=%i AND officer_level=%i ORDER BY turn_idx ASC', $nationID, $officerLevel); dst.brief = IF(src.id IS NULL, %s, src.brief)
WHERE dst.nation_id = %i AND dst.officer_level = %i',
$turnCnt,
'휴식',
'{}',
'휴식',
$nationID,
$officerLevel
);
} }
function repeatNationCommand(int $nationID, int $officerLevel, int $turnCnt){ function repeatNationCommand(int $nationID, int $officerLevel, int $turnCnt){
@@ -494,4 +522,4 @@ function setNationCommand(int $generalID, array $turnList, string $command, ?arr
'arg_test'=>true, 'arg_test'=>true,
'reason'=>'success' 'reason'=>'success'
]; ];
} }
+1 -1
View File
@@ -686,7 +686,7 @@ function TechLimit($startYear, $year, $tech) : bool {
$relYear = $year - $startYear; $relYear = $year - $startYear;
$relMaxTech = Util::valueFit( $relMaxTech = Util::valueFit(
floor($relYear / 5) + 1, floor($relYear / GameConst::$techLevelIncYear) + GameConst::$initialAllowedTechLevel,
1, 1,
GameConst::$maxTechLevel GameConst::$maxTechLevel
); );
+28 -17
View File
@@ -559,7 +559,7 @@ function checkStatistic()
$nationHist = ''; $nationHist = '';
foreach (GameConst::$availableNationType as $nationType) { foreach (GameConst::$availableNationType as $nationType) {
if (!Util::array_get($nationHists[$nationType])) { if (!($nationHists[$nationType] ?? null)) {
$nationHists[$nationType] = '-'; $nationHists[$nationType] = '-';
} }
$nationHist .= getNationType($nationType) . "({$nationHists[$nationType]}), "; $nationHist .= getNationType($nationType) . "({$nationHists[$nationType]}), ";
@@ -651,16 +651,24 @@ function checkStatistic()
} }
function convForOldGeneral(array $general, int $year, int $month) function convForOldGeneral(array $general, int $year, int $month)
{ {
$general['history'] = getGeneralHistoryLogAll($general['no']); $general['history'] = getGeneralHistoryLogAll($general['no']);
return [ $clock = GameClock::fromStorage(KVStorage::getStorage(DB::db(), 'game_env'));
$turnTimeDisplay = $clock->formatTick(Util::toInt($general['turntime']), true);
$general['turntime_tick'] = Util::toInt($general['turntime']);
$general['turntime'] = $turnTimeDisplay;
if ($general['recent_war'] !== null) {
$general['recent_war_tick'] = Util::toInt($general['recent_war']);
$general['recent_war'] = $clock->formatTick(Util::toInt($general['recent_war']), true);
}
return [
'server_id' => UniqueConst::$serverID, 'server_id' => UniqueConst::$serverID,
'general_no' => $general['no'], 'general_no' => $general['no'],
'owner' => $general['owner'], 'owner' => $general['owner'],
'name' => $general['name'], 'name' => $general['name'],
'last_yearmonth' => $year * 100 + $month, 'last_yearmonth' => $year * 100 + $month,
'turntime' => $general['turntime'], 'turntime' => $turnTimeDisplay,
'data' => Json::encode($general) 'data' => Json::encode($general)
]; ];
} }
@@ -734,7 +742,7 @@ function checkEmperior()
/** @var int[] */ /** @var int[] */
$auctionList = $db->queryFirstColumn( $auctionList = $db->queryFirstColumn(
'SELECT `id` FROM `ng_auction` WHERE `type` = %s AND `finished` = 0 ORDER BY `close_date` ASC', 'SELECT `id` FROM `ng_auction` WHERE `type` = %s AND `finished` = 0 ORDER BY `close_tick` ASC',
AuctionType::UniqueItem->value AuctionType::UniqueItem->value
); );
foreach($auctionList as $auctionID){ foreach($auctionList as $auctionID){
@@ -830,20 +838,23 @@ function checkEmperior()
$nation['aux'] += $nationStor->max_power ?? []; $nation['aux'] += $nationStor->max_power ?? [];
$nation['history'] = getNationHistoryLogAll($nation['nation']); $nation['history'] = getNationHistoryLogAll($nation['nation']);
storeOldGenerals(0, $admin['year'], $admin['month']); storeOldGenerals(0, $admin['year'], $admin['month']);
storeOldGenerals($nation['nation'], $admin['year'], $admin['month']); storeOldGenerals($nation['nation'], $admin['year'], $admin['month']);
$gameDate = GameClock::fromStorage($gameStor)->formatNow();
$db->insert('ng_old_nations', [
'server_id' => UniqueConst::$serverID, $db->insert('ng_old_nations', [
'nation' => $nation['nation'], 'server_id' => UniqueConst::$serverID,
'data' => Json::encode($nation) 'nation' => $nation['nation'],
'date' => $gameDate,
'data' => Json::encode($nation)
]); ]);
$noNationGeneral = $db->queryFirstColumn('SELECT `no` FROM general WHERE nation=0'); $noNationGeneral = $db->queryFirstColumn('SELECT `no` FROM general WHERE nation=0');
$db->insert('ng_old_nations', [ $db->insert('ng_old_nations', [
'server_id' => UniqueConst::$serverID, 'server_id' => UniqueConst::$serverID,
'nation' => 0, 'nation' => 0,
'data' => Json::encode([ 'date' => $gameDate,
'data' => Json::encode([
'nation' => 0, 'nation' => 0,
'name' => '재야', 'name' => '재야',
'generals' => $noNationGeneral 'generals' => $noNationGeneral
+6 -2
View File
@@ -4,6 +4,10 @@ namespace sammo;
function printLimitMsg($turntime) function printLimitMsg($turntime)
{ {
if (is_int($turntime) || (is_string($turntime) && ctype_digit(ltrim($turntime, '-')))) {
$gameStor = KVStorage::getStorage(DB::db(), 'game_env');
$turntime = GameClock::fromStorage($gameStor)->formatTick(Util::toInt($turntime), true);
}
//FIXME: template로 이동. //FIXME: template로 이동.
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
@@ -138,7 +142,7 @@ function JSCitiesBasedOnDistance(int $cityNo, int $maxDistance = 1): array{
$distanceList = searchDistance($cityNo, $maxDistance, true); $distanceList = searchDistance($cityNo, $maxDistance, true);
$result = []; $result = [];
for ($dist = 1; $dist <= $maxDistance; $dist++) { for ($dist = 1; $dist <= $maxDistance; $dist++) {
$result[$dist] = Util::array_get($distanceList[$dist], []); $result[$dist] = $distanceList[$dist] ?? [];
} }
return $result; return $result;
} }
@@ -152,7 +156,7 @@ function printCitiesBasedOnDistance(int $cityNo, int $maxDistance = 1): string
for ($dist = 1; $dist <= $maxDistance; $dist++) { for ($dist = 1; $dist <= $maxDistance; $dist++) {
$cityList = array_map(function ($cityID) { $cityList = array_map(function ($cityID) {
return CityConst::byID($cityID)->name; return CityConst::byID($cityID)->name;
}, Util::array_get($distanceList[$dist], [])); }, $distanceList[$dist] ?? []);
$cityStr = join(', ', $cityList); $cityStr = join(', ', $cityList);
+6 -2
View File
@@ -61,8 +61,12 @@ function chiefTurnTable()
"; ";
} }
function templateLimitMsg(string $turntime): string function templateLimitMsg(string $turntime): string
{ {
if (ctype_digit(ltrim($turntime, '-'))) {
$gameStor = KVStorage::getStorage(DB::db(), 'game_env');
$turntime = GameClock::fromStorage($gameStor)->formatTick(Util::toInt($turntime), true);
}
return "이미 너무 많은 접속을 하셨습니다. 다음 턴에 다시 시도해주세요. (턴시간: {$turntime})"; return "이미 너무 많은 접속을 하셨습니다. 다음 턴에 다시 시도해주세요. (턴시간: {$turntime})";
} }
+17 -10
View File
@@ -17,10 +17,10 @@ function processTournament()
{ {
$db = DB::db(); $db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env'); $gameStor = KVStorage::getStorage($db, 'game_env');
$clock = GameClock::fromStorage($gameStor);
$admin = $gameStor->getValues(['tournament', 'phase', 'tnmt_type', 'tnmt_auto', 'tnmt_time', 'turnterm', 'last_tournament_betting_id']); $admin = $gameStor->getValues(['tournament', 'phase', 'tnmt_type', 'tnmt_auto', 'tnmt_time', 'turnterm', 'last_tournament_betting_id']);
$now = new \DateTime(); $offset = intdiv($clock->nowTick() - Util::toInt($admin['tnmt_time']), $clock->ticksPerSecond());
$offset = $now->getTimestamp() - (new \DateTime($admin['tnmt_time']))->getTimestamp();
//수동일땐 무시 //수동일땐 무시
if (!$admin['tnmt_auto']) { if (!$admin['tnmt_auto']) {
@@ -122,10 +122,10 @@ function processTournament()
if ($tnmt == 6) { if ($tnmt == 6) {
$betTerm = Util::valueFit($unit * 60, null, 3600); $betTerm = Util::valueFit($unit * 60, null, 3600);
//처리 초 더한 날짜 //처리 초 더한 날짜
$dt = date("Y-m-d H:i:s", strtotime($admin['tnmt_time']) + $unit * $i + $betTerm);
$gameStor->tournament = $tnmt; $gameStor->tournament = $tnmt;
$gameStor->phase = $phase; $gameStor->phase = $phase;
$gameStor->tnmt_time = $dt; $gameStor->tnmt_time = Util::toInt($admin['tnmt_time'])
+ $clock->ticksFromSeconds($unit * $i + $betTerm);
return; return;
} }
} }
@@ -133,7 +133,7 @@ function processTournament()
$second = $unit * $iter; $second = $unit * $iter;
$gameStor->tournament = $tnmt; $gameStor->tournament = $tnmt;
$gameStor->phase = $phase; $gameStor->phase = $phase;
$gameStor->tnmt_time = (new \DateTimeImmutable($admin['tnmt_time']))->add(new \DateInterval("PT{$second}S"))->format('Y-m-d H:i:s'); $gameStor->tnmt_time = Util::toInt($admin['tnmt_time']) + $clock->ticksFromSeconds($second);
} }
function getTournamentTermText(int $turnTerm) function getTournamentTermText(int $turnTerm)
@@ -160,7 +160,8 @@ function getTournamentTime()
$gameStor = KVStorage::getStorage($db, 'game_env'); $gameStor = KVStorage::getStorage($db, 'game_env');
list($tnmt, $tnmt_time) = $gameStor->getValuesAsArray(['tournament', 'tnmt_time']); list($tnmt, $tnmt_time) = $gameStor->getValuesAsArray(['tournament', 'tnmt_time']);
$dt = substr($tnmt_time, 11, 5); $clock = GameClock::fromStorage($gameStor);
$dt = substr($clock->formatTick(Util::toInt($tnmt_time)), 11, 5);
switch ($tnmt) { switch ($tnmt) {
case 1: case 1:
$tnmt = "개막시간 {$dt}"; $tnmt = "개막시간 {$dt}";
@@ -284,9 +285,11 @@ function startTournament($type)
$admin = $gameStor->getValues(['year', 'month', 'turnterm']); $admin = $gameStor->getValues(['year', 'month', 'turnterm']);
$turnTerm = $admin['turnterm']; $turnTerm = $admin['turnterm'];
$unit = calcTournamentTerm($turnTerm); $unit = calcTournamentTerm($turnTerm);
$clock = GameClock::fromStorage($gameStor);
$gameStor->tnmt_auto = true; $gameStor->tnmt_auto = true;
$gameStor->tnmt_time = (new \DateTimeImmutable())->add(new \DateInterval("PT{$unit}M"))->format('Y-m-d H:i:s'); // 기존 startTournament은 unit을 분으로 더하므로 그 계약을 유지합니다.
$gameStor->tnmt_time = $clock->nowTick() + $clock->ticksFromMinutes($unit);
$gameStor->tournament = 1; $gameStor->tournament = 1;
$gameStor->tnmt_type = $type; $gameStor->tnmt_type = $type;
$gameStor->last_tournament_betting_id = 0; $gameStor->last_tournament_betting_id = 0;
@@ -1153,21 +1156,25 @@ function fight($tnmt_type, $tnmt, $phs, $group, $g1, $g2, $type)
]; ];
//막판 분노 //막판 분노
$factor1 = 1.0;
$factor2 = 1.0;
$ratio = rand() % 300; $ratio = rand() % 300;
if ($e1 / 5 > $energy1 && $damage1 > $damage2 && $gen1[$tp] >= $ratio) { if ($e1 / 5 > $energy1 && $damage1 > $damage2 && $gen1[$tp] >= $ratio) {
$damage2 *= Util::round((rand() % 301 + 200) / 100); // 200 ~ 500% $factor2 = Util::round((rand() % 301 + 200) / 100); // 200 ~ 500%
$critical1 = 1; $critical1 = 1;
$str = Util::choiceRandom($crticialSkillMap[$tnmt_type]); $str = Util::choiceRandom($crticialSkillMap[$tnmt_type]);
$log[] = "<S>●</> <Y>{$gen1['name']}</>의 분노의 <M>{$str}</> 공격!"; $log[] = "<S>●</> <Y>{$gen1['name']}</>의 분노의 <M>{$str}</> 공격!";
} }
$ratio = rand() % 300; $ratio = rand() % 300;
if ($e2 / 5 > $energy2 && $damage2 > $damage1 && $gen2[$tp] >= $ratio) { if ($e2 / 5 > $energy2 && $damage2 > $damage1 && $gen2[$tp] >= $ratio) {
$damage1 *= Util::round((rand() % 301 + 200) / 100); // 200 ~ 500% $factor1 = Util::round((rand() % 301 + 200) / 100); // 200 ~ 500%
$critical2 = 1; $critical2 = 1;
$str = Util::choiceRandom($crticialSkillMap[$tnmt_type]); $str = Util::choiceRandom($crticialSkillMap[$tnmt_type]);
$log[] = "<S>●</> <Y>{$gen2['name']}</>의 분노의 <M>{$str}</> 공격!"; $log[] = "<S>●</> <Y>{$gen2['name']}</>의 분노의 <M>{$str}</> 공격!";
} }
$damage1 *= $factor1;
$damage2 *= $factor2;
//1합 승부 //1합 승부
if ($phase == 1) { if ($phase == 1) {
+3 -2
View File
@@ -7,6 +7,7 @@ include "func.php";
Session::requireLogin()->loginGame()->setReadOnly(); Session::requireLogin()->loginGame()->setReadOnly();
$mapName = GameConst::$mapName; $mapName = GameConst::$mapName;
$frontClock = GameClock::fromStorage(KVStorage::getStorage(DB::db(), 'game_env'));
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
@@ -28,7 +29,7 @@ $mapName = GameConst::$mapName;
'maxTurn' => GameConst::$maxTurn, 'maxTurn' => GameConst::$maxTurn,
'maxPushTurn' => 12, 'maxPushTurn' => 12,
'serverNow' => TimeUtil::now(false), 'serverNow' => $frontClock->formatTick($frontClock->nowTick()),
] ]
], false) ?> ], false) ?>
<?= WebUtil::printJS('../d_shared/common_path.js') ?> <?= WebUtil::printJS('../d_shared/common_path.js') ?>
@@ -42,4 +43,4 @@ $mapName = GameConst::$mapName;
<div id="app"></div> <div id="app"></div>
</body> </body>
</html> </html>
+15 -12
View File
@@ -25,21 +25,24 @@ if(!$reserved){
]); ]);
} }
$reservedDate = new \DateTime($reserved['date']); $reservedDate = new \DateTimeImmutable($reserved['date']);
$now = new \DateTime(); $now = GameClock::readWallTime();
$status = 'not_yet'; $status = 'not_yet';
list($isUnited, $lastTurn) = $gameStor->getValuesAsArray(['isunited', 'turntime']); list($isUnited, $unitedWallAnchor) = $gameStor->getValuesAsArray(['isunited', 'autoreset_united_wall_anchor']);
if($isUnited === null || $lastTurn === null){ if($isUnited === null){
$isUnited = 2; $isUnited = 2;
$lastTurn = '2000-01-01'; }
} $lastTurn = null;
if($isUnited > 0){
if($lastTurn !== null){ if(!is_string($unitedWallAnchor) || $unitedWallAnchor === ''){
$lastTurn = new \DateTime($lastTurn); $unitedWallAnchor = TimeUtil::format($now, true);
} $gameStor->autoreset_united_wall_anchor = $unitedWallAnchor;
}
$lastTurn = new \DateTimeImmutable($unitedWallAnchor);
}
if($lastTurn === null){ if($lastTurn === null){
//이미 리셋된 상태임 //이미 리셋된 상태임
@@ -99,4 +102,4 @@ $result['affected']=1;
$prefix = DB::prefix(); $prefix = DB::prefix();
ServConfig::getServerList()[$prefix]->openServer(); ServConfig::getServerList()[$prefix]->openServer();
Json::die($result); Json::die($result);
+3 -2
View File
@@ -10,6 +10,7 @@ $session = Session::requireGameLogin()->setReadOnly();
$userID = Session::getUserID(); $userID = Session::getUserID();
$db = DB::db(); $db = DB::db();
$clock = GameClock::fromStorage(KVStorage::getStorage($db, 'game_env'));
$isSecretBoard = Util::getPost('isSecret', 'bool', false); $isSecretBoard = Util::getPost('isSecret', 'bool', false);
$title = Util::getPost('title'); $title = Util::getPost('title');
@@ -67,7 +68,7 @@ $icon = GetImageURL($me['imgsvr'], $me['picture']);
$db->insert('board', [ $db->insert('board', [
'nation_no'=>$me['nation'], 'nation_no'=>$me['nation'],
'is_secret'=>$isSecretBoard, 'is_secret'=>$isSecretBoard,
'date'=>TimeUtil::now(), 'date'=>$clock->formatNow(),
'general_no'=>$me['no'], 'general_no'=>$me['no'],
'author'=>$me['name'], 'author'=>$me['name'],
'author_icon'=>$icon, 'author_icon'=>$icon,
@@ -79,4 +80,4 @@ Json::die([
'result'=>true, 'result'=>true,
'reason'=>'success', 'reason'=>'success',
'row_id'=>$db->insertId() 'row_id'=>$db->insertId()
]); ]);
+3 -2
View File
@@ -10,6 +10,7 @@ $session = Session::requireGameLogin()->setReadOnly();
$userID = Session::getUserID(); $userID = Session::getUserID();
$db = DB::db(); $db = DB::db();
$clock = GameClock::fromStorage(KVStorage::getStorage($db, 'game_env'));
$articleNo = Util::getPost('articleNo', 'int'); $articleNo = Util::getPost('articleNo', 'int');
$text = Util::getPost('text'); $text = Util::getPost('text');
@@ -73,7 +74,7 @@ else if ($isSecretBoard && $permission < 2) {
$db->insert('comment', [ $db->insert('comment', [
'nation_no'=>$me['nation'], 'nation_no'=>$me['nation'],
'is_secret'=>$isSecretBoard, 'is_secret'=>$isSecretBoard,
'date'=>TimeUtil::now(), 'date'=>$clock->formatNow(),
'document_no'=>$articleNo, 'document_no'=>$articleNo,
'general_no'=>$me['no'], 'general_no'=>$me['no'],
'author'=>$me['name'], 'author'=>$me['name'],
@@ -84,4 +85,4 @@ Json::die([
'result'=>true, 'result'=>true,
'reason'=>'success', 'reason'=>'success',
'row_id'=>$db->insertId() 'row_id'=>$db->insertId()
]); ]);
+2 -2
View File
@@ -83,7 +83,7 @@ else{
} }
$now = new \DateTime(); $now = Message::gameNow();
$unlimited = new \DateTime('9999-12-31'); $unlimited = new \DateTime('9999-12-31');
if(in_array($stateOpt, ['try_destroy_src', 'try_destroy_dest'])){ if(in_array($stateOpt, ['try_destroy_src', 'try_destroy_dest'])){
@@ -138,4 +138,4 @@ Json::die([
'result'=>true, 'result'=>true,
'reason'=>'success', 'reason'=>'success',
'state'=>$lastState 'state'=>$lastState
]); ]);
+2 -2
View File
@@ -69,7 +69,7 @@ $destNation = getNationStaticInfo($letter['dest_nation_id']);
$src = new MessageTarget($me['no'], $me['name'], $destNation['nation'], $destNation['name'], $destNation['color'], $me['icon']); $src = new MessageTarget($me['no'], $me['name'], $destNation['nation'], $destNation['name'], $destNation['color'], $me['icon']);
$dest = new MessageTarget(0, '', $srcNation['nation'], $srcNation['name'], $srcNation['color']); $dest = new MessageTarget(0, '', $srcNation['nation'], $srcNation['name'], $srcNation['color']);
$now = new \DateTime(); $now = Message::gameNow();
$unlimited = new \DateTime('9999-12-31'); $unlimited = new \DateTime('9999-12-31');
if($isAgree){ if($isAgree){
@@ -133,4 +133,4 @@ $msgID = $msg->send();
Json::die([ Json::die([
'result'=>true, 'result'=>true,
'reason'=>'success' 'reason'=>'success'
]); ]);
+2 -2
View File
@@ -63,7 +63,7 @@ $destNation = getNationStaticInfo($letter['dest_nation_id']);
$src = new MessageTarget($me['no'], $me['name'], $srcNation['nation'], $srcNation['name'], $srcNation['color'], $me['icon']); $src = new MessageTarget($me['no'], $me['name'], $srcNation['nation'], $srcNation['name'], $srcNation['color'], $me['icon']);
$dest = new MessageTarget(0, '', $destNation['nation'], $destNation['name'], $destNation['color']); $dest = new MessageTarget(0, '', $destNation['nation'], $destNation['name'], $destNation['color']);
$now = new \DateTime(); $now = Message::gameNow();
$unlimited = new \DateTime('9999-12-31'); $unlimited = new \DateTime('9999-12-31');
$aux['reason'] = [ $aux['reason'] = [
@@ -91,4 +91,4 @@ $msgID = $msg->send();
Json::die([ Json::die([
'result'=>true, 'result'=>true,
'reason'=>'success' 'reason'=>'success'
]); ]);
+5 -3
View File
@@ -140,6 +140,8 @@ else{
$me['icon'] = GetImageURL($me['imgsvr'], $me['picture']); $me['icon'] = GetImageURL($me['imgsvr'], $me['picture']);
$clock = GameClock::fromStorage($gameStor);
$gameNow = $clock->nowDateTime();
$db->insert('ng_diplomacy', [ $db->insert('ng_diplomacy', [
'src_nation_id'=>$srcNation['nation'], 'src_nation_id'=>$srcNation['nation'],
'dest_nation_id'=>$destNation['nation'], 'dest_nation_id'=>$destNation['nation'],
@@ -147,7 +149,7 @@ $db->insert('ng_diplomacy', [
'state'=>'proposed', 'state'=>'proposed',
'text_brief'=>$textBrief, 'text_brief'=>$textBrief,
'text_detail'=>$textDetail, 'text_detail'=>$textDetail,
'date'=>TimeUtil::now(), 'date'=>TimeUtil::format($gameNow),
'src_signer'=>$me['no'], 'src_signer'=>$me['no'],
'dest_signer'=>null, 'dest_signer'=>null,
'aux'=>Json::encode([ 'aux'=>Json::encode([
@@ -168,7 +170,7 @@ $newLetterNo = $db->insertId();
$src = new MessageTarget($me['no'], $me['name'], $srcNation['nation'], $srcNation['name'], $srcNation['color'], $me['icon']); $src = new MessageTarget($me['no'], $me['name'], $srcNation['nation'], $srcNation['name'], $srcNation['color'], $me['icon']);
$dest = new MessageTarget(0, '', $destNation['nation'], $destNation['name'], $destNation['color']); $dest = new MessageTarget(0, '', $destNation['nation'], $destNation['name'], $destNation['color']);
$now = new \DateTime(); $now = \DateTime::createFromImmutable($gameNow);
$unlimited = new \DateTime('9999-12-31'); $unlimited = new \DateTime('9999-12-31');
$josaYi = JosaUtil::pick($newLetterNo, '이'); $josaYi = JosaUtil::pick($newLetterNo, '이');
@@ -195,4 +197,4 @@ Json::die([
'result'=>true, 'result'=>true,
'reason'=>'success', 'reason'=>'success',
'row_id'=>$db->insertId() 'row_id'=>$db->insertId()
]); ]);
+23 -23
View File
@@ -16,12 +16,10 @@ $session = Session::requireLogin([])->setReadOnly();
$userID = Session::getUserID(); $userID = Session::getUserID();
$oNow = new \DateTimeImmutable();
$now = $oNow->format('Y-m-d H:i:s');
$db = DB::db(); $db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env'); $gameStor = KVStorage::getStorage($db, 'game_env');
$clock = GameClock::fromStorage($gameStor);
$now = $clock->nowTick();
$oldGeneral = $db->queryFirstField('SELECT `no` FROM general WHERE `owner`=%i', $userID); $oldGeneral = $db->queryFirstField('SELECT `no` FROM general WHERE `owner`=%i', $userID);
if($oldGeneral !== null){ if($oldGeneral !== null){
@@ -44,15 +42,14 @@ if($npcmode!=1){
]); ]);
} }
$token = $db->queryFirstRow('SELECT * FROM select_npc_token WHERE `owner`=%i AND `valid_until`>=%s', $userID, $now); $token = $db->queryFirstRow('SELECT * FROM select_npc_token WHERE `owner`=%i AND `valid_until`>=%i', $userID, $now);
$pickResult = []; $pickResult = [];
if($token && $refresh){ if($token && $refresh){
$pickMoreFrom = (new \DateTime($token['pick_more_from']))->getTimestamp(); $pickMoreFrom = Util::toInt($token['pick_more_from']);
$nowT = $oNow->getTimestamp();
if($nowT >= $pickMoreFrom){ if($now >= $pickMoreFrom){
$oldPickResult = Json::decode($token['pick_result']); $oldPickResult = Json::decode($token['pick_result']);
foreach($keepResult as $keepId){ foreach($keepResult as $keepId){
@@ -75,15 +72,16 @@ if($token && $refresh){
} }
if($token && !$refresh){ if($token && !$refresh){
$pickMoreFrom = (new \DateTime($token['pick_more_from']))->getTimestamp(); $pickMoreFrom = Util::toInt($token['pick_more_from']);
$nowT = $oNow->getTimestamp();
Json::die([ Json::die([
'result'=>true, 'result'=>true,
'pick'=>Json::decode($token['pick_result']), 'pick'=>Json::decode($token['pick_result']),
'pickMoreFrom'=>$token['pick_more_from'], 'pickMoreFrom'=>$clock->formatTick($pickMoreFrom),
'pickMoreSeconds'=>$pickMoreFrom-$nowT, 'pickMoreSeconds'=>intdiv($pickMoreFrom - $now, $clock->ticksPerSecond()),
'validUntil'=>$token['valid_until'] 'validUntil'=>$clock->formatTick(Util::toInt($token['valid_until'])),
'validForSeconds'=>max(0, intdiv(Util::toInt($token['valid_until']) - $now, $clock->ticksPerSecond())),
'clockMode'=>$clock->getMode(),
]); ]);
} }
@@ -100,7 +98,7 @@ foreach($db->query('SELECT `no`, `name`, leadership, strength, intel, nation, im
$weight[$general['no']] = pow($allStat, 1.5); $weight[$general['no']] = pow($allStat, 1.5);
} }
foreach($db->queryFirstColumn('SELECT pick_result FROM select_npc_token WHERE `owner`!=%i AND valid_until >=%s', $userID, $now) as $reserved){ foreach($db->queryFirstColumn('SELECT pick_result FROM select_npc_token WHERE `owner`!=%i AND valid_until >=%i', $userID, $now) as $reserved){
$reserved = Json::decode($reserved); $reserved = Json::decode($reserved);
foreach(array_keys($reserved) as $reservedNPC){ foreach(array_keys($reserved) as $reservedNPC){
if(key_exists($reservedNPC, $weight)){ if(key_exists($reservedNPC, $weight)){
@@ -131,8 +129,8 @@ $newNonce = random_int(0, 0xfffffff);
$validSecond = max(VALID_SECOND, $turnterm*40); $validSecond = max(VALID_SECOND, $turnterm*40);
$pickMoreSecond = max(PICK_MORE_SECOND, Util::round(pow($turnterm, 0.672)*8)); $pickMoreSecond = max(PICK_MORE_SECOND, Util::round(pow($turnterm, 0.672)*8));
$validUntil = $oNow->add(new \DateInterval(sprintf('PT%dS', $validSecond))); $validUntil = $now + $clock->ticksFromSeconds($validSecond);
$pickMoreFrom = $oNow->add(new \DateInterval(sprintf('PT%dS', $pickMoreSecond))); $pickMoreFrom = $now + $clock->ticksFromSeconds($pickMoreSecond);
$db->delete('select_npc_token', 'valid_until < %s', $now); $db->delete('select_npc_token', 'valid_until < %s', $now);
@@ -140,8 +138,8 @@ $inserted = 0;
if($token){ if($token){
$db->update('select_npc_token', [ $db->update('select_npc_token', [
'valid_until'=>$validUntil->format('Y-m-d H:i:s'), 'valid_until'=>$validUntil,
'pick_more_from'=>$pickMoreFrom->format('Y-m-d H:i:s'), 'pick_more_from'=>$pickMoreFrom,
'pick_result'=>Json::encode($pickResult), 'pick_result'=>Json::encode($pickResult),
'nonce'=>$newNonce 'nonce'=>$newNonce
], 'owner = %i AND nonce = %i', $userID, $token['nonce']); ], 'owner = %i AND nonce = %i', $userID, $token['nonce']);
@@ -152,8 +150,8 @@ if($token){
else{ else{
$db->insertIgnore('select_npc_token', [ $db->insertIgnore('select_npc_token', [
'owner'=>$userID, 'owner'=>$userID,
'valid_until'=>$validUntil->format('Y-m-d H:i:s'), 'valid_until'=>$validUntil,
'pick_more_from'=>'2000-01-01 01:00:00', 'pick_more_from'=>$now,
'pick_result'=>Json::encode($pickResult), 'pick_result'=>Json::encode($pickResult),
'nonce'=>$newNonce 'nonce'=>$newNonce
]); ]);
@@ -173,7 +171,9 @@ if($inserted === 0){
Json::die([ Json::die([
'result'=>true, 'result'=>true,
'pick'=>$pickResult, 'pick'=>$pickResult,
'pickMoreFrom'=>($inserted===-1)?$pickMoreFrom->format('Y-m-d H:i:s'):'2000-01-01 01:00:00', 'pickMoreFrom'=>$clock->formatTick(($inserted===-1)?$pickMoreFrom:$now),
'pickMoreSeconds'=>($inserted===-1)?$pickMoreSecond:0, 'pickMoreSeconds'=>($inserted===-1)?$pickMoreSecond:0,
'validUntil'=>$validUntil->format('Y-m-d H:i:s') 'validUntil'=>$clock->formatTick($validUntil),
]); 'validForSeconds'=>max(0, intdiv($validUntil - $now, $clock->ticksPerSecond())),
'clockMode'=>$clock->getMode(),
]);
+34 -14
View File
@@ -10,7 +10,23 @@ function sortTokens(&$tokens){
}); });
} }
function putInfoText(&$info){ function putInfoText(&$info, ?array $currentTargetEnv){
if (($info['event100Growth'] ?? false) === true) {
if ($currentTargetEnv === null) {
$displayStats = CentennialAllStarGrowthService::calculateUserInitialStats($info);
$info['selectionStatLabel'] = '시작 능력치';
} else {
$displayStats = CentennialAllStarGrowthService::calculateUserCurrentTargetStats(
$info,
$currentTargetEnv
);
$info['selectionStatLabel'] = '현재 변경 기준 능력치';
}
$info['selectionLeadership'] = $displayStats['leadership'];
$info['selectionStrength'] = $displayStats['strength'];
$info['selectionIntel'] = $displayStats['intel'];
}
if(key_exists('specialDomestic', $info)){ if(key_exists('specialDomestic', $info)){
$class = buildGeneralSpecialDomesticClass($info['specialDomestic']); $class = buildGeneralSpecialDomesticClass($info['specialDomestic']);
$info['specialDomesticName'] = $class->getName(); $info['specialDomesticName'] = $class->getName();
@@ -27,14 +43,13 @@ function putInfoText(&$info){
$session = Session::requireLogin([])->setReadOnly(); $session = Session::requireLogin([])->setReadOnly();
$userID = Session::getUserID(); $userID = Session::getUserID();
$oNow = new \DateTimeImmutable(); $db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env');
$clock = GameClock::fromStorage($gameStor);
$now = $clock->nowTick();
$now = $oNow->format('Y-m-d H:i:s'); $eventEnv = $gameStor->getValues(['npcmode', 'startyear', 'year', 'month']);
$npcmode = $eventEnv['npcmode'];
$db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env');
$npcmode = $gameStor->getValue('npcmode');
if($npcmode!=2){ if($npcmode!=2){
Json::die([ Json::die([
'result'=>false, 'result'=>false,
@@ -42,7 +57,8 @@ if($npcmode!=2){
]); ]);
} }
$rawGeneral = $db->queryFirstRow('SELECT no, aux FROM general WHERE `owner` = %i', $userID); $rawGeneral = $db->queryFirstRow('SELECT no, aux FROM general WHERE `owner` = %i', $userID);
$currentTargetEnv = $rawGeneral ? $eventEnv : null;
if($rawGeneral){ if($rawGeneral){
$generalAux = Json::decode($rawGeneral['aux']); $generalAux = Json::decode($rawGeneral['aux']);
if(key_exists('next_change', $generalAux)&& $generalAux['next_change'] > $now){ if(key_exists('next_change', $generalAux)&& $generalAux['next_change'] > $now){
@@ -62,7 +78,7 @@ if($tokens){
foreach($tokens as $token){ foreach($tokens as $token){
$valid_until = $token['reserved_until']; $valid_until = $token['reserved_until'];
$info = Json::decode($token['info']); $info = Json::decode($token['info']);
putInfoText($info); putInfoText($info, $currentTargetEnv);
$info['uniqueName'] = $token['unique_name']; $info['uniqueName'] = $token['unique_name'];
$pick[] = $info; $pick[] = $info;
} }
@@ -70,7 +86,9 @@ if($tokens){
Json::die([ Json::die([
'result'=>true, 'result'=>true,
'pick'=>$pick, 'pick'=>$pick,
'validUntil'=>$valid_until 'validUntil'=>$clock->formatTick(Util::toInt($valid_until)),
'validForSeconds'=>max(0, intdiv(Util::toInt($valid_until) - $now, $clock->ticksPerSecond())),
'clockMode'=>$clock->getMode(),
]); ]);
} }
@@ -83,12 +101,14 @@ $valid_until = null;
foreach(pickGeneralFromPool($db, $rng, $userID, 14) as $pickObj){ foreach(pickGeneralFromPool($db, $rng, $userID, 14) as $pickObj){
$valid_until = $pickObj->getValidUntil(); $valid_until = $pickObj->getValidUntil();
$info = $pickObj->getInfo(); $info = $pickObj->getInfo();
putInfoText($info); putInfoText($info, $currentTargetEnv);
$pick[] = $info; $pick[] = $info;
} }
sortTokens($pick);//좀 무식하지만.. sortTokens($pick);//좀 무식하지만..
Json::die([ Json::die([
'result'=>true, 'result'=>true,
'pick'=>$pick, 'pick'=>$pick,
'validUntil'=>$valid_until 'validUntil'=>$valid_until === null ? null : $clock->formatTick(Util::toInt($valid_until)),
]); 'validForSeconds'=>$valid_until === null ? 0 : max(0, intdiv(Util::toInt($valid_until) - $now, $clock->ticksPerSecond())),
'clockMode'=>$clock->getMode(),
]);
+2 -2
View File
@@ -211,7 +211,7 @@ try{
$show_img_level, $show_img_level,
!!$tournament_trig, !!$tournament_trig,
$join_mode, $join_mode,
TimeUtil::now(), TimeUtil::format(GameClock::readWallTime(), false),
$autorun_user $autorun_user
)); ));
} }
@@ -220,4 +220,4 @@ catch(\Exception $e){
'result'=>false, 'result'=>false,
'reason'=>$e->getMessage() 'reason'=>$e->getMessage()
]); ]);
} }
+2 -2
View File
@@ -144,7 +144,7 @@ function do도시임명(General $general, int $cityID, int $targetOfficerLevel):
return '올바르지 않은 도시입니다'; return '올바르지 않은 도시입니다';
} }
if ($general->getVar(GeneralColumn::officer_level) >= 4 && $myPenalty[PenaltyKey::NoChiefChange->value] ?? false) { if ($general->getVar(GeneralColumn::officer_level) >= 4 && ($myPenalty[PenaltyKey::NoChiefChange->value] ?? false)) {
return '수뇌인 장수를 변경할 수 없는 상태입니다.'; return '수뇌인 장수를 변경할 수 없는 상태입니다.';
} }
@@ -290,7 +290,7 @@ function do추방(General $general, int $myOfficerLevel): ?string
$src, $src,
$src, $src,
$str, $str,
new \DateTime(), Message::gameNow(),
new \DateTime('9999-12-31'), new \DateTime('9999-12-31'),
[] []
); );
+4 -4
View File
@@ -24,8 +24,8 @@ $db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env'); $gameStor = KVStorage::getStorage($db, 'game_env');
$rootDB = RootDB::db(); $rootDB = RootDB::db();
$oNow = new \DateTimeImmutable(); $clock = GameClock::fromStorage($gameStor);
$now = $oNow->format('Y-m-d H:i:s'); $now = $clock->nowTick();
$member = $rootDB->queryFirstRow('SELECT `name`, `penalty` FROM member WHERE `NO`=%i', $userID); $member = $rootDB->queryFirstRow('SELECT `name`, `penalty` FROM member WHERE `NO`=%i', $userID);
if(!$member){ if(!$member){
@@ -41,7 +41,7 @@ $penaltyInfo = array_merge($memberPenalty['any'] ?? [], $memberPenalty[DB::prefi
$penalty = []; $penalty = [];
foreach($penaltyInfo as $penaltyKey => $penaltyValue){ foreach($penaltyInfo as $penaltyKey => $penaltyValue){
if($penaltyValue['expire'] ?? 0 > TimeUtil::now()){ if(($penaltyValue['expire'] ?? 0) > TimeUtil::now()){
$penalty[$penaltyKey] = $penaltyValue['value']; $penalty[$penaltyKey] = $penaltyValue['value'];
} }
} }
@@ -139,4 +139,4 @@ $rootDB->insert('member_log', [
Json::die([ Json::die([
'result'=>true, 'result'=>true,
'reason'=>'success' 'reason'=>'success'
]); ]);
+95 -19
View File
@@ -8,10 +8,20 @@ WebUtil::requireAJAX();
$pick = Util::getPost('pick'); $pick = Util::getPost('pick');
$leadership = Util::getPost('leadership', 'int', GameConst::$defaultStatMin); $leadership = Util::getPost('leadership', 'int', GameConst::$defaultStatMin);
$strength = Util::getPost('leadership', 'int', GameConst::$defaultStatMin); $isCentennialAllStar = CentennialAllStarGrowthService::isActive();
$intel = Util::getPost('leadership', 'int', GameConst::$defaultStatMin); $strength = Util::getPost(
$isCentennialAllStar ? 'strength' : 'leadership',
'int',
GameConst::$defaultStatMin
);
$intel = Util::getPost(
$isCentennialAllStar ? 'intel' : 'leadership',
'int',
GameConst::$defaultStatMin
);
$personal = Util::getPost('personal', 'string', null); $personal = Util::getPost('personal', 'string', null);
$use_own_picture = Util::getPost('use_own_picture', 'bool', false); $use_own_picture = Util::getPost('use_own_picture', 'bool', false);
$pictureSource = Util::getPost('picture_source', 'string', 'selected');
if(!$pick){ if(!$pick){
@@ -27,8 +37,8 @@ $db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env'); $gameStor = KVStorage::getStorage($db, 'game_env');
$rootDB = RootDB::db(); $rootDB = RootDB::db();
$oNow = new \DateTimeImmutable(); $clock = GameClock::fromStorage($gameStor);
$now = $oNow->format('Y-m-d H:i:s'); $now = $clock->nowTick();
$hasGeneralID = $db->queryFirstField('SELECT no FROM general WHERE owner = %i', $userID); $hasGeneralID = $db->queryFirstField('SELECT no FROM general WHERE owner = %i', $userID);
if($hasGeneralID){ if($hasGeneralID){
@@ -60,16 +70,39 @@ if(!$selectInfo){
} }
$selectInfo = Json::decode($selectInfo); $selectInfo = Json::decode($selectInfo);
$ownerInfo = RootDB::db()->queryFirstRow('SELECT `name`,`picture`,`imgsvr` FROM member WHERE `NO`=%i',$userID); $ownerInfo = RootDB::db()->queryFirstRow(
if(!$ownerInfo){ 'SELECT `name`,`picture`,`imgsvr`,`grade` FROM member WHERE `NO`=%i',
$userID
);
if(!$ownerInfo){
Json::die([ Json::die([
'result'=>false, 'result'=>false,
'reason'=>'멤버 정보를 가져오지 못했습니다.' 'reason'=>'멤버 정보를 가져오지 못했습니다.'
]); ]);
} }
if ($isCentennialAllStar) {
if (!in_array($pictureSource, ['selected', 'own'], true)) {
$gencount = $db->queryFirstField('SELECT count(`no`) FROM general WHERE npc<2'); Json::die([
'result' => false,
'reason' => '올바르지 않은 전콘 선택입니다.',
]);
}
if ($pictureSource === 'own') {
$canUseOwnPicture = in_array('picture', GameConst::$generalPoolAllowOption, true)
&& $env['show_img_level'] >= 1
&& $ownerInfo['grade'] >= 1
&& $ownerInfo['picture'] !== '';
if (!$canUseOwnPicture) {
Json::die([
'result' => false,
'reason' => '사용할 수 있는 내 전콘이 없습니다.',
]);
}
}
}
$gencount = $db->queryFirstField('SELECT count(`no`) FROM general WHERE npc<2');
if ($gencount >= $maxgeneral) { if ($gencount >= $maxgeneral) {
Json::die([ Json::die([
@@ -80,12 +113,25 @@ if ($gencount >= $maxgeneral) {
$poolClass = getGeneralPoolClass(GameConst::$targetGeneralPool); $poolClass = getGeneralPoolClass(GameConst::$targetGeneralPool);
/** @var AbsGeneralPool */ /** @var AbsGeneralPool */
$pickedGeneral = new $poolClass($db, $selectInfo, $now); if ($isCentennialAllStar) {
$rng = new RandUtil(new LiteHashDRBG(Util::simpleSerialize(
UniqueConst::$hiddenSeed,
'selectPickedGeneral',
$userID,
$pick
)));
$pickedGeneral = new $poolClass($db, $rng, $selectInfo, $now);
} else {
$pickedGeneral = new $poolClass($db, $selectInfo, $now);
}
$builder = $pickedGeneral->getGeneralBuilder(); $builder = $pickedGeneral->getGeneralBuilder();
if ($isCentennialAllStar) {
CentennialAllStarGrowthService::prepareInitialUser($builder, $selectInfo);
}
foreach(GameConst::$generalPoolAllowOption as $allowOption){ foreach(GameConst::$generalPoolAllowOption as $allowOption){
if($allowOption == 'stat'){ if($allowOption == 'stat' && !$isCentennialAllStar){
$leadership = Util::valueFit($leadership, GameConst::$defaultStatMin, GameConst::$defaultStatMax); $leadership = Util::valueFit($leadership, GameConst::$defaultStatMin, GameConst::$defaultStatMax);
$strength = Util::valueFit($strength, GameConst::$defaultStatMin, GameConst::$defaultStatMax); $strength = Util::valueFit($strength, GameConst::$defaultStatMin, GameConst::$defaultStatMax);
$intel = Util::valueFit($intel, GameConst::$defaultStatMin, GameConst::$defaultStatMax); $intel = Util::valueFit($intel, GameConst::$defaultStatMin, GameConst::$defaultStatMax);
@@ -98,14 +144,23 @@ foreach(GameConst::$generalPoolAllowOption as $allowOption){
} }
$builder->setStat($leadership, $strength, $intel); $builder->setStat($leadership, $strength, $intel);
} }
else if($allowOption == 'picture' && $use_own_picture){ else if(
$builder->setPicture($ownerInfo['imgsvr'], $ownerInfo['picture']); $allowOption == 'picture'
&& (
(!$isCentennialAllStar && $use_own_picture)
|| ($isCentennialAllStar && $pictureSource === 'own')
)
){
$builder->setPicture($ownerInfo['imgsvr'], $ownerInfo['picture']);
} }
else if($allowOption == 'ego'){ else if($allowOption == 'ego'){
if(!$personal || $personal == 'Random'){ if(!$personal || $personal == 'Random'){
$personal = Util::choiceRandom(GameConst::$availablePersonality); $personal = Util::choiceRandom(GameConst::$availablePersonality);
} }
if(!array_search($personal, GameConst::$availablePersonality)){ $invalidPersonal = $isCentennialAllStar
? !in_array($personal, GameConst::$availablePersonality, true)
: !array_search($personal, GameConst::$availablePersonality);
if($invalidPersonal){
Json::die([ Json::die([
'result'=>false, 'result'=>false,
'reason'=>'올바르지 않은 성격입니다.' 'reason'=>'올바르지 않은 성격입니다.'
@@ -113,7 +168,7 @@ foreach(GameConst::$generalPoolAllowOption as $allowOption){
} }
$builder->setEgo($personal); $builder->setEgo($personal);
} }
} }
$userNick = $ownerInfo['name']; $userNick = $ownerInfo['name'];
@@ -121,8 +176,29 @@ $builder->setOwner($userID);
$builder->setOwnerName($userNick); $builder->setOwnerName($userNick);
$builder->setKillturn(5); $builder->setKillturn(5);
$builder->setNPCType(0); $builder->setNPCType(0);
$builder->setAuxVar('next_change', TimeUtil::nowAddMinutes(12 * $env['turnterm'])); $builder->setAuxVar('next_change', $now + GameClock::TICKS_PER_TURN * 12);
$builder->setAuxVar(
'prestart_delete_after',
addTurn($now, $env['turnterm'], GameConst::$minTurnDieOnPrestart)
);
$builder->fillRemainSpecAsZero($env); $builder->fillRemainSpecAsZero($env);
if ($isCentennialAllStar) {
$candidateCities = $db->queryFirstColumn(
'SELECT city FROM city WHERE level >= 5 AND level <= 6 AND nation = 0'
);
if (!$candidateCities) {
$candidateCities = $db->queryFirstColumn(
'SELECT city FROM city WHERE level >= 5 AND level <= 6'
);
}
if (!$candidateCities) {
Json::die([
'result' => false,
'reason' => '장수를 생성할 소·중성이 없습니다.',
]);
}
$builder->setCityID($rng->choice($candidateCities));
}
$builder->build($env); $builder->build($env);
$generalID = $builder->getGeneralID(); $generalID = $builder->getGeneralID();
if(!$generalID){ if(!$generalID){
@@ -167,4 +243,4 @@ $rootDB->insert('member_log', [
Json::die([ Json::die([
'result'=>true, 'result'=>true,
'reason'=>'success' 'reason'=>'success'
]); ]);
+5 -3
View File
@@ -19,6 +19,7 @@ if(!class_exists('\\sammo\\DB')){
$db = DB::db(); $db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env'); $gameStor = KVStorage::getStorage($db, 'game_env');
$clock = GameClock::fromStorage($gameStor);
if(file_exists(__DIR__.'/.htaccess')){ if(file_exists(__DIR__.'/.htaccess')){
$reserved = $db->queryFirstRow( $reserved = $db->queryFirstRow(
@@ -75,8 +76,9 @@ $admin['maxUserCnt'] = $admin['maxgeneral'];
$admin['npcMode'] = $admin['npcmode']; $admin['npcMode'] = $admin['npcmode'];
$admin['turnTerm'] = $admin['turnterm']; $admin['turnTerm'] = $admin['turnterm'];
$admin['isUnited'] = $admin['isunited']; $admin['isUnited'] = $admin['isunited'];
$admin['starttime'] = substr($admin['opentime'], 5, 11); $admin['isOpen'] = $clock->nowTick() >= Util::toInt($admin['opentime']);
$admin['turntime'] = substr($admin['turntime'], 5, 11); $admin['starttime'] = substr($clock->formatTick(Util::toInt($admin['opentime'])), 5, 11);
$admin['turntime'] = substr($clock->formatTick(Util::toInt($admin['turntime'])), 5, 11);
unset($admin['npcmode']); unset($admin['npcmode']);
unset($admin['maxgeneral']); unset($admin['maxgeneral']);
unset($admin['turnterm']); unset($admin['turnterm']);
@@ -131,4 +133,4 @@ if($general){
Json::die([ Json::die([
'game'=>$admin, 'game'=>$admin,
'me'=>$me?:null 'me'=>$me?:null
]); ]);
+1 -1
View File
@@ -88,7 +88,7 @@ $penaltyInfo = array_merge($memberPenalty['any'] ?? [], $memberPenalty[DB::prefi
$penalty = []; $penalty = [];
foreach ($penaltyInfo as $penaltyKey => $penaltyValue) { foreach ($penaltyInfo as $penaltyKey => $penaltyValue) {
if ($penaltyValue['expire'] ?? 0 > TimeUtil::now()) { if (($penaltyValue['expire'] ?? 0) > TimeUtil::now()) {
$penalty[$penaltyKey] = $penaltyValue['value']; $penalty[$penaltyKey] = $penaltyValue['value'];
} }
} }
+6 -3
View File
@@ -64,6 +64,7 @@ if ($permission < 3) {
function applyNationPolicy($policy, $nationID, $generalName): ?string function applyNationPolicy($policy, $nationID, $generalName): ?string
{ {
$db = DB::db(); $db = DB::db();
$gameNow = GameClock::fromStorage(KVStorage::getStorage($db, 'game_env'))->formatNow();
$nationStor = KVStorage::getStorage($db, $nationID, 'nation_env'); $nationStor = KVStorage::getStorage($db, $nationID, 'nation_env');
$defaultPolicy = AutorunNationPolicy::$defaultPolicy; $defaultPolicy = AutorunNationPolicy::$defaultPolicy;
@@ -139,7 +140,7 @@ function applyNationPolicy($policy, $nationID, $generalName): ?string
$nationPolicyRoot['values'] = $nationPolicy; $nationPolicyRoot['values'] = $nationPolicy;
$nationPolicyRoot['valueSetter'] = $generalName; $nationPolicyRoot['valueSetter'] = $generalName;
$nationPolicyRoot['valueSetTime'] = TimeUtil::now(); $nationPolicyRoot['valueSetTime'] = $gameNow;
$nationStor->npc_nation_policy = $nationPolicyRoot; $nationStor->npc_nation_policy = $nationPolicyRoot;
return null; return null;
} }
@@ -147,6 +148,7 @@ function applyNationPolicy($policy, $nationID, $generalName): ?string
function applyNationPriority($priority, $nationID, $generalName): ?string function applyNationPriority($priority, $nationID, $generalName): ?string
{ {
$db = DB::db(); $db = DB::db();
$gameNow = GameClock::fromStorage(KVStorage::getStorage($db, 'game_env'))->formatNow();
$nationStor = KVStorage::getStorage($db, $nationID, 'nation_env'); $nationStor = KVStorage::getStorage($db, $nationID, 'nation_env');
$nationPolicyRoot = $nationStor->npc_nation_policy; $nationPolicyRoot = $nationStor->npc_nation_policy;
@@ -158,7 +160,7 @@ function applyNationPriority($priority, $nationID, $generalName): ?string
} }
$nationPolicyRoot['priority'] = $priority; $nationPolicyRoot['priority'] = $priority;
$nationPolicyRoot['prioritySetter'] = $generalName; $nationPolicyRoot['prioritySetter'] = $generalName;
$nationPolicyRoot['prioritySetTime'] = TimeUtil::now(); $nationPolicyRoot['prioritySetTime'] = $gameNow;
$nationStor->npc_nation_policy = $nationPolicyRoot; $nationStor->npc_nation_policy = $nationPolicyRoot;
return null; return null;
} }
@@ -166,6 +168,7 @@ function applyNationPriority($priority, $nationID, $generalName): ?string
function applyGeneralPriority($priority, $nationID, $generalName): ?string function applyGeneralPriority($priority, $nationID, $generalName): ?string
{ {
$db = DB::db(); $db = DB::db();
$gameNow = GameClock::fromStorage(KVStorage::getStorage($db, 'game_env'))->formatNow();
$nationStor = KVStorage::getStorage($db, $nationID, 'nation_env'); $nationStor = KVStorage::getStorage($db, $nationID, 'nation_env');
$generalPolicyRoot = $nationStor->npc_general_policy; $generalPolicyRoot = $nationStor->npc_general_policy;
@@ -206,7 +209,7 @@ function applyGeneralPriority($priority, $nationID, $generalName): ?string
$generalPolicyRoot['priority'] = $priority; $generalPolicyRoot['priority'] = $priority;
$generalPolicyRoot['prioritySetter'] = $generalName; $generalPolicyRoot['prioritySetter'] = $generalName;
$generalPolicyRoot['prioritySetTime'] = TimeUtil::now(); $generalPolicyRoot['prioritySetTime'] = $gameNow;
$nationStor->npc_general_policy = $generalPolicyRoot; $nationStor->npc_general_policy = $generalPolicyRoot;
return null; return null;
} }
+2 -1
View File
@@ -77,7 +77,8 @@ $month = $query['month'];
$repeatCnt = $query['repeatCnt']; $repeatCnt = $query['repeatCnt'];
$rawAttacker = $query['attackerGeneral']; $rawAttacker = $query['attackerGeneral'];
$rawAttacker['turntime'] = TimeUtil::now(); $battleClock = GameClock::fromStorage(KVStorage::getStorage(DB::db(), 'game_env'));
$rawAttacker['turntime'] = $battleClock->formatTick($battleClock->nowTick());
$rawAttackerCity = $query['attackerCity']; $rawAttackerCity = $query['attackerCity'];
$rawAttackerNation = $query['attackerNation']; $rawAttackerNation = $query['attackerNation'];
+80 -28
View File
@@ -7,6 +7,7 @@ include "func.php";
WebUtil::requireAJAX(); WebUtil::requireAJAX();
$pick = Util::getPost('pick'); $pick = Util::getPost('pick');
$pictureSource = Util::getPost('picture_source', 'string', 'selected');
if(!$pick){ if(!$pick){
Json::die([ Json::die([
@@ -21,8 +22,8 @@ $db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env'); $gameStor = KVStorage::getStorage($db, 'game_env');
$rootDB = RootDB::db(); $rootDB = RootDB::db();
$oNow = new \DateTimeImmutable(); $clock = GameClock::fromStorage($gameStor);
$now = $oNow->format('Y-m-d H:i:s'); $now = $clock->nowTick();
$generalID = $db->queryFirstField('SELECT no FROM general WHERE owner = %i', $userID); $generalID = $db->queryFirstField('SELECT no FROM general WHERE owner = %i', $userID);
if(!$generalID){ if(!$generalID){
@@ -35,10 +36,20 @@ if(!$generalID){
list( list(
$year, $year,
$month, $month,
$startYear,
$maxgeneral, $maxgeneral,
$npcmode, $npcmode,
$turnterm $turnterm,
) = $gameStor->getValuesAsArray(['year', 'month', 'maxgeneral', 'npcmode', 'turnterm']); $showImgLevel
) = $gameStor->getValuesAsArray([
'year',
'month',
'startyear',
'maxgeneral',
'npcmode',
'turnterm',
'show_img_level',
]);
if($npcmode!=2){ if($npcmode!=2){
Json::die([ Json::die([
@@ -55,7 +66,10 @@ if(!$info){
]); ]);
} }
$ownerInfo = RootDB::db()->queryFirstRow('SELECT `name`,`picture`,`imgsvr` FROM member WHERE `NO`=%i',$userID); $ownerInfo = RootDB::db()->queryFirstRow(
'SELECT `name`,`picture`,`imgsvr`,`grade` FROM member WHERE `NO`=%i',
$userID
);
if(!$ownerInfo){ if(!$ownerInfo){
Json::die([ Json::die([
'result'=>false, 'result'=>false,
@@ -64,6 +78,27 @@ if(!$ownerInfo){
} }
$info = Json::decode($info); $info = Json::decode($info);
$isCentennialAllStar = CentennialAllStarGrowthService::isActive();
if ($isCentennialAllStar) {
if (!in_array($pictureSource, ['current', 'own', 'selected'], true)) {
Json::die([
'result' => false,
'reason' => '올바르지 않은 전콘 선택입니다.',
]);
}
if ($pictureSource === 'own') {
$canUseOwnPicture = in_array('picture', GameConst::$generalPoolAllowOption, true)
&& $showImgLevel >= 1
&& $ownerInfo['grade'] >= 1
&& $ownerInfo['picture'] !== '';
if (!$canUseOwnPicture) {
Json::die([
'result' => false,
'reason' => '사용할 수 있는 내 전콘이 없습니다.',
]);
}
}
}
$generalObj = General::createObjFromDB($generalID); $generalObj = General::createObjFromDB($generalID);
@@ -101,35 +136,52 @@ $db->update('select_pool',[
'reserved_until'=>null, 'reserved_until'=>null,
], '(owner=%i or reserved_until < %s) AND general_id is NULL', $userID, $now); ], '(owner=%i or reserved_until < %s) AND general_id is NULL', $userID, $now);
if(key_exists('leadership', $info)){ if ($isCentennialAllStar) {
$generalObj->updateVar('leadership', $info['leadership']); CentennialAllStarGrowthService::prepareLegacyUserReselection($generalObj);
$generalObj->updateVar('strength', $info['strength']); CentennialAllStarGrowthService::applyTarget($generalObj, $info, [
$generalObj->updateVar('intel', $info['intel']); 'startyear' => $startYear,
'year' => $year,
'month' => $month,
]);
} else {
if(key_exists('leadership', $info)){
$generalObj->updateVar('leadership', $info['leadership']);
$generalObj->updateVar('strength', $info['strength']);
$generalObj->updateVar('intel', $info['intel']);
}
if(key_exists('dex', $info)){
$generalObj->updateVar('dex1', $info['dex'][0]);
$generalObj->updateVar('dex2', $info['dex'][1]);
$generalObj->updateVar('dex3', $info['dex'][2]);
$generalObj->updateVar('dex4', $info['dex'][3]);
$generalObj->updateVar('dex5', $info['dex'][4]);
}
if(key_exists('ego', $info)){
$generalObj->updateVar('personal', $info['ego']);
}
if(key_exists('specialDomestic', $info)){
$generalObj->updateVar('special', $info['specialDomestic']);
}
if(key_exists('specialWar', $info)){
$generalObj->updateVar('special2', $info['specialWar']);
}
} }
if(key_exists('picture', $info)){ if ($isCentennialAllStar) {
if ($pictureSource === 'own') {
$generalObj->updateVar('imgsvr', $ownerInfo['imgsvr']);
$generalObj->updateVar('picture', $ownerInfo['picture']);
} elseif ($pictureSource === 'selected' && key_exists('picture', $info)) {
$generalObj->updateVar('imgsvr', $info['imgsvr']);
$generalObj->updateVar('picture', $info['picture']);
}
} elseif(key_exists('picture', $info)){
$generalObj->updateVar('imgsvr', $info['imgsvr']); $generalObj->updateVar('imgsvr', $info['imgsvr']);
$generalObj->updateVar('picture', $info['picture']); $generalObj->updateVar('picture', $info['picture']);
} }
if(key_exists('generalName', $info)){ if(key_exists('generalName', $info)){
$generalObj->updateVar('name', $info['generalName']); $generalObj->updateVar('name', $info['generalName']);
} }
if(key_exists('dex', $info)){ $generalObj->setAuxVar('next_change', $now + GameClock::TICKS_PER_TURN * 12);
$generalObj->updateVar('dex1', $info['dex'][0]);
$generalObj->updateVar('dex2', $info['dex'][1]);
$generalObj->updateVar('dex3', $info['dex'][2]);
$generalObj->updateVar('dex4', $info['dex'][3]);
$generalObj->updateVar('dex5', $info['dex'][4]);
}
if(key_exists('ego', $info)){
$generalObj->updateVar('personal', $info['ego']);
}
if(key_exists('specialDomestic', $info)){
$generalObj->updateVar('special', $info['specialDomestic']);
}
if(key_exists('specialWar', $info)){
$generalObj->updateVar('special2', $info['specialWar']);
}
$generalObj->setAuxVar('next_change', TimeUtil::nowAddMinutes(12 * $turnterm));
$userNick = $ownerInfo['name']; $userNick = $ownerInfo['name'];
$generalObj->setVar('owner_name', $userNick); $generalObj->setVar('owner_name', $userNick);
@@ -148,4 +200,4 @@ $generalObj->applyDB($db);
Json::die([ Json::die([
'result'=>true, 'result'=>true,
'reason'=>'success' 'reason'=>'success'
]); ]);
+3 -2
View File
@@ -51,8 +51,9 @@ function MessageBox($str)
function LogText($prefix, $variable) function LogText($prefix, $variable)
{ {
$text = sprintf('%s : %s'."\r\n", $prefix, TVarDumper::dump($variable)); return;
file_put_contents(ROOT.'/d_log/'.UniqueConst::$serverName.'_dbg_logs.txt', $text, FILE_APPEND); //$text = sprintf('%s : %s'."\r\n", $prefix, TVarDumper::dump($variable));
//file_put_contents(ROOT.'/d_log/'.UniqueConst::$serverName.'_dbg_logs.txt', $text, FILE_APPEND);
} }
function prepareDir(string $dirPath, bool $forceCreate=true):bool{ function prepareDir(string $dirPath, bool $forceCreate=true):bool{
+11 -1
View File
@@ -8,4 +8,14 @@ $session = Session::getInstance()->setReadOnly();
$db = DB::db(); $db = DB::db();
TurnExecutionHelper::executeAllCommand(); $updated = false;
$locked = false;
$lastExecuted = TurnExecutionHelper::executeAllCommand($updated, $locked);
$clock = GameClock::fromStorage(KVStorage::getStorage($db, 'game_env'));
Json::die([
'result' => true,
'updated' => $updated,
'locked' => $locked,
'lastExecutedTick' => $lastExecuted,
'lastExecuted' => $clock->formatTick($lastExecuted, true),
]);
+3 -3
View File
@@ -526,7 +526,7 @@ function DeleteConflict($nation)
function getConquerNation($city): int function getConquerNation($city): int
{ {
$conflict = Json::decode($city['conflict']); $conflict = Json::decode($city['conflict']);
return Util::array_first_key($conflict); return array_key_first($conflict);
} }
function ConquerCity(array $admin, General $general, array $city, array $defenderCityGeneralList) function ConquerCity(array $admin, General $general, array $city, array $defenderCityGeneralList)
@@ -785,8 +785,8 @@ function ConquerCity(array $admin, General $general, array $city, array $defende
'officer_set' => 0, 'officer_set' => 0,
]; ];
if ($city['level'] > 3) { if ($city['level'] > 3) {
$query['def'] = 1000; $query['def'] = GameConst::$defaultCityWall;
$query['wall'] = 1000; $query['wall'] = GameConst::$defaultCityWall;
} else { } else {
$query['def'] = $db->sqleval('def_max/2'); $query['def'] = $db->sqleval('def_max/2');
$query['wall'] = $db->sqleval('wall_max/2'); $query['wall'] = $db->sqleval('wall_max/2');
+2 -2
View File
@@ -21,7 +21,7 @@ class BidUniqueAuction extends \sammo\BaseAPI
]) ])
->rule('int', 'amount') ->rule('int', 'amount')
->rule('int', 'auctionID') ->rule('int', 'auctionID')
->rule('boolean', 'extendCloseDate'); ->rule('boolean', 'extendCloseTick');
if (!$v->validate()) { if (!$v->validate()) {
return $v->errorStr(); return $v->errorStr();
@@ -38,7 +38,7 @@ class BidUniqueAuction extends \sammo\BaseAPI
{ {
$auctionID = $this->args['auctionID']; $auctionID = $this->args['auctionID'];
$amount = $this->args['amount']; $amount = $this->args['amount'];
$tryExtendCloseDate = $this->args['extendCloseDate'] ?? false; $tryExtendCloseDate = $this->args['extendCloseTick'] ?? false;
$generalID = $session->generalID; $generalID = $session->generalID;
$general = General::createObjFromDB($generalID); $general = General::createObjFromDB($generalID);
@@ -15,6 +15,8 @@ use sammo\General;
use sammo\Json; use sammo\Json;
use sammo\TimeUtil; use sammo\TimeUtil;
use sammo\Util; use sammo\Util;
use sammo\GameClock;
use sammo\KVStorage;
use function sammo\getAuctionLogRecent; use function sammo\getAuctionLogRecent;
@@ -33,12 +35,13 @@ class GetActiveResourceAuctionList extends \sammo\BaseAPI
public function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag): null | string | array | APIRecoveryType public function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag): null | string | array | APIRecoveryType
{ {
$db = DB::db(); $db = DB::db();
$clock = GameClock::fromStorage(KVStorage::getStorage($db, 'game_env'));
$buyRiceList = []; $buyRiceList = [];
$sellRiceList = []; $sellRiceList = [];
/** @var AuctionInfo[] */ /** @var AuctionInfo[] */
$auctions = array_map(fn ($raw) => AuctionInfo::fromArray($raw), $db->query( $auctions = array_map(fn ($raw) => AuctionInfo::fromArray($raw), $db->query(
'SELECT * FROM `ng_auction` WHERE `type` IN %ls AND `finished` = 0 ORDER BY `close_date` ASC', 'SELECT * FROM `ng_auction` WHERE `type` IN %ls AND `finished` = 0 ORDER BY `close_tick` ASC',
[ [
AuctionType::BuyRice->value, AuctionType::BuyRice->value,
AuctionType::SellRice->value, AuctionType::SellRice->value,
@@ -87,8 +90,8 @@ class GetActiveResourceAuctionList extends \sammo\BaseAPI
'type' => $auction->type->value, 'type' => $auction->type->value,
'hostGeneralID' => $auction->hostGeneralID, 'hostGeneralID' => $auction->hostGeneralID,
'hostName' => $auction->detail->hostName, 'hostName' => $auction->detail->hostName,
'openDate' => TimeUtil::format($auction->openDate, false), 'openDate' => $clock->formatTick($auction->openTick),
'closeDate' => TimeUtil::format($auction->closeDate, false), 'closeDate' => $clock->formatTick($auction->closeTick),
'amount' => $auction->detail->amount, 'amount' => $auction->detail->amount,
'startBidAmount' => $auction->detail->startBidAmount, 'startBidAmount' => $auction->detail->startBidAmount,
'finishBidAmount' => $auction->detail->finishBidAmount, 'finishBidAmount' => $auction->detail->finishBidAmount,
@@ -14,6 +14,8 @@ use sammo\Enums\GeneralQueryMode;
use sammo\Enums\InheritanceKey; use sammo\Enums\InheritanceKey;
use sammo\InheritancePointManager; use sammo\InheritancePointManager;
use sammo\TimeUtil; use sammo\TimeUtil;
use sammo\GameClock;
use sammo\KVStorage;
use sammo\Validator; use sammo\Validator;
use sammo\General; use sammo\General;
@@ -42,6 +44,7 @@ class GetUniqueItemAuctionDetail extends \sammo\BaseAPI
public function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag): null | string | array | APIRecoveryType public function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag): null | string | array | APIRecoveryType
{ {
$db = DB::db(); $db = DB::db();
$clock = GameClock::fromStorage(KVStorage::getStorage($db, 'game_env'));
$generalID = $session->generalID; $generalID = $session->generalID;
$auctionID = $this->args['auctionID']; $auctionID = $this->args['auctionID'];
@@ -92,9 +95,11 @@ class GetUniqueItemAuctionDetail extends \sammo\BaseAPI
'target' => $auction->target, 'target' => $auction->target,
'isCallerHost' => $auction->hostGeneralID === $generalID, 'isCallerHost' => $auction->hostGeneralID === $generalID,
'hostName' => $auction->detail->hostName, 'hostName' => $auction->detail->hostName,
'closeDate' => TimeUtil::format($auction->closeDate, false), 'closeDate' => $clock->formatTick($auction->closeTick),
'remainCloseDateExtensionCnt' => $auction->detail->remainCloseDateExtensionCnt, 'remainCloseDateExtensionCnt' => $auction->detail->remainCloseDateExtensionCnt,
'availableLatestBidCloseDate' => TimeUtil::format($auction->detail->availableLatestBidCloseDate, false), 'availableLatestBidCloseDate' => $auction->detail->availableLatestBidCloseTick === null
? null
: $clock->formatTick($auction->detail->availableLatestBidCloseTick),
], ],
'bidList' => $responseBid, 'bidList' => $responseBid,
'obfuscatedName' => $obfuscatedName, 'obfuscatedName' => $obfuscatedName,
@@ -12,6 +12,8 @@ use sammo\Enums\APIRecoveryType;
use sammo\Enums\AuctionType; use sammo\Enums\AuctionType;
use sammo\TimeUtil; use sammo\TimeUtil;
use sammo\Util; use sammo\Util;
use sammo\GameClock;
use sammo\KVStorage;
class GetUniqueItemAuctionList extends \sammo\BaseAPI class GetUniqueItemAuctionList extends \sammo\BaseAPI
{ {
@@ -28,12 +30,13 @@ class GetUniqueItemAuctionList extends \sammo\BaseAPI
public function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag): null | string | array | APIRecoveryType public function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag): null | string | array | APIRecoveryType
{ {
$db = DB::db(); $db = DB::db();
$clock = GameClock::fromStorage(KVStorage::getStorage($db, 'game_env'));
$generalID = $session->generalID; $generalID = $session->generalID;
/** @var AuctionInfo[] */ /** @var AuctionInfo[] */
$auctions = array_map(fn($raw)=>AuctionInfo::fromArray($raw), $db->query( $auctions = array_map(fn($raw)=>AuctionInfo::fromArray($raw), $db->query(
'SELECT * FROM `ng_auction` WHERE `type` = %s ORDER BY `close_date` ASC', 'SELECT * FROM `ng_auction` WHERE `type` = %s ORDER BY `close_tick` ASC',
AuctionType::UniqueItem->value AuctionType::UniqueItem->value
) ?? []); ) ?? []);
@@ -85,9 +88,11 @@ class GetUniqueItemAuctionList extends \sammo\BaseAPI
'target' => $auction->target, 'target' => $auction->target,
'isCallerHost' => $auction->hostGeneralID === $generalID, 'isCallerHost' => $auction->hostGeneralID === $generalID,
'hostName' => $auction->detail->hostName, 'hostName' => $auction->detail->hostName,
'closeDate' => TimeUtil::format($auction->closeDate, false), 'closeDate' => $clock->formatTick($auction->closeTick),
'remainCloseDateExtensionCnt' => $auction->detail->remainCloseDateExtensionCnt, 'remainCloseDateExtensionCnt' => $auction->detail->remainCloseDateExtensionCnt,
'availableLatestBidCloseDate' => TimeUtil::format($auction->detail->availableLatestBidCloseDate, false), 'availableLatestBidCloseDate' => $auction->detail->availableLatestBidCloseTick === null
? null
: $clock->formatTick($auction->detail->availableLatestBidCloseTick),
'highestBid' => [ 'highestBid' => [
'generalName' => $highestBid->aux->generalName, 'generalName' => $highestBid->aux->generalName,
'amount' => $highestBid->amount, 'amount' => $highestBid->amount,
+7 -3
View File
@@ -7,9 +7,10 @@ use DateTimeInterface;
use sammo\DB; use sammo\DB;
use sammo\Enums\APIRecoveryType; use sammo\Enums\APIRecoveryType;
use sammo\GameConst; use sammo\GameConst;
use sammo\GameClock;
use sammo\Json; use sammo\Json;
use sammo\KVStorage; use sammo\KVStorage;
use sammo\TimeUtil; use sammo\Util;
use function sammo\cutTurn; use function sammo\cutTurn;
@@ -31,6 +32,7 @@ class GetReservedCommand extends \sammo\BaseAPI
$commandList = []; $commandList = [];
$gameStor = KVStorage::getStorage($db, 'game_env'); $gameStor = KVStorage::getStorage($db, 'game_env');
$clock = GameClock::fromStorage($gameStor);
$generalID = $session->generalID; $generalID = $session->generalID;
$invalidTurnList = 0; $invalidTurnList = 0;
@@ -82,11 +84,13 @@ class GetReservedCommand extends \sammo\BaseAPI
return [ return [
'result' => true, 'result' => true,
'turnTime' => $turnTime, 'turnTimeTick' => Util::toInt($turnTime),
'turnTime' => $clock->formatTick(Util::toInt($turnTime)),
'turnTerm' => $turnTerm, 'turnTerm' => $turnTerm,
'year' => $year, 'year' => $year,
'month' => $month, 'month' => $month,
'date' => TimeUtil::now(true), 'date' => $clock->formatTick($clock->nowTick(), true),
'clockMode' => $clock->getMode(),
'turn' => $commandList, 'turn' => $commandList,
'autorun_limit' => $generalAux['autorun_limit'] ?? null, 'autorun_limit' => $generalAux['autorun_limit'] ?? null,
]; ];
+24 -11
View File
@@ -11,7 +11,8 @@ use sammo\Session;
use sammo\General; use sammo\General;
use sammo\JosaUtil; use sammo\JosaUtil;
use sammo\KVStorage; use sammo\KVStorage;
use sammo\TimeUtil; use sammo\GameClock;
use sammo\Util;
use function sammo\addTurn; use function sammo\addTurn;
use function sammo\increaseRefresh; use function sammo\increaseRefresh;
@@ -37,8 +38,14 @@ class DieOnPrestart extends \sammo\BaseAPI
$db = DB::db(); $db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env'); $gameStor = KVStorage::getStorage($db, 'game_env');
$gameStor->cacheValues(['turnterm', 'opentime', 'turntime', 'year', 'month']); $gameStor->cacheValues(['turnterm', 'opentime', 'turntime', 'year', 'month']);
$clock = GameClock::fromStorage($gameStor);
$nowTick = $clock->nowTick();
$general = $db->queryFirstRow('SELECT no,name,nation,owner_name,npc FROM general WHERE owner=%i AND npc = 0', $userID); $general = $db->queryFirstRow('SELECT no,name,nation,owner_name,npc FROM general WHERE owner=%i AND npc = 0', $userID);
if (!$general) {
return '장수가 없습니다';
}
$lastRefresh = $db->queryFirstField( $lastRefresh = $db->queryFirstField(
'SELECT %b FROM general_access_log WHERE %b = %i', 'SELECT %b FROM general_access_log WHERE %b = %i',
GeneralAccessLogColumn::lastRefresh->value, GeneralAccessLogColumn::lastRefresh->value,
@@ -46,8 +53,9 @@ class DieOnPrestart extends \sammo\BaseAPI
$general['no'] $general['no']
); );
if (!$general) { $generalObj = General::createObjFromDB($general['no']);
return '장수가 없습니다'; if ($generalObj instanceof DummyGeneral) {
trigger_error("올바르지 않은 삭제 프로세스 $userID", E_USER_WARNING);
} }
increaseRefresh("장수 삭제", 1); increaseRefresh("장수 삭제", 1);
@@ -61,16 +69,21 @@ class DieOnPrestart extends \sammo\BaseAPI
return '이미 국가에 소속되어있습니다.'; return '이미 국가에 소속되어있습니다.';
} }
//서버 가오픈시 할 수 있는 행동 $targetTime = $generalObj->getAuxVar('prestart_delete_after');
$targetTime = addTurn($lastRefresh, $gameStor->turnterm, GameConst::$minTurnDieOnPrestart); if (!is_int($targetTime)) {
if ($targetTime > TimeUtil::now()) { $targetTime = addTurn(
$targetTimeShort = substr($targetTime, 0, 19); $lastRefresh === null ? $nowTick : Util::toInt($lastRefresh),
return "아직 삭제할 수 없습니다. {$targetTimeShort} 부터 가능합니다."; $gameStor->turnterm,
GameConst::$minTurnDieOnPrestart
);
$generalObj->setAuxVar('prestart_delete_after', $targetTime);
$generalObj->applyDB($db);
} }
$generalObj = General::createObjFromDB($general['no']); //서버 가오픈시 할 수 있는 행동
if ($generalObj instanceof DummyGeneral) { if ($targetTime > $nowTick) {
trigger_error("올바르지 않은 삭제 프로세스 $userID", E_USER_WARNING); $targetTimeShort = $clock->formatTick($targetTime);
return "아직 삭제할 수 없습니다. {$targetTimeShort} 부터 가능합니다.";
} }
$generalName = $generalObj->getName(); $generalName = $generalObj->getName();
+18 -8
View File
@@ -14,13 +14,13 @@ use sammo\Enums\GeneralColumn;
use sammo\Enums\GeneralQueryMode; use sammo\Enums\GeneralQueryMode;
use sammo\Enums\RankColumn; use sammo\Enums\RankColumn;
use sammo\GameConst; use sammo\GameConst;
use sammo\GameClock;
use sammo\General; use sammo\General;
use sammo\KVStorage; use sammo\KVStorage;
use sammo\LastTurn; use sammo\LastTurn;
use sammo\Validator; use sammo\Validator;
use sammo\Session; use sammo\Session;
use sammo\TimeUtil;
use sammo\Util; use sammo\Util;
use function sammo\buildNationCommandClass; use function sammo\buildNationCommandClass;
@@ -105,6 +105,7 @@ class GetFrontInfo extends \sammo\BaseAPI
$db = DB::db(); $db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env'); $gameStor = KVStorage::getStorage($db, 'game_env');
$clock = GameClock::fromStorage($gameStor);
$gameStor->cacheValues(['isunited', 'opentime', 'refresh']); $gameStor->cacheValues(['isunited', 'opentime', 'refresh']);
$lastHistoryID = $this->args['lastWorldHistoryID']; $lastHistoryID = $this->args['lastWorldHistoryID'];
@@ -158,6 +159,7 @@ class GetFrontInfo extends \sammo\BaseAPI
private function generateGlobalInfo(MeekroDB $db): array private function generateGlobalInfo(MeekroDB $db): array
{ {
$gameStor = KVStorage::getStorage($db, 'game_env'); $gameStor = KVStorage::getStorage($db, 'game_env');
$clock = GameClock::fromStorage($gameStor);
[ [
$scenarioText, $extendedGeneral, $isFiction, $npcMode, $scenarioText, $extendedGeneral, $isFiction, $npcMode,
@@ -182,8 +184,9 @@ class GetFrontInfo extends \sammo\BaseAPI
$lastVote = null; $lastVote = null;
if ($lastVoteID) { if ($lastVoteID) {
$voteStor = KVStorage::getStorage($db, 'vote'); $voteStor = KVStorage::getStorage($db, 'vote');
$lastVote = VoteInfo::fromArray($voteStor->getValue("vote_{$lastVoteID}")); $rawLastVote = VoteInfo::normalizeGameStorage($voteStor->getValue("vote_{$lastVoteID}"), $clock);
if ($lastVote->endDate && $lastVote->endDate < TimeUtil::now()) { $lastVote = VoteInfo::fromGameStorage($rawLastVote, $clock);
if ($rawLastVote['endTick'] !== null && $rawLastVote['endTick'] < $clock->nowTick()) {
$lastVote = null; $lastVote = null;
} }
} }
@@ -210,7 +213,8 @@ class GetFrontInfo extends \sammo\BaseAPI
'month' => $month, 'month' => $month,
'autorunUser' => $autorunUser, 'autorunUser' => $autorunUser,
'turnterm' => $turnterm, 'turnterm' => $turnterm,
'lastExecuted' => $lastExecuted, 'lastExecutedTick' => $lastExecuted,
'lastExecuted' => $clock->formatTick(Util::toInt($lastExecuted), true),
'lastVoteID' => $lastVoteID, 'lastVoteID' => $lastVoteID,
'develCost' => $develCost, 'develCost' => $develCost,
'noticeMsg' => $noticeMsg, 'noticeMsg' => $noticeMsg,
@@ -224,7 +228,8 @@ class GetFrontInfo extends \sammo\BaseAPI
'isLocked' => $isLocked, 'isLocked' => $isLocked,
'tournamentType' => $tournamentType, 'tournamentType' => $tournamentType,
'tournamentState' => $tournamentState, 'tournamentState' => $tournamentState,
'tournamentTime' => $tournamentTime, 'tournamentTimeTick' => $tournamentTime,
'tournamentTime' => $tournamentTime === null ? null : $clock->formatTick(Util::toInt($tournamentTime)),
'genCount' => $globalGenCount, 'genCount' => $globalGenCount,
'generalCntLimit' => $generalCntLimit, 'generalCntLimit' => $generalCntLimit,
'serverCnt' => $serverCnt, 'serverCnt' => $serverCnt,
@@ -361,6 +366,7 @@ class GetFrontInfo extends \sammo\BaseAPI
public function generateGeneralInfo(MeekroDB $db, General $general, array $rawNation): array public function generateGeneralInfo(MeekroDB $db, General $general, array $rawNation): array
{ {
$clock = GameClock::fromStorage(KVStorage::getStorage($db, 'game_env'));
$permission = checkSecretPermission($general->getRaw()); $permission = checkSecretPermission($general->getRaw());
@@ -425,8 +431,12 @@ class GetFrontInfo extends \sammo\BaseAPI
'crew' => $general->getVar(GeneralColumn::crew), // number; 'crew' => $general->getVar(GeneralColumn::crew), // number;
'train' => $general->getVar(GeneralColumn::train), // number; 'train' => $general->getVar(GeneralColumn::train), // number;
'atmos' => $general->getVar(GeneralColumn::atmos), // number; 'atmos' => $general->getVar(GeneralColumn::atmos), // number;
'turntime' => $general->getVar(GeneralColumn::turntime), // string; 'turntimeTick' => $general->getTurnTick(), // number;
'recent_war' => $general->getVar(GeneralColumn::recent_war), // string; 'turntime' => $general->getTurnTime(), // string;
'recent_war_tick' => $general->getVar(GeneralColumn::recent_war), // number|null;
'recent_war' => $general->getVar(GeneralColumn::recent_war) === null
? null
: $clock->formatTick(Util::toInt($general->getVar(GeneralColumn::recent_war)), true), // string|null;
'horse' => $general->getVar(GeneralColumn::horse), // GameObjClassKey; 'horse' => $general->getVar(GeneralColumn::horse), // GameObjClassKey;
'weapon' => $general->getVar(GeneralColumn::weapon), // GameObjClassKey; 'weapon' => $general->getVar(GeneralColumn::weapon), // GameObjClassKey;
'book' => $general->getVar(GeneralColumn::book), // GameObjClassKey; 'book' => $general->getVar(GeneralColumn::book), // GameObjClassKey;
@@ -544,7 +554,7 @@ class GetFrontInfo extends \sammo\BaseAPI
'result' => false, 'result' => false,
'reason' => '접속 제한중입니다.', 'reason' => '접속 제한중입니다.',
'recovery' => APIRecoveryType::GameQuota, 'recovery' => APIRecoveryType::GameQuota,
'recovery_arg' => $general->getVar('turntime'), 'recovery_arg' => $general->getTurnTime(),
]; ];
} }
+17 -11
View File
@@ -11,6 +11,7 @@ use sammo\Enums\APIRecoveryType;
use sammo\Enums\GeneralAccessLogColumn; use sammo\Enums\GeneralAccessLogColumn;
use sammo\Enums\RankColumn; use sammo\Enums\RankColumn;
use sammo\GameConst; use sammo\GameConst;
use sammo\GameClock;
use sammo\GameUnitConst; use sammo\GameUnitConst;
use sammo\General; use sammo\General;
use sammo\InheritancePointManager; use sammo\InheritancePointManager;
@@ -99,7 +100,7 @@ class Join extends \sammo\BaseAPI
} }
$gameID = UniqueConst::$serverID; $gameID = UniqueConst::$serverID;
$alreadyJoined = $db->queryFirstField('SELECT count(*) FROM `storage` WHERE `namespace` = %s AND `key` LIKE %s', 'inheritance_result', "{$gameID}_{$userID}_%"); $alreadyJoined = $db->queryFirstField('SELECT COUNT(*) FROM `inheritance_result` WHERE `server_id` = %s AND `owner` = %i', $gameID, $userID);
if ($alreadyJoined) { if ($alreadyJoined) {
//이미 받았음 //이미 받았음
return 0; return 0;
@@ -157,7 +158,7 @@ class Join extends \sammo\BaseAPI
$penalty = []; $penalty = [];
foreach($penaltyInfo as $penaltyKey => $penaltyValue){ foreach($penaltyInfo as $penaltyKey => $penaltyValue){
if($penaltyValue['expire'] ?? 0 > TimeUtil::now()){ if(($penaltyValue['expire'] ?? 0) > TimeUtil::now()){
$penalty[$penaltyKey] = $penaltyValue['value']; $penalty[$penaltyKey] = $penaltyValue['value'];
} }
} }
@@ -165,6 +166,7 @@ class Join extends \sammo\BaseAPI
$db = DB::db(); $db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env'); $gameStor = KVStorage::getStorage($db, 'game_env');
$gameStor->cacheValues(['year', 'month', 'maxgeneral', 'scenario', 'show_img_level', 'block_general_create', 'turnterm', 'turntime', 'genius', 'npcmode']); $gameStor->cacheValues(['year', 'month', 'maxgeneral', 'scenario', 'show_img_level', 'block_general_create', 'turnterm', 'turntime', 'genius', 'npcmode']);
$clock = GameClock::fromStorage($gameStor);
########## 동일 정보 존재여부 확인. ########## ########## 동일 정보 존재여부 확인. ##########
$block_general_create = $gameStor->getValue('block_general_create'); $block_general_create = $gameStor->getValue('block_general_create');
@@ -222,7 +224,7 @@ class Join extends \sammo\BaseAPI
$userLogger = new UserLogger($userID, $admin['year'], $admin['month'], false); $userLogger = new UserLogger($userID, $admin['year'], $admin['month'], false);
$now = TimeUtil::now(false); $now = $clock->nowTick();
$rng = new RandUtil(new LiteHashDRBG(Util::simpleSerialize( $rng = new RandUtil(new LiteHashDRBG(Util::simpleSerialize(
UniqueConst::$hiddenSeed, UniqueConst::$hiddenSeed,
'MakeGeneral', 'MakeGeneral',
@@ -360,17 +362,14 @@ class Join extends \sammo\BaseAPI
$userLogger->push(sprintf("턴 시간 %02d:%02d 로 지정", intdiv($inheritTurntime, 60), $inheritTurntime % 60), "inheritPoint"); $userLogger->push(sprintf("턴 시간 %02d:%02d 로 지정", intdiv($inheritTurntime, 60), $inheritTurntime % 60), "inheritPoint");
$inheritTurntime += $rng->nextRangeInt(0, 999999) / 1000000; $inheritTurnMicrosecond = $rng->nextRangeInt(0, 999999);
$turntime = cutTurn(Util::toInt($admin['turntime']), $admin['turnterm'])
$turntime = new \DateTimeImmutable(cutTurn($admin['turntime'], $admin['turnterm'])); + $clock->ticksFromSeconds($inheritTurntime)
$turntime = $turntime->add(TimeUtil::secondsToDateInterval($inheritTurntime)); + intdiv($inheritTurnMicrosecond * $clock->ticksPerSecond(), 1_000_000);
$turntime = TimeUtil::format($turntime, true);
} else { } else {
$turntime = getRandTurn($rng, $admin['turnterm'], new \DateTimeImmutable($admin['turntime'])); $turntime = getRandTurn($rng, $admin['turnterm'], Util::toInt($admin['turntime']));
} }
$now = TimeUtil::now(true);
if ($now >= $turntime) { if ($now >= $turntime) {
$turntime = addTurn($turntime, $admin['turnterm']); $turntime = addTurn($turntime, $admin['turnterm']);
} }
@@ -435,6 +434,13 @@ class Join extends \sammo\BaseAPI
'specage2' => $specage2, 'specage2' => $specage2,
'special2' => $special2, 'special2' => $special2,
'penalty' => Json::encode($penalty), 'penalty' => Json::encode($penalty),
'aux' => Json::encode([
'prestart_delete_after' => addTurn(
$now,
$admin['turnterm'],
GameConst::$minTurnDieOnPrestart
),
]),
]); ]);
$generalID = $db->insertId(); $generalID = $db->insertId();
$db->insert('general_access_log', [ $db->insert('general_access_log', [
+5 -1
View File
@@ -8,6 +8,8 @@ use DateTimeInterface;
use sammo\Enums\APIRecoveryType; use sammo\Enums\APIRecoveryType;
use sammo\TurnExecutionHelper; use sammo\TurnExecutionHelper;
use sammo\UniqueConst; use sammo\UniqueConst;
use sammo\GameClock;
use sammo\KVStorage;
class ExecuteEngine extends \sammo\BaseAPI class ExecuteEngine extends \sammo\BaseAPI
{ {
@@ -35,11 +37,13 @@ class ExecuteEngine extends \sammo\BaseAPI
$updated = false; $updated = false;
$locked = false; $locked = false;
$lastExecuted = TurnExecutionHelper::executeAllCommand($updated, $locked); $lastExecuted = TurnExecutionHelper::executeAllCommand($updated, $locked);
$clock = GameClock::fromStorage(KVStorage::getStorage(DB::db(), 'game_env'));
return [ return [
'result' => true, 'result' => true,
'updated' => $updated, 'updated' => $updated,
'locked' => $locked, 'locked' => $locked,
'lastExecuted' => $lastExecuted, 'lastExecutedTick' => $lastExecuted,
'lastExecuted' => $clock->formatTick($lastExecuted, true),
]; ];
} }
} }
+3 -1
View File
@@ -3,6 +3,7 @@
namespace sammo\API\Global; namespace sammo\API\Global;
use sammo\DB; use sammo\DB;
use sammo\GameClock;
use sammo\Enums\APIRecoveryType; use sammo\Enums\APIRecoveryType;
use sammo\Json; use sammo\Json;
use sammo\KVStorage; use sammo\KVStorage;
@@ -145,7 +146,8 @@ class GeneralList extends \sammo\BaseAPI
if (static::$withToken) { if (static::$withToken) {
$now = (new \DateTimeImmutable())->format('Y-m-d H:i:s'); $gameStor = KVStorage::getStorage($db, 'game_env');
$now = GameClock::fromStorage($gameStor)->nowTick();
$tokens = []; $tokens = [];
foreach ($db->query('SELECT * FROM select_npc_token WHERE `valid_until`>=%s', $now) as $token) { foreach ($db->query('SELECT * FROM select_npc_token WHERE `valid_until`>=%s', $now) as $token) {
$validUntil = $token['valid_until']; $validUntil = $token['valid_until'];
@@ -10,7 +10,6 @@ use sammo\Enums\RankColumn;
use sammo\GameConst; use sammo\GameConst;
use sammo\General; use sammo\General;
use sammo\KVStorage; use sammo\KVStorage;
use sammo\TimeUtil;
use sammo\UserLogger; use sammo\UserLogger;
class BuyRandomUnique extends \sammo\BaseAPI class BuyRandomUnique extends \sammo\BaseAPI
@@ -56,7 +55,7 @@ class BuyRandomUnique extends \sammo\BaseAPI
$userLogger->push("{$reqAmount} 포인트로 랜덤 유니크 구입", "inheritPoint"); $userLogger->push("{$reqAmount} 포인트로 랜덤 유니크 구입", "inheritPoint");
$userLogger->flush(); $userLogger->flush();
$general->setAuxVar('inheritRandomUnique', TimeUtil::now()); $general->setAuxVar('inheritRandomUnique', true);
$inheritStor->setValue('previous', [$previousPoint - $reqAmount, null]); $inheritStor->setValue('previous', [$previousPoint - $reqAmount, null]);
$general->increaseRankVar(RankColumn::inherit_point_spent_dynamic, $reqAmount); $general->increaseRankVar(RankColumn::inherit_point_spent_dynamic, $reqAmount);
$general->applyDB($db); $general->applyDB($db);
+2 -2
View File
@@ -116,7 +116,7 @@ class CheckOwner extends \sammo\BaseAPI
$src, $src,
$dest, $dest,
"{$destGeneralName}의 소유자는 {$destGeneralOwnerName} 입니다.", "{$destGeneralName}의 소유자는 {$destGeneralOwnerName} 입니다.",
new \DateTime(), Message::gameNow(),
new \DateTime('9999-12-31'), new \DateTime('9999-12-31'),
[] []
); );
@@ -142,7 +142,7 @@ class CheckOwner extends \sammo\BaseAPI
$src, $src,
$dest, $dest,
"소유자명이 누군가에 의해 확인되었습니다.", "소유자명이 누군가에 의해 확인되었습니다.",
new \DateTime(), Message::gameNow(),
new \DateTime('9999-12-31'), new \DateTime('9999-12-31'),
[] []
); );
@@ -4,6 +4,7 @@ namespace sammo\API\InheritAction;
use sammo\Session; use sammo\Session;
use DateTimeInterface; use DateTimeInterface;
use sammo\CentennialAllStarGrowthService;
use sammo\DB; use sammo\DB;
use sammo\Enums\APIRecoveryType; use sammo\Enums\APIRecoveryType;
use sammo\Enums\RankColumn; use sammo\Enums\RankColumn;
@@ -98,6 +99,10 @@ class ResetStat extends \sammo\BaseAPI
return 'NPC는 능력치 초기화를 할 수 없습니다.'; return 'NPC는 능력치 초기화를 할 수 없습니다.';
} }
if (!CentennialAllStarGrowthService::isStatResetAllowed()) {
return '100기 올스타 장수는 능력치 초기화를 사용할 수 없습니다.';
}
$db = DB::db(); $db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env'); $gameStor = KVStorage::getStorage($db, 'game_env');
@@ -64,13 +64,11 @@ class ResetTurnTime extends \sammo\BaseAPI
$gameStor = KVStorage::getStorage($db, 'game_env'); $gameStor = KVStorage::getStorage($db, 'game_env');
$turnTerm = $gameStor->getValue('turnterm'); $turnTerm = $gameStor->getValue('turnterm');
$currTurnTime = new DateTimeImmutable($general->getTurnTime());
$rng = new RandUtil(new LiteHashDRBG(Util::simpleSerialize( $rng = new RandUtil(new LiteHashDRBG(Util::simpleSerialize(
UniqueConst::$hiddenSeed, UniqueConst::$hiddenSeed,
'ResetTurnTime', 'ResetTurnTime',
$userID, $userID,
$general->getAuxVar('nextTurnTimeBase') ?? $general->getTurnTime() $general->getAuxVar('nextTurnTimeBase') ?? $general->getTurnTick()
))); )));
$afterTurn = $rng->nextFloat1() * $turnTerm * 60; $afterTurn = $rng->nextFloat1() * $turnTerm * 60;
+4 -4
View File
@@ -44,7 +44,7 @@ class SendMessage extends \sammo\BaseAPI
private function genPublicMessage(MessageTarget $src, string $text): Message private function genPublicMessage(MessageTarget $src, string $text): Message
{ {
$now = new \DateTime(); $now = Message::gameNow();
$unlimited = new \DateTime('9999-12-31'); $unlimited = new \DateTime('9999-12-31');
$msg = new Message( $msg = new Message(
@@ -62,7 +62,7 @@ class SendMessage extends \sammo\BaseAPI
private function genNationalMessage(MessageTarget $src, string $text): Message private function genNationalMessage(MessageTarget $src, string $text): Message
{ {
$now = new \DateTime(); $now = Message::gameNow();
$unlimited = new \DateTime('9999-12-31'); $unlimited = new \DateTime('9999-12-31');
$dest = new MessageTarget(0, '', $src->nationID, $src->nationName, $src->color); $dest = new MessageTarget(0, '', $src->nationID, $src->nationName, $src->color);
@@ -82,7 +82,7 @@ class SendMessage extends \sammo\BaseAPI
private function genDiplomacyMessage(MessageTarget $src, int $destNationID, string $text): Message|string private function genDiplomacyMessage(MessageTarget $src, int $destNationID, string $text): Message|string
{ {
$now = new \DateTime(); $now = Message::gameNow();
$unlimited = new \DateTime('9999-12-31'); $unlimited = new \DateTime('9999-12-31');
$destNation = getNationStaticInfo($destNationID); $destNation = getNationStaticInfo($destNationID);
@@ -107,7 +107,7 @@ class SendMessage extends \sammo\BaseAPI
private function genPrivateMessage(MessageTarget $src, int $destGeneralID, int $permission, string $text): Message|string private function genPrivateMessage(MessageTarget $src, int $destGeneralID, int $permission, string $text): Message|string
{ {
$now = new \DateTime(); $now = Message::gameNow();
$unlimited = new \DateTime('9999-12-31'); $unlimited = new \DateTime('9999-12-31');
$db = DB::db(); $db = DB::db();
+6 -2
View File
@@ -8,6 +8,7 @@ use sammo\Enums\APIRecoveryType;
use sammo\Enums\GeneralLiteQueryMode; use sammo\Enums\GeneralLiteQueryMode;
use sammo\Enums\GeneralQueryMode; use sammo\Enums\GeneralQueryMode;
use sammo\General; use sammo\General;
use sammo\GameClock;
use sammo\GeneralLite; use sammo\GeneralLite;
use sammo\Session; use sammo\Session;
use sammo\Util; use sammo\Util;
@@ -264,8 +265,11 @@ class GeneralList extends \sammo\BaseAPI
'honorText' => fn ($rawGeneral) => getHonor($rawGeneral['experience']), 'honorText' => fn ($rawGeneral) => getHonor($rawGeneral['experience']),
'dedLevelText' => fn ($rawGeneral) => getDedLevelText($rawGeneral['dedlevel']), 'dedLevelText' => fn ($rawGeneral) => getDedLevelText($rawGeneral['dedlevel']),
//'0000-00-00 11:23'; //'0000-00-00 11:23';
'turntime' => fn ($rawGeneral) => substr($rawGeneral['turntime'], 0, 19), 'turntime' => fn ($rawGeneral) => GameClock::fromStorage($gameStor)
'recent_war' => fn ($rawGeneral) => substr($rawGeneral['recent_war'], 0, 19), ->formatTick(Util::toInt($rawGeneral['turntime'])),
'recent_war' => fn ($rawGeneral) => $rawGeneral['recent_war'] === null
? null
: GameClock::fromStorage($gameStor)->formatTick(Util::toInt($rawGeneral['recent_war'])),
'bill' => fn ($rawGeneral) => getBillByLevel($rawGeneral['dedlevel']), 'bill' => fn ($rawGeneral) => getBillByLevel($rawGeneral['dedlevel']),
'reservedCommand' => fn ($rawGeneral) => $reservedCommand[$rawGeneral['no']] ?? null, 'reservedCommand' => fn ($rawGeneral) => $reservedCommand[$rawGeneral['no']] ?? null,
'autorun_limit' => fn ($rawGeneral) => ($rawGeneral['aux'] ?? [])['autorun_limit'] ?? 0, 'autorun_limit' => fn ($rawGeneral) => ($rawGeneral['aux'] ?? [])['autorun_limit'] ?? 0,
+3 -2
View File
@@ -6,8 +6,8 @@ use sammo\Session;
use DateTimeInterface; use DateTimeInterface;
use sammo\DB; use sammo\DB;
use sammo\Enums\APIRecoveryType; use sammo\Enums\APIRecoveryType;
use sammo\GameClock;
use sammo\KVStorage; use sammo\KVStorage;
use sammo\TimeUtil;
use sammo\Validator; use sammo\Validator;
use sammo\WebUtil; use sammo\WebUtil;
@@ -51,8 +51,9 @@ class SetNotice extends \sammo\BaseAPI
$nationID = $me['nation']; $nationID = $me['nation'];
$nationStor = KVStorage::getStorage($db, $nationID, 'nation_env'); $nationStor = KVStorage::getStorage($db, $nationID, 'nation_env');
$gameNow = GameClock::fromStorage(KVStorage::getStorage($db, 'game_env'))->formatNow();
$nationStor->nationNotice = [ $nationStor->nationNotice = [
'date'=>TimeUtil::now(), 'date'=>$gameNow,
'msg'=>WebUtil::htmlPurify($msg), 'msg'=>WebUtil::htmlPurify($msg),
'author'=>$me['name'], 'author'=>$me['name'],
'authorID'=>$me['no'], 'authorID'=>$me['no'],
@@ -8,10 +8,10 @@ use sammo\DB;
use sammo\Enums\APIRecoveryType; use sammo\Enums\APIRecoveryType;
use sammo\Enums\GeneralQueryMode; use sammo\Enums\GeneralQueryMode;
use sammo\GameConst; use sammo\GameConst;
use sammo\GameClock;
use sammo\General; use sammo\General;
use sammo\Json; use sammo\Json;
use sammo\KVStorage; use sammo\KVStorage;
use sammo\TimeUtil;
use sammo\Util; use sammo\Util;
use function sammo\checkLimit; use function sammo\checkLimit;
@@ -39,6 +39,7 @@ class GetReservedCommand extends \sammo\BaseAPI
increaseRefresh("사령부", 1); increaseRefresh("사령부", 1);
$gameStor = KVStorage::getStorage($db, 'game_env'); $gameStor = KVStorage::getStorage($db, 'game_env');
$clock = GameClock::fromStorage($gameStor);
$userID = $session->userID; $userID = $session->userID;
$me = $db->queryFirstRow( $me = $db->queryFirstRow(
@@ -51,7 +52,8 @@ class GetReservedCommand extends \sammo\BaseAPI
$nationID = $me['nation']; $nationID = $me['nation'];
$limitState = checkLimit($me['refresh_score']); $limitState = checkLimit($me['refresh_score']);
if ($limitState >= 2) { if ($limitState >= 2) {
return "접속 제한중입니다. 1턴 이내에 너무 많은 갱신을 하셨습니다. (다음 갱신 가능 시각 : {$me['turntime']})"; $limitTime = GameClock::fromStorage($gameStor)->formatTick(Util::toInt($me['turntime']), true);
return "접속 제한중입니다. 1턴 이내에 너무 많은 갱신을 하셨습니다. (다음 갱신 가능 시각 : {$limitTime})";
} }
$permission = checkSecretPermission($me); $permission = checkSecretPermission($me);
@@ -156,7 +158,8 @@ class GetReservedCommand extends \sammo\BaseAPI
'year' => $year, 'year' => $year,
'month' => $month, 'month' => $month,
'turnTerm' => $turnTerm, 'turnTerm' => $turnTerm,
'date' => TimeUtil::now(true), 'date' => $clock->formatTick($clock->nowTick(), true),
'clockMode' => $clock->getMode(),
'chiefList' => $nationChiefList, 'chiefList' => $nationChiefList,
'troopList' => $troopList, 'troopList' => $troopList,
'isChief' => ($me['officer_level'] > 4), 'isChief' => ($me['officer_level'] > 4),
+4 -3
View File
@@ -10,8 +10,9 @@ use sammo\Enums\GeneralLiteQueryMode;
use sammo\Enums\GeneralQueryMode; use sammo\Enums\GeneralQueryMode;
use sammo\General; use sammo\General;
use sammo\GeneralLite; use sammo\GeneralLite;
use sammo\GameClock;
use sammo\KVStorage;
use sammo\Session; use sammo\Session;
use sammo\TimeUtil;
use sammo\Validator; use sammo\Validator;
class AddComment extends \sammo\BaseAPI class AddComment extends \sammo\BaseAPI
@@ -47,7 +48,8 @@ class AddComment extends \sammo\BaseAPI
$generalName = $general->getName(); $generalName = $general->getName();
$nationID = $general->getNationID(); $nationID = $general->getNationID();
$nationName = $general->getStaticNation()['name']; $nationName = $general->getStaticNation()['name'];
$date = TimeUtil::now(); $db = DB::db();
$date = GameClock::fromStorage(KVStorage::getStorage($db, 'game_env'))->formatNow();
$comment = new VoteComment( $comment = new VoteComment(
@@ -61,7 +63,6 @@ class AddComment extends \sammo\BaseAPI
date: $date date: $date
); );
$db = DB::db();
$db->insert('vote_comment', $comment->toArray()); $db->insert('vote_comment', $comment->toArray());
return null; return null;
+6 -1
View File
@@ -8,6 +8,7 @@ use sammo\DB;
use sammo\DTO\VoteComment; use sammo\DTO\VoteComment;
use sammo\DTO\VoteInfo; use sammo\DTO\VoteInfo;
use sammo\Enums\APIRecoveryType; use sammo\Enums\APIRecoveryType;
use sammo\GameClock;
use sammo\Json; use sammo\Json;
use sammo\KVStorage; use sammo\KVStorage;
use sammo\Validator; use sammo\Validator;
@@ -35,13 +36,16 @@ class GetVoteDetail extends \sammo\BaseAPI
{ {
$voteID = $this->args['voteID']; $voteID = $this->args['voteID'];
$db = DB::db(); $db = DB::db();
$clock = GameClock::fromStorage(KVStorage::getStorage($db, 'game_env'));
$voteStor = KVStorage::getStorage($db, 'vote'); $voteStor = KVStorage::getStorage($db, 'vote');
$rawVote = $voteStor->getValue("vote_{$voteID}"); $rawVote = $voteStor->getValue("vote_{$voteID}");
if (!$rawVote) { if (!$rawVote) {
return '설문조사가 없습니다.'; return '설문조사가 없습니다.';
} }
$voteInfo = VoteInfo::fromArray($rawVote); $rawVote = VoteInfo::normalizeGameStorage($rawVote, $clock);
$voteInfo = VoteInfo::fromGameStorage($rawVote, $clock);
$isOpen = $rawVote['endTick'] === null || $rawVote['endTick'] >= $clock->nowTick();
$votes = array_map(fn ($arr) => [Json::decode($arr[0]), $arr[1]], $db->queryAllLists( $votes = array_map(fn ($arr) => [Json::decode($arr[0]), $arr[1]], $db->queryAllLists(
@@ -70,6 +74,7 @@ class GetVoteDetail extends \sammo\BaseAPI
'comments' => $comments, 'comments' => $comments,
'myVote' => $myVote, 'myVote' => $myVote,
'userCnt' => $userCnt, 'userCnt' => $userCnt,
'isOpen' => $isOpen,
]; ];
} }
} }
+5 -3
View File
@@ -6,6 +6,7 @@ use DateTimeInterface;
use sammo\DB; use sammo\DB;
use sammo\DTO\VoteInfo; use sammo\DTO\VoteInfo;
use sammo\Enums\APIRecoveryType; use sammo\Enums\APIRecoveryType;
use sammo\GameClock;
use sammo\KVStorage; use sammo\KVStorage;
use sammo\Session; use sammo\Session;
@@ -25,16 +26,17 @@ class GetVoteList extends \sammo\BaseAPI
public function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag): null | string | array | APIRecoveryType public function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag): null | string | array | APIRecoveryType
{ {
$db = DB::db(); $db = DB::db();
$clock = GameClock::fromStorage(KVStorage::getStorage($db, 'game_env'));
$voteStor = KVStorage::getStorage($db, 'vote'); $voteStor = KVStorage::getStorage($db, 'vote');
$votes = []; $votes = [];
foreach($voteStor->getAll() as $voteKey => $rawVote){ foreach($voteStor->getAll() as $voteKey => $rawVote){
if(!str_starts_with($voteKey, 'vote_')){ if(preg_match('/^vote_(\d+)$/D', $voteKey, $matches) !== 1){
continue; continue;
} }
$voteID = (int)substr($voteKey, 5); $voteID = (int)$matches[1];
$votes[$voteID] = VoteInfo::fromArray($rawVote); $votes[$voteID] = VoteInfo::fromGameStorage($rawVote, $clock);
} }
return [ return [
+19 -15
View File
@@ -9,7 +9,7 @@ use sammo\Enums\APIRecoveryType;
use sammo\KVStorage; use sammo\KVStorage;
use sammo\RootDB; use sammo\RootDB;
use sammo\Session; use sammo\Session;
use sammo\TimeUtil; use sammo\GameClock;
use sammo\Util; use sammo\Util;
use sammo\Validator; use sammo\Validator;
@@ -37,7 +37,7 @@ class NewVote extends \sammo\BaseAPI
return null; return null;
} }
function closeOldVote(int $voteID, KVStorage $voteStor) function closeOldVote(int $voteID, KVStorage $voteStor, GameClock $clock)
{ {
$db = DB::db(); $db = DB::db();
$voteStor = KVStorage::getStorage($db, 'vote'); $voteStor = KVStorage::getStorage($db, 'vote');
@@ -45,13 +45,14 @@ class NewVote extends \sammo\BaseAPI
if (!$rawLastVoteInfo) { if (!$rawLastVoteInfo) {
return; return;
} }
$lastVoteInfo = VoteInfo::fromArray($rawLastVoteInfo); $rawLastVoteInfo = VoteInfo::normalizeGameStorage($rawLastVoteInfo, $clock);
if ($lastVoteInfo->endDate) { if ($rawLastVoteInfo['endTick'] !== null) {
return; return;
} }
$lastVoteInfo->endDate = TimeUtil::now(); $rawLastVoteInfo['endTick'] = $clock->nowTick();
$voteStor->setValue("vote_{$voteID}", $lastVoteInfo->toArray()); $rawLastVoteInfo['endDate'] = $clock->formatTick($rawLastVoteInfo['endTick']);
$voteStor->setValue("vote_{$voteID}", $rawLastVoteInfo);
} }
function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag): null | string | array | APIRecoveryType function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag): null | string | array | APIRecoveryType
@@ -71,7 +72,11 @@ class NewVote extends \sammo\BaseAPI
$multipleOptions = 0; $multipleOptions = 0;
} }
$now = TimeUtil::now(); $db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env');
$clock = GameClock::fromStorage($gameStor);
$nowTick = $clock->nowTick();
$now = $clock->formatTick($nowTick);
/** @var ?string */ /** @var ?string */
$endDate = $this->args['endDate'] ?? null; $endDate = $this->args['endDate'] ?? null;
/** @var string[] */ /** @var string[] */
@@ -83,9 +88,9 @@ class NewVote extends \sammo\BaseAPI
if($endDate !== null){ if($endDate !== null){
try{ try{
$oNow = new \DateTimeImmutable($now);
$oEndDate = new \DateTimeImmutable($endDate); $oEndDate = new \DateTimeImmutable($endDate);
if($oEndDate < $oNow){ $endTick = $clock->dateTimeToTick($oEndDate);
if($endTick < $nowTick){
return '종료일이 이미 지났습니다.'; return '종료일이 이미 지났습니다.';
} }
} }
@@ -96,17 +101,13 @@ class NewVote extends \sammo\BaseAPI
$userName = $session->userName; $userName = $session->userName;
$db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env');
$lastVote = $gameStor->getValue('lastVote') ?? 0; $lastVote = $gameStor->getValue('lastVote') ?? 0;
$voteID = $lastVote + 1; $voteID = $lastVote + 1;
$voteStor = KVStorage::getStorage($db, 'vote'); $voteStor = KVStorage::getStorage($db, 'vote');
if (!($this->args['keepOldVote'] ?? false)) { if (!($this->args['keepOldVote'] ?? false)) {
$this->closeOldVote($lastVote, $voteStor); $this->closeOldVote($lastVote, $voteStor, $clock);
} }
$multipleOptions = Util::valueFit($multipleOptions, 0, count($options)); $multipleOptions = Util::valueFit($multipleOptions, 0, count($options));
@@ -122,7 +123,10 @@ class NewVote extends \sammo\BaseAPI
options: $options, options: $options,
); );
$voteStor->setValue("vote_{$voteID}", $voteInfo->toArray()); $voteStor->setValue("vote_{$voteID}", $voteInfo->toArray() + [
'startTick' => $nowTick,
'endTick' => $endDate === null ? null : $clock->dateTimeToTick(new \DateTimeImmutable($endDate)),
]);
$gameStor->setValue('lastVote', $voteID); $gameStor->setValue('lastVote', $voteID);
$db->update('general', [ $db->update('general', [
+5 -2
View File
@@ -8,6 +8,7 @@ use sammo\DTO\VoteInfo;
use sammo\Enums\APIRecoveryType; use sammo\Enums\APIRecoveryType;
use sammo\Enums\GeneralQueryMode; use sammo\Enums\GeneralQueryMode;
use sammo\General; use sammo\General;
use sammo\GameClock;
use sammo\Json; use sammo\Json;
use sammo\KVStorage; use sammo\KVStorage;
use sammo\LiteHashDRBG; use sammo\LiteHashDRBG;
@@ -54,15 +55,17 @@ class Vote extends \sammo\BaseAPI
return '선택한 항목이 없습니다.'; return '선택한 항목이 없습니다.';
} }
$db = DB::db(); $db = DB::db();
$clock = GameClock::fromStorage(KVStorage::getStorage($db, 'game_env'));
$voteStor = KVStorage::getStorage($db, 'vote'); $voteStor = KVStorage::getStorage($db, 'vote');
$rawVoteInfo = $voteStor->getValue("vote_{$voteID}"); $rawVoteInfo = $voteStor->getValue("vote_{$voteID}");
if (!$rawVoteInfo) { if (!$rawVoteInfo) {
return '설문조사가 없습니다.'; return '설문조사가 없습니다.';
} }
$voteInfo = VoteInfo::fromArray($rawVoteInfo); $rawVoteInfo = VoteInfo::normalizeGameStorage($rawVoteInfo, $clock);
$voteInfo = VoteInfo::fromGameStorage($rawVoteInfo, $clock);
if ($voteInfo->endDate && $voteInfo->endDate < new \DateTimeImmutable()) { if ($rawVoteInfo['endTick'] !== null && $rawVoteInfo['endTick'] < $clock->nowTick()) {
return '설문조사가 종료되었습니다.'; return '설문조사가 종료되었습니다.';
} }
+12 -8
View File
@@ -8,6 +8,11 @@ use sammo\Util;
abstract class AbsFromUserPool extends AbsGeneralPool{ abstract class AbsFromUserPool extends AbsGeneralPool{
protected static function getCandidateWeight(array $info, int $owner): int|float
{
return array_sum($info['dex'] ?? []);
}
public function occupyGeneralName(): bool public function occupyGeneralName(): bool
{ {
$generalID = $this->getGeneralBuilder()->getGeneralID(); $generalID = $this->getGeneralBuilder()->getGeneralID();
@@ -25,8 +30,9 @@ abstract class AbsFromUserPool extends AbsGeneralPool{
} }
static public function pickGeneralFromPool(\MeekroDB $db, RandUtil $rng, int $owner, int $pickCnt, ?string $prefix=null):array{ static public function pickGeneralFromPool(\MeekroDB $db, RandUtil $rng, int $owner, int $pickCnt, ?string $prefix=null):array{
$oNow = new \DateTimeImmutable(); $gameStor = KVStorage::getStorage($db, 'game_env');
$now = $oNow->format('Y-m-d H:i:s'); $clock = GameClock::fromStorage($gameStor);
$now = $clock->nowTick();
$db->update('select_pool', [ $db->update('select_pool', [
'reserved_until'=>null, 'reserved_until'=>null,
@@ -36,17 +42,15 @@ abstract class AbsFromUserPool extends AbsGeneralPool{
$pool = []; $pool = [];
foreach($db->query('SELECT id, unique_name, info FROM select_pool WHERE reserved_until IS NULL AND general_id IS NULL', $pickCnt) as $cand){ foreach($db->query('SELECT id, unique_name, info FROM select_pool WHERE reserved_until IS NULL AND general_id IS NULL', $pickCnt) as $cand){
$cand['info'] = Json::decode($cand['info']); $cand['info'] = Json::decode($cand['info']);
$dexTotal = array_sum($cand['info']['dex']); $pool[] = [$cand, static::getCandidateWeight($cand['info'], $owner)];
$pool[] = [$cand, $dexTotal];
} }
if(count($pool) < $pickCnt){ if(count($pool) < $pickCnt){
throw new \RuntimeException('pool 부족'); throw new \RuntimeException('pool 부족');
} }
$gameStor = KVStorage::getStorage($db, 'game_env'); $result = [];
$result = []; $validUntil = $now + GameClock::TICKS_PER_TURN * 2;
$validUntil = TimeUtil::nowAddMinutes(2 * $gameStor->turnterm);
while(count($result) < $pickCnt){ while(count($result) < $pickCnt){
$cand = $rng->choiceUsingWeightPair($pool); $cand = $rng->choiceUsingWeightPair($pool);
$poolID = $cand['id']; $poolID = $cand['id'];
@@ -69,4 +73,4 @@ abstract class AbsFromUserPool extends AbsGeneralPool{
return array_values($result); return array_values($result);
} }
} }
+3 -3
View File
@@ -34,7 +34,7 @@ abstract class AbsGeneralPool{
* specialWar * specialWar
*/ */
public function __construct(\MeekroDB $db, RandUtil $rng, array $info, string $validUntil) public function __construct(\MeekroDB $db, RandUtil $rng, array $info, int $validUntil)
{ {
$this->db = $db; $this->db = $db;
$this->info = $info; $this->info = $info;
@@ -92,7 +92,7 @@ abstract class AbsGeneralPool{
return $this->builder; return $this->builder;
} }
public function getValidUntil():string{ public function getValidUntil():int{
return $this->validUntil; return $this->validUntil;
} }
@@ -109,4 +109,4 @@ abstract class AbsGeneralPool{
abstract public static function getPoolName():string; abstract public static function getPoolName():string;
abstract public static function initPool(\MeekroDB $db); abstract public static function initPool(\MeekroDB $db);
} }
@@ -15,7 +15,7 @@ class che_척사_오악진형도 extends \sammo\BaseItem{
public function getWarPowerMultiplier(WarUnit $unit):array{ public function getWarPowerMultiplier(WarUnit $unit):array{
$opposeCrewType = $unit->getOppose()->getCrewType(); $opposeCrewType = $unit->getOppose()->getCrewType();
if($opposeCrewType->reqCities || $opposeCrewType->reqRegions){ if($opposeCrewType->reqCities() || $opposeCrewType->reqRegions()){
return [1.15, 0.85]; return [1.15, 0.85];
} }
return [1, 1]; return [1, 1];
@@ -33,7 +33,7 @@ class event_전투특기_공성 extends \sammo\BaseItem{
} }
public function onCalcStat(General $general, string $statName, $value, $aux=null){ public function onCalcStat(General $general, string $statName, $value, $aux=null){
if(\sammo\Util::starts_with($statName, 'dex')){ if(str_starts_with($statName, 'dex')){
$myArmType = 'dex'.GameUnitConst::T_SIEGE; $myArmType = 'dex'.GameUnitConst::T_SIEGE;
$opposeArmType = 'dex'.$aux['opposeType']->armType;; $opposeArmType = 'dex'.$aux['opposeType']->armType;;
if($aux['isAttacker'] && $opposeArmType === $statName){ if($aux['isAttacker'] && $opposeArmType === $statName){
@@ -28,7 +28,7 @@ class event_전투특기_궁병 extends \sammo\BaseItem{
if($statName === 'warAvoidRatio'){ if($statName === 'warAvoidRatio'){
return $value + 0.2; return $value + 0.2;
} }
if(\sammo\Util::starts_with($statName, 'dex')){ if(str_starts_with($statName, 'dex')){
$myArmType = 'dex'.GameUnitConst::T_ARCHER; $myArmType = 'dex'.GameUnitConst::T_ARCHER;
$opposeArmType = 'dex'.$aux['opposeType']->armType;; $opposeArmType = 'dex'.$aux['opposeType']->armType;;
if($aux['isAttacker'] && $opposeArmType === $statName){ if($aux['isAttacker'] && $opposeArmType === $statName){
@@ -28,7 +28,7 @@ class event_전투특기_귀병 extends \sammo\BaseItem{
if($statName === 'warMagicSuccessProb'){ if($statName === 'warMagicSuccessProb'){
return $value + 0.2; return $value + 0.2;
} }
if(\sammo\Util::starts_with($statName, 'dex')){ if(str_starts_with($statName, 'dex')){
$myArmType = 'dex'.GameUnitConst::T_WIZARD; $myArmType = 'dex'.GameUnitConst::T_WIZARD;
$opposeArmType = 'dex'.$aux['opposeType']->armType;; $opposeArmType = 'dex'.$aux['opposeType']->armType;;
if($aux['isAttacker'] && $opposeArmType === $statName){ if($aux['isAttacker'] && $opposeArmType === $statName){
@@ -32,7 +32,7 @@ class event_전투특기_기병 extends \sammo\BaseItem{
} }
public function onCalcStat(General $general, string $statName, $value, $aux=null){ public function onCalcStat(General $general, string $statName, $value, $aux=null){
if(\sammo\Util::starts_with($statName, 'dex')){ if(str_starts_with($statName, 'dex')){
$myArmType = 'dex'.GameUnitConst::T_CAVALRY; $myArmType = 'dex'.GameUnitConst::T_CAVALRY;
$opposeArmType = 'dex'.$aux['opposeType']->armType;; $opposeArmType = 'dex'.$aux['opposeType']->armType;;
if($aux['isAttacker'] && $opposeArmType === $statName){ if($aux['isAttacker'] && $opposeArmType === $statName){
@@ -33,7 +33,7 @@ class event_전투특기_보병 extends \sammo\BaseItem{
} }
public function onCalcStat(General $general, string $statName, $value, $aux=null){ public function onCalcStat(General $general, string $statName, $value, $aux=null){
if(\sammo\Util::starts_with($statName, 'dex')){ if(str_starts_with($statName, 'dex')){
$myArmType = 'dex'.GameUnitConst::T_FOOTMAN; $myArmType = 'dex'.GameUnitConst::T_FOOTMAN;
$opposeArmType = 'dex'.$aux['opposeType']->armType;; $opposeArmType = 'dex'.$aux['opposeType']->armType;;
if($aux['isAttacker'] && $opposeArmType === $statName){ if($aux['isAttacker'] && $opposeArmType === $statName){
@@ -17,7 +17,7 @@ class event_전투특기_척사 extends \sammo\BaseItem{
public function getWarPowerMultiplier(WarUnit $unit):array{ public function getWarPowerMultiplier(WarUnit $unit):array{
$opposeCrewType = $unit->getOppose()->getCrewType(); $opposeCrewType = $unit->getOppose()->getCrewType();
if($opposeCrewType->reqCities || $opposeCrewType->reqRegions){ if($opposeCrewType->reqCities() || $opposeCrewType->reqRegions()){
return [1.2, 0.8]; return [1.2, 0.8];
} }
return [1, 1]; return [1, 1];
@@ -0,0 +1,68 @@
<?php
namespace sammo\ActionScenarioEffect;
use \sammo\iAction;
use sammo\Util;
use sammo\WarUnit;
use sammo\WarUnitCity;
use sammo\WarUnitTrigger\che_전멸시페이즈증가;
use sammo\WarUnitTriggerCaller;
class event_MoreEffect implements iAction
{
use \sammo\DefaultAction;
public function getWarPowerMultiplier(WarUnit $unit): array
{
if ($unit->isAttacker()) {
return [1.4, 0.7143];
}
return [1, 1];
}
public function onCalcDomestic(string $turnType, string $varType, float $value, $aux = null): float
{
$scoreMap = [
'상업' => 2,
'농업' => 2,
'치안' => 2,
'기술' => 2,
'성벽' => 2,
'수비' => 2,
'인구' => 2,
'민심' => 2,
];
if ($turnType == 'changeDefenceTrain') {
return 0;
}
if ($varType === 'score' && key_exists($turnType, $scoreMap)) {
$multiplier = $scoreMap[$turnType];
$value *= $multiplier;
return $value;
}
return $value;
}
public function onCalcNationalIncome(string $type, $amount){
if($type == 'gold'){
return $amount * 2;
}
if($type == 'rice'){
return $amount * 2;
}
if($type == 'pop' && $amount > 0){
return $amount * 2;
}
return $amount;
}
public function getBattlePhaseSkillTriggerList(\sammo\WarUnit $unit): ?WarUnitTriggerCaller
{
return new WarUnitTriggerCaller(
new che_전멸시페이즈증가($unit),
);
}
}
@@ -35,7 +35,7 @@ class che_event_공성 extends \sammo\BaseSpecial{
} }
public function onCalcStat(General $general, string $statName, $value, $aux=null){ public function onCalcStat(General $general, string $statName, $value, $aux=null){
if(\sammo\Util::starts_with($statName, 'dex')){ if(str_starts_with($statName, 'dex')){
$myArmType = 'dex'.GameUnitConst::T_SIEGE; $myArmType = 'dex'.GameUnitConst::T_SIEGE;
$opposeArmType = 'dex'.$aux['opposeType']->armType;; $opposeArmType = 'dex'.$aux['opposeType']->armType;;
if($aux['isAttacker'] && $opposeArmType === $statName){ if($aux['isAttacker'] && $opposeArmType === $statName){
@@ -31,7 +31,7 @@ class che_event_궁병 extends \sammo\BaseSpecial{
if($statName === 'warAvoidRatio'){ if($statName === 'warAvoidRatio'){
return $value + 0.2; return $value + 0.2;
} }
if(\sammo\Util::starts_with($statName, 'dex')){ if(str_starts_with($statName, 'dex')){
$myArmType = 'dex'.GameUnitConst::T_ARCHER; $myArmType = 'dex'.GameUnitConst::T_ARCHER;
$opposeArmType = 'dex'.$aux['opposeType']->armType;; $opposeArmType = 'dex'.$aux['opposeType']->armType;;
if($aux['isAttacker'] && $opposeArmType === $statName){ if($aux['isAttacker'] && $opposeArmType === $statName){
@@ -30,7 +30,7 @@ class che_event_귀병 extends \sammo\BaseSpecial{
if($statName === 'warMagicSuccessProb'){ if($statName === 'warMagicSuccessProb'){
return $value + 0.2; return $value + 0.2;
} }
if(\sammo\Util::starts_with($statName, 'dex')){ if(str_starts_with($statName, 'dex')){
$myArmType = 'dex'.GameUnitConst::T_WIZARD; $myArmType = 'dex'.GameUnitConst::T_WIZARD;
$opposeArmType = 'dex'.$aux['opposeType']->armType;; $opposeArmType = 'dex'.$aux['opposeType']->armType;;
if($aux['isAttacker'] && $opposeArmType === $statName){ if($aux['isAttacker'] && $opposeArmType === $statName){
@@ -35,7 +35,7 @@ class che_event_기병 extends \sammo\BaseSpecial{
} }
public function onCalcStat(General $general, string $statName, $value, $aux=null){ public function onCalcStat(General $general, string $statName, $value, $aux=null){
if(\sammo\Util::starts_with($statName, 'dex')){ if(str_starts_with($statName, 'dex')){
$myArmType = 'dex'.GameUnitConst::T_CAVALRY; $myArmType = 'dex'.GameUnitConst::T_CAVALRY;
$opposeArmType = 'dex'.$aux['opposeType']->armType;; $opposeArmType = 'dex'.$aux['opposeType']->armType;;
if($aux['isAttacker'] && $opposeArmType === $statName){ if($aux['isAttacker'] && $opposeArmType === $statName){
@@ -35,7 +35,7 @@ class che_event_보병 extends \sammo\BaseSpecial{
} }
public function onCalcStat(General $general, string $statName, $value, $aux=null){ public function onCalcStat(General $general, string $statName, $value, $aux=null){
if(\sammo\Util::starts_with($statName, 'dex')){ if(str_starts_with($statName, 'dex')){
$myArmType = 'dex'.GameUnitConst::T_FOOTMAN; $myArmType = 'dex'.GameUnitConst::T_FOOTMAN;
$opposeArmType = 'dex'.$aux['opposeType']->armType;; $opposeArmType = 'dex'.$aux['opposeType']->armType;;
if($aux['isAttacker'] && $opposeArmType === $statName){ if($aux['isAttacker'] && $opposeArmType === $statName){
@@ -21,7 +21,7 @@ class che_event_척사 extends \sammo\BaseSpecial{
public function getWarPowerMultiplier(WarUnit $unit):array{ public function getWarPowerMultiplier(WarUnit $unit):array{
$opposeCrewType = $unit->getOppose()->getCrewType(); $opposeCrewType = $unit->getOppose()->getCrewType();
if($opposeCrewType->reqCities || $opposeCrewType->reqRegions){ if($opposeCrewType->reqCities() || $opposeCrewType->reqRegions()){
return [1.2, 0.8]; return [1.2, 0.8];
} }
return [1, 1]; return [1, 1];
+1 -1
View File
@@ -35,7 +35,7 @@ class che_공성 extends \sammo\BaseSpecial{
} }
public function onCalcStat(General $general, string $statName, $value, $aux=null){ public function onCalcStat(General $general, string $statName, $value, $aux=null){
if(\sammo\Util::starts_with($statName, 'dex')){ if(str_starts_with($statName, 'dex')){
$myArmType = 'dex'.GameUnitConst::T_SIEGE; $myArmType = 'dex'.GameUnitConst::T_SIEGE;
$opposeArmType = 'dex'.$aux['opposeType']->armType;; $opposeArmType = 'dex'.$aux['opposeType']->armType;;
if($aux['isAttacker'] && $opposeArmType === $statName){ if($aux['isAttacker'] && $opposeArmType === $statName){
+1 -1
View File
@@ -31,7 +31,7 @@ class che_궁병 extends \sammo\BaseSpecial{
if($statName === 'warAvoidRatio'){ if($statName === 'warAvoidRatio'){
return $value + 0.2; return $value + 0.2;
} }
if(\sammo\Util::starts_with($statName, 'dex')){ if(str_starts_with($statName, 'dex')){
$myArmType = 'dex'.GameUnitConst::T_ARCHER; $myArmType = 'dex'.GameUnitConst::T_ARCHER;
$opposeArmType = 'dex'.$aux['opposeType']->armType;; $opposeArmType = 'dex'.$aux['opposeType']->armType;;
if($aux['isAttacker'] && $opposeArmType === $statName){ if($aux['isAttacker'] && $opposeArmType === $statName){
+1 -1
View File
@@ -30,7 +30,7 @@ class che_귀병 extends \sammo\BaseSpecial{
if($statName === 'warMagicSuccessProb'){ if($statName === 'warMagicSuccessProb'){
return $value + 0.2; return $value + 0.2;
} }
if(\sammo\Util::starts_with($statName, 'dex')){ if(str_starts_with($statName, 'dex')){
$myArmType = 'dex'.GameUnitConst::T_WIZARD; $myArmType = 'dex'.GameUnitConst::T_WIZARD;
$opposeArmType = 'dex'.$aux['opposeType']->armType;; $opposeArmType = 'dex'.$aux['opposeType']->armType;;
if($aux['isAttacker'] && $opposeArmType === $statName){ if($aux['isAttacker'] && $opposeArmType === $statName){
+1 -1
View File
@@ -35,7 +35,7 @@ class che_기병 extends \sammo\BaseSpecial{
} }
public function onCalcStat(General $general, string $statName, $value, $aux=null){ public function onCalcStat(General $general, string $statName, $value, $aux=null){
if(\sammo\Util::starts_with($statName, 'dex')){ if(str_starts_with($statName, 'dex')){
$myArmType = 'dex'.GameUnitConst::T_CAVALRY; $myArmType = 'dex'.GameUnitConst::T_CAVALRY;
$opposeArmType = 'dex'.$aux['opposeType']->armType;; $opposeArmType = 'dex'.$aux['opposeType']->armType;;
if($aux['isAttacker'] && $opposeArmType === $statName){ if($aux['isAttacker'] && $opposeArmType === $statName){
+1 -1
View File
@@ -35,7 +35,7 @@ class che_보병 extends \sammo\BaseSpecial{
} }
public function onCalcStat(General $general, string $statName, $value, $aux=null){ public function onCalcStat(General $general, string $statName, $value, $aux=null){
if(\sammo\Util::starts_with($statName, 'dex')){ if(str_starts_with($statName, 'dex')){
$myArmType = 'dex'.GameUnitConst::T_FOOTMAN; $myArmType = 'dex'.GameUnitConst::T_FOOTMAN;
$opposeArmType = 'dex'.$aux['opposeType']->armType;; $opposeArmType = 'dex'.$aux['opposeType']->armType;;
if($aux['isAttacker'] && $opposeArmType === $statName){ if($aux['isAttacker'] && $opposeArmType === $statName){
+1 -1
View File
@@ -21,7 +21,7 @@ class che_척사 extends \sammo\BaseSpecial{
public function getWarPowerMultiplier(WarUnit $unit):array{ public function getWarPowerMultiplier(WarUnit $unit):array{
$opposeCrewType = $unit->getOppose()->getCrewType(); $opposeCrewType = $unit->getOppose()->getCrewType();
if($opposeCrewType->reqCities || $opposeCrewType->reqRegions){ if($opposeCrewType->reqCities() || $opposeCrewType->reqRegions()){
return [1.2, 0.8]; return [1.2, 0.8];
} }
return [1, 1]; return [1, 1];
+55 -54
View File
@@ -146,40 +146,35 @@ abstract class Auction
return $this->info; return $this->info;
} }
public function shrinkCloseDate(?DateTimeInterface $date): ?string public function shrinkCloseTick(?int $tick): ?string
{ {
if ($date === null) {
$date = new DateTimeImmutable();
}
$this->info->closeDate = $date;
$db = DB::db(); $db = DB::db();
$clock = GameClock::fromStorage(KVStorage::getStorage($db, 'game_env'));
$this->info->closeTick = $tick ?? $clock->nowTick();
$db->update('ng_auction', $this->info->toArray('id'), 'id = %i', $this->info->id); $db->update('ng_auction', $this->info->toArray('id'), 'id = %i', $this->info->id);
return null; return null;
} }
public function extendLatestBidCloseDate(?DateTimeInterface $date): ?string public function extendLatestBidCloseTick(?int $tick): ?string
{ {
if ($date === null) { if ($tick === null) {
$db = DB::db(); $db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env'); $gameStor = KVStorage::getStorage($db, 'game_env');
$clock = GameClock::fromStorage($gameStor);
$turnTerm = $gameStor->getValue('turnterm'); $turnTerm = $gameStor->getValue('turnterm');
$date = $this->info->closeDate->add(TimeUtil::secondsToDateInterval( $tick = $this->info->closeTick + $clock->ticksFromMinutes(
max(static::MIN_EXTENSION_MINUTES_PER_BID, $turnTerm * static::COEFF_EXTENSION_MINUTES_PER_BID) * 60 max(static::MIN_EXTENSION_MINUTES_PER_BID, $turnTerm * static::COEFF_EXTENSION_MINUTES_PER_BID)
)); );
} }
else{ if ($this->info->detail->availableLatestBidCloseTick !== null && $tick < $this->info->detail->availableLatestBidCloseTick) {
$date = DateTimeImmutable::createFromInterface($date);
}
if ($this->info->detail->availableLatestBidCloseDate !== null && $date < $this->info->detail->availableLatestBidCloseDate) {
return '기간보다 짧습니다.'; return '기간보다 짧습니다.';
} }
$this->info->detail->availableLatestBidCloseDate = $date; $this->info->detail->availableLatestBidCloseTick = $tick;
return null; return null;
} }
public function extendCloseDate(DateTimeInterface $date, bool $force = false): ?string public function extendCloseTick(int $tick, bool $force = false): ?string
{ {
if (!$force) { if (!$force) {
if ($this->info->detail->remainCloseDateExtensionCnt === null) { if ($this->info->detail->remainCloseDateExtensionCnt === null) {
@@ -193,12 +188,11 @@ abstract class Auction
} }
} }
if ($date < $this->info->closeDate) { if ($tick < $this->info->closeTick) {
return '종료 기간보다 짧습니다.'; return '종료 기간보다 짧습니다.';
} }
$closeDate = DateTimeImmutable::createFromInterface($date); $this->info->closeTick = $tick;
$this->info->closeDate = $closeDate;
return null; return null;
} }
@@ -245,12 +239,13 @@ abstract class Auction
//TODO: 전역 알림이 나타나야한다. 일반 메시지보다는 중요하고, 메시지보단 약하게.. //TODO: 전역 알림이 나타나야한다. 일반 메시지보다는 중요하고, 메시지보단 약하게..
//TODO: 바로가기를 제공하는 편이 좋을 것 같다. //TODO: 바로가기를 제공하는 편이 좋을 것 같다.
$clock = GameClock::fromStorage(KVStorage::getStorage($db, 'game_env'));
$msg = new Message( $msg = new Message(
MessageType::private, MessageType::private,
$src, $src,
$dest, $dest,
$reason, $reason,
new DateTime(), DateTime::createFromImmutable($clock->tickToDateTime($clock->nowTick())),
new DateTime('9999-12-31'), new DateTime('9999-12-31'),
[] []
); );
@@ -275,7 +270,12 @@ abstract class Auction
$db->update('ng_auction', $this->info->toArray('id'), 'id = %i', $this->info->id); $db->update('ng_auction', $this->info->toArray('id'), 'id = %i', $this->info->id);
} }
private function bidInheritPoint(int $amount, \DateTimeImmutable $now, bool $tryExtendCloseDate): ?string private function bidInheritPoint(
int $amount,
int $nowTick,
\DateTimeImmutable $nowDate,
bool $tryExtendCloseDate,
): ?string
{ {
$db = DB::db(); $db = DB::db();
@@ -311,7 +311,7 @@ abstract class Auction
$general->getVar('owner'), $general->getVar('owner'),
$general->getID(), $general->getID(),
$amount, $amount,
$now, $nowDate,
new AuctionBidItemData( new AuctionBidItemData(
$general->getVar('owner_name'), $general->getVar('owner_name'),
$obfuscatedName, $obfuscatedName,
@@ -324,15 +324,16 @@ abstract class Auction
} }
$gameStor = KVStorage::getStorage($db, 'game_env'); $gameStor = KVStorage::getStorage($db, 'game_env');
$clock = GameClock::fromStorage($gameStor);
$turnTerm = $gameStor->getValue('turnterm'); $turnTerm = $gameStor->getValue('turnterm');
if ($this->info->detail->availableLatestBidCloseDate !== null) { if ($this->info->detail->availableLatestBidCloseTick !== null) {
$extendedCloseDate = $now->add(TimeUtil::secondsToDateInterval( $extendedCloseTick = $nowTick + $clock->ticksFromMinutes(
max(static::MIN_EXTENSION_MINUTES_PER_BID, $turnTerm * static::COEFF_EXTENSION_MINUTES_PER_BID) * 60 max(static::MIN_EXTENSION_MINUTES_PER_BID, $turnTerm * static::COEFF_EXTENSION_MINUTES_PER_BID)
)); );
if ($extendedCloseDate > $this->info->closeDate && $this->info->closeDate < $this->info->detail->availableLatestBidCloseDate) { if ($extendedCloseTick > $this->info->closeTick && $this->info->closeTick < $this->info->detail->availableLatestBidCloseTick) {
$this->extendCloseDate(min($extendedCloseDate, $this->info->detail->availableLatestBidCloseDate), true); $this->extendCloseTick(min($extendedCloseTick, $this->info->detail->availableLatestBidCloseTick), true);
$this->applyDB(); $this->applyDB();
} }
} }
@@ -356,12 +357,16 @@ abstract class Auction
return '경매가 이미 끝났습니다.'; return '경매가 이미 끝났습니다.';
} }
$now = new \DateTimeImmutable(); $db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env');
$clock = GameClock::fromStorage($gameStor);
$nowTick = $clock->nowTick();
$nowDate = $clock->tickToDateTime($nowTick);
if ($auctionInfo->closeDate < $now) { if ($auctionInfo->closeTick < $nowTick) {
return '경매가 이미 끝났습니다.'; return '경매가 이미 끝났습니다.';
} }
if ($auctionInfo->openDate > $now) { if ($auctionInfo->openTick > $nowTick) {
return '경매가 아직 시작되지 않았습니다.'; return '경매가 아직 시작되지 않았습니다.';
} }
@@ -377,13 +382,11 @@ abstract class Auction
if ($auctionInfo->reqResource === ResourceType::inheritancePoint) { if ($auctionInfo->reqResource === ResourceType::inheritancePoint) {
return $this->bidInheritPoint($amount, $now, $tryExtendCloseDate); return $this->bidInheritPoint($amount, $nowTick, $nowDate, $tryExtendCloseDate);
} }
//reqResource는 말 그대로 '구매자가 내야하는 자원'이다. //reqResource는 말 그대로 '구매자가 내야하는 자원'이다.
$db = DB::db();
$highestBid = $this->getHighestBid(); $highestBid = $this->getHighestBid();
if (!$auctionInfo->detail->isReverse) { if (!$auctionInfo->detail->isReverse) {
if ($highestBid !== null && $amount <= $highestBid->amount) { if ($highestBid !== null && $amount <= $highestBid->amount) {
@@ -421,7 +424,7 @@ abstract class Auction
$general->getVar('owner'), $general->getVar('owner'),
$general->getID(), $general->getID(),
$amount, $amount,
$now, $nowDate,
new AuctionBidItemData( new AuctionBidItemData(
$general->getVar('owner_name'), $general->getVar('owner_name'),
$general->getName(), $general->getName(),
@@ -436,14 +439,13 @@ abstract class Auction
$general->increaseVar($resType->value, -$morePoint); $general->increaseVar($resType->value, -$morePoint);
$gameStor = KVStorage::getStorage($db, 'game_env');
$turnTerm = $gameStor->getValue('turnterm'); $turnTerm = $gameStor->getValue('turnterm');
$extendedCloseDate = $now->add(TimeUtil::secondsToDateInterval( $extendedCloseTick = $nowTick + $clock->ticksFromMinutes(
max(static::MIN_EXTENSION_MINUTES_PER_BID, $turnTerm * static::COEFF_EXTENSION_MINUTES_PER_BID) * 60 max(static::MIN_EXTENSION_MINUTES_PER_BID, $turnTerm * static::COEFF_EXTENSION_MINUTES_PER_BID)
)); );
if ($extendedCloseDate > $this->info->closeDate) { if ($extendedCloseTick > $this->info->closeTick) {
$this->extendCloseDate($extendedCloseDate, true); $this->extendCloseTick($extendedCloseTick, true);
$this->applyDB(); $this->applyDB();
} }
@@ -456,10 +458,10 @@ abstract class Auction
public function tryFinish(): ?bool public function tryFinish(): ?bool
{ {
$now = new DateTimeImmutable(); $db = DB::db();
if ($now < $this->info->closeDate) { $gameStor = KVStorage::getStorage($db, 'game_env');
return null; $clock = GameClock::fromStorage($gameStor);
} if ($clock->nowTick() < $this->info->closeTick) return null;
//경매를 닫아야한다. //경매를 닫아야한다.
$highestBid = $this->getHighestBid(); $highestBid = $this->getHighestBid();
@@ -469,17 +471,15 @@ abstract class Auction
} }
if ($highestBid->aux->tryExtendCloseDate) { if ($highestBid->aux->tryExtendCloseDate) {
$db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env');
$turnTerm = $gameStor->getValue('turnterm'); $turnTerm = $gameStor->getValue('turnterm');
//연장 요청이 있었다. //연장 요청이 있었다.
$extendedCloseDate = $this->info->closeDate->add(TimeUtil::secondsToDateInterval( $extendedCloseTick = $this->info->closeTick + $clock->ticksFromMinutes(
max(static::MIN_EXTENSION_MINUTES_BY_EXTENSION_QUERY, $turnTerm * static::COEFF_EXTENSION_MINUTES_BY_EXTENSION_QUERY) * 60 max(static::MIN_EXTENSION_MINUTES_BY_EXTENSION_QUERY, $turnTerm * static::COEFF_EXTENSION_MINUTES_BY_EXTENSION_QUERY)
)); );
if ($this->extendCloseDate($extendedCloseDate) === null) { if ($this->extendCloseTick($extendedCloseTick) === null) {
$this->extendLatestBidCloseDate(null); $this->extendLatestBidCloseTick(null);
$this->applyDB(); $this->applyDB();
return false; return false;
} }
@@ -509,12 +509,13 @@ abstract class Auction
//TODO: 전역 알림이 나타나야한다. 일반 메시지보다는 중요하고, 메시지보단 약하게.. //TODO: 전역 알림이 나타나야한다. 일반 메시지보다는 중요하고, 메시지보단 약하게..
//TODO: 바로가기를 제공하는 편이 좋을 것 같다. //TODO: 바로가기를 제공하는 편이 좋을 것 같다.
$clock = GameClock::fromStorage(KVStorage::getStorage($db, 'game_env'));
$msg = new Message( $msg = new Message(
MessageType::private, MessageType::private,
$src, $src,
$dest, $dest,
$failReason, $failReason,
new \DateTime(), DateTime::createFromImmutable($clock->tickToDateTime($clock->nowTick())),
new \DateTime('9999-12-31'), new \DateTime('9999-12-31'),
[] []
); );
+9 -7
View File
@@ -56,10 +56,11 @@ abstract class AuctionBasicResource extends Auction
} }
$now = new \DateTimeImmutable();
$gameStor = KVStorage::getStorage($db, 'game_env'); $gameStor = KVStorage::getStorage($db, 'game_env');
$clock = GameClock::fromStorage($gameStor);
$nowTick = $clock->nowTick();
$turnTerm = $gameStor->getValue('turnterm'); $turnTerm = $gameStor->getValue('turnterm');
$closeDate = $now->add(TimeUtil::secondsToDateInterval($closeTurnCnt * $turnTerm * 60)); $closeTick = $nowTick + GameClock::TICKS_PER_TURN * $closeTurnCnt;
$openResult = static::openAuction(new AuctionInfo( $openResult = static::openAuction(new AuctionInfo(
null, null,
@@ -68,8 +69,8 @@ abstract class AuctionBasicResource extends Auction
"$amount", "$amount",
$general->getId(), $general->getId(),
$bidderRes, $bidderRes,
$now, $nowTick,
$closeDate, $closeTick,
new AuctionInfoDetail( new AuctionInfoDetail(
"{$hostResName} {$amount} 경매", "{$hostResName} {$amount} 경매",
$general->getName(), $general->getName(),
@@ -145,12 +146,13 @@ abstract class AuctionBasicResource extends Auction
//TODO: 전역 알림이 나타나야한다. 일반 메시지보다는 중요하고, 메시지보단 약하게.. //TODO: 전역 알림이 나타나야한다. 일반 메시지보다는 중요하고, 메시지보단 약하게..
//TODO: 바로가기를 제공하는 편이 좋을 것 같다. //TODO: 바로가기를 제공하는 편이 좋을 것 같다.
$clock = GameClock::fromStorage(KVStorage::getStorage(DB::db(), 'game_env'));
$msg = new Message( $msg = new Message(
MessageType::private, MessageType::private,
$src, $src,
$dest, $dest,
"{$this->auctionID}{$hostResName} 경매에 입찰이 없어 취소되었습니다.", "{$this->auctionID}{$hostResName} 경매에 입찰이 없어 취소되었습니다.",
new \DateTime(), \DateTime::createFromImmutable($clock->tickToDateTime($clock->nowTick())),
new \DateTime('9999-12-31'), new \DateTime('9999-12-31'),
[] []
); );
@@ -246,8 +248,8 @@ abstract class AuctionBasicResource extends Auction
$db = DB::db(); $db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env'); $gameStor = KVStorage::getStorage($db, 'game_env');
$turnTerm = $gameStor->getValue('turnterm'); $turnTerm = $gameStor->getValue('turnterm');
$date = (new DateTimeImmutable())->add(TimeUtil::secondsToDateInterval($turnTerm * 60)); $clock = GameClock::fromStorage($gameStor);
$this->shrinkCloseDate($date); $this->shrinkCloseTick($clock->nowTick() + GameClock::TICKS_PER_TURN);
} }
return null; return null;
+27 -26
View File
@@ -72,16 +72,15 @@ class AuctionUniqueItem extends Auction
$gameStor = KVStorage::getStorage($db, 'game_env'); $gameStor = KVStorage::getStorage($db, 'game_env');
$now = new DateTimeImmutable();
[$turnTerm, $year, $month] = $gameStor->getValuesAsArray(['turnterm', 'year', 'month']); [$turnTerm, $year, $month] = $gameStor->getValuesAsArray(['turnterm', 'year', 'month']);
$clock = GameClock::fromStorage($gameStor);
$closeDate = $now->add(TimeUtil::secondsToDateInterval( $nowTick = $clock->nowTick();
max(static::MIN_AUCTION_CLOSE_MINUTES, $turnTerm * static::COEFF_AUCTION_CLOSE_MINUTES) * 60 $closeTick = $nowTick + $clock->ticksFromMinutes(
)); max(static::MIN_AUCTION_CLOSE_MINUTES, $turnTerm * static::COEFF_AUCTION_CLOSE_MINUTES)
$availableLatestBidCloseDate = $closeDate->add(TimeUtil::secondsToDateInterval( );
max(static::MIN_EXTENSION_MINUTES_LIMIT_BY_BID, $turnTerm * static::COEFF_EXTENSION_MINUTES_LIMIT_BY_BID) * 60 $availableLatestBidCloseTick = $closeTick + $clock->ticksFromMinutes(
)); max(static::MIN_EXTENSION_MINUTES_LIMIT_BY_BID, $turnTerm * static::COEFF_EXTENSION_MINUTES_LIMIT_BY_BID)
);
$info = new AuctionInfo( $info = new AuctionInfo(
null, null,
@@ -90,8 +89,8 @@ class AuctionUniqueItem extends Auction
$itemKey, $itemKey,
$general->getID(), $general->getID(),
ResourceType::inheritancePoint, ResourceType::inheritancePoint,
$now, $nowTick,
$closeDate, $closeTick,
new AuctionInfoDetail( new AuctionInfoDetail(
"{$item->getName()} 경매", "{$item->getName()} 경매",
static::genObfuscatedName($general->getID()), static::genObfuscatedName($general->getID()),
@@ -100,7 +99,7 @@ class AuctionUniqueItem extends Auction
$startAmount, $startAmount,
null, null,
1, 1,
$availableLatestBidCloseDate, $availableLatestBidCloseTick,
) )
); );
@@ -267,21 +266,22 @@ class AuctionUniqueItem extends Auction
if ($availableEquipUniqueCnt <= 0) { if ($availableEquipUniqueCnt <= 0) {
$turnTerm = $gameStor->getValue('turnterm'); $turnTerm = $gameStor->getValue('turnterm');
$clock = GameClock::fromStorage($gameStor);
//제한에 걸렸다면 자동 연장 //제한에 걸렸다면 자동 연장
$extendedCloseDate = $this->info->closeDate->add(TimeUtil::secondsToDateInterval( $extendedCloseTick = $this->info->closeTick + $clock->ticksFromMinutes(
max(static::MIN_EXTENSION_MINUTES_BY_EXTENSION_QUERY, $turnTerm * static::COEFF_EXTENSION_MINUTES_LIMIT_UNIQUE_CNT) * 60 max(static::MIN_EXTENSION_MINUTES_BY_EXTENSION_QUERY, $turnTerm * static::COEFF_EXTENSION_MINUTES_LIMIT_UNIQUE_CNT)
)); );
if($bidder->getID() != $this->info->hostGeneralID){ if($bidder->getID() != $this->info->hostGeneralID){
$this->setHostAsNeutral(); $this->setHostAsNeutral();
} }
$this->extendCloseDate($extendedCloseDate, true); $this->extendCloseTick($extendedCloseTick, true);
$this->extendLatestBidCloseDate(null); $this->extendLatestBidCloseTick(null);
$this->applyDB(); $this->applyDB();
return '유니크 아이템 소유 제한 상태입니다. 종료 시간이 연장됩니다.'; return '유니크 아이템 소유 제한 상태입니다. 종료 시간이 연장됩니다.';
} }
$isExtendCloseDateRequired = false; $isExtendCloseTickRequired = false;
foreach (GameConst::$allItems as $itemType => $itemList) { foreach (GameConst::$allItems as $itemType => $itemList) {
//아직은 그런 경우는 없지만 동일 유니크를 여러 부위에 장착할 수 있을지도 모름 //아직은 그런 경우는 없지만 동일 유니크를 여러 부위에 장착할 수 있을지도 모름
if (!key_exists($itemKey, $itemList)) { if (!key_exists($itemKey, $itemList)) {
@@ -291,13 +291,13 @@ class AuctionUniqueItem extends Auction
$ownItem = $general->getItem($itemType); $ownItem = $general->getItem($itemType);
if ($ownItem->getRawClassName() == $itemKey) { if ($ownItem->getRawClassName() == $itemKey) {
//FIXME: 이 경우에는 환불이 되던가 해야함. //FIXME: 이 경우에는 환불이 되던가 해야함.
$isExtendCloseDateRequired = true; $isExtendCloseTickRequired = true;
$reasons[] = '이미 그 유니크를 가지고 있습니다.'; $reasons[] = '이미 그 유니크를 가지고 있습니다.';
continue; continue;
} }
if (!$ownItem->isBuyable()) { if (!$ownItem->isBuyable()) {
$isExtendCloseDateRequired = true; $isExtendCloseTickRequired = true;
$reasons[] = '이미 다른 유니크를 가지고 있습니다.'; $reasons[] = '이미 다른 유니크를 가지고 있습니다.';
continue; continue;
} }
@@ -313,18 +313,19 @@ class AuctionUniqueItem extends Auction
} }
if (!$availableItemTypes) { if (!$availableItemTypes) {
if ($isExtendCloseDateRequired) { if ($isExtendCloseTickRequired) {
$turnTerm = $gameStor->getValue('turnterm'); $turnTerm = $gameStor->getValue('turnterm');
$clock = GameClock::fromStorage($gameStor);
//동일 부위 제한에 걸렸다면 자동 연장 //동일 부위 제한에 걸렸다면 자동 연장
$extendedCloseDate = $this->info->closeDate->add(TimeUtil::secondsToDateInterval( $extendedCloseTick = $this->info->closeTick + $clock->ticksFromMinutes(
max(static::MIN_EXTENSION_MINUTES_LIMIT_BY_BID, $turnTerm * static::COEFF_EXTENSION_MINUTES_LIMIT_BY_BID) * 60 max(static::MIN_EXTENSION_MINUTES_LIMIT_BY_BID, $turnTerm * static::COEFF_EXTENSION_MINUTES_LIMIT_BY_BID)
)); );
if($bidder->getID() != $this->info->hostGeneralID){ if($bidder->getID() != $this->info->hostGeneralID){
$this->setHostAsNeutral(); $this->setHostAsNeutral();
} }
$this->extendCloseDate($extendedCloseDate, true); $this->extendCloseTick($extendedCloseTick, true);
$this->extendLatestBidCloseDate(null); $this->extendLatestBidCloseTick(null);
$this->applyDB(); $this->applyDB();
} }
return join(' ', $reasons); return join(' ', $reasons);
@@ -0,0 +1,640 @@
<?php
namespace sammo;
use sammo\Scenario\GeneralBuilder;
final class CentennialAllStarGrowthService
{
public const POOL_CLASS = 'SPoolUnderU100';
public const AUX_KEY = 'event100_allstar';
public const TRAIT_UNLOCK_PROGRESS = 0.4;
public const NPC_PROGRESS_MULTIPLIER = 0.9;
private const STAT_KEYS = ['leadership', 'strength', 'intel'];
private const DEX_KEYS = ['dex1', 'dex2', 'dex3', 'dex4', 'dex5'];
public static function isActive(): bool
{
return GameConst::$targetGeneralPool === self::POOL_CLASS;
}
public static function isStatResetAllowed(): bool
{
return !self::isActive();
}
public static function initialAux(array $targetInfo, ?array $userInitialStats = null): array
{
$granted = array_fill_keys(array_merge(self::STAT_KEYS, self::DEX_KEYS), 0);
if ($userInitialStats !== null) {
foreach (self::STAT_KEYS as $key) {
$initial = (int) ($userInitialStats[$key] ?? GameConst::$defaultStatMin);
$granted[$key] = max(0, $initial - min($initial, GameConst::$defaultStatMin));
}
}
return [
'targetId' => (string) ($targetInfo['uniqueName'] ?? ''),
'granted' => $granted,
'dexConsumed' => array_fill_keys(self::DEX_KEYS, 0),
'dexFloor' => array_fill_keys(self::DEX_KEYS, 0),
'progressMonth' => -1,
'milestone' => 0,
'naturalSpecialDomestic' => null,
'eventSpecialDomestic' => null,
'userInitialStats' => $userInitialStats,
'dexTargetRatio' => 1.0,
];
}
public static function attachInitialTarget(GeneralBuilder $builder, array $targetInfo): void
{
$builder->setAuxVar(self::AUX_KEY, self::initialAux($targetInfo));
}
/**
* Builds an ordinary-user stat total while preserving the selected
* candidate's relative strengths as closely as integer stats allow.
*
* @return array{leadership:int,strength:int,intel:int}
*/
public static function calculateUserInitialStats(array $targetInfo): array
{
$targets = [];
$bases = [];
foreach (self::STAT_KEYS as $key) {
$target = min(
GameConst::$defaultStatMax,
max(0, (int) ($targetInfo[$key] ?? 0))
);
$targets[$key] = $target;
$bases[$key] = min($target, GameConst::$defaultStatMin);
}
$targetTotal = array_sum($targets);
$desiredTotal = min(GameConst::$defaultStatTotal, $targetTotal);
$baseTotal = array_sum($bases);
$capacityTotal = $targetTotal - $baseTotal;
if ($capacityTotal <= 0 || $desiredTotal <= $baseTotal) {
return $bases;
}
$ratio = ($desiredTotal - $baseTotal) / $capacityTotal;
$result = [];
$fractions = [];
foreach (self::STAT_KEYS as $idx => $key) {
$raw = $bases[$key] + ($targets[$key] - $bases[$key]) * $ratio;
$result[$key] = (int) floor($raw);
$fractions[] = [
'key' => $key,
'fraction' => $raw - $result[$key],
'order' => $idx,
];
}
usort($fractions, static function (array $lhs, array $rhs): int {
$fractionOrder = $rhs['fraction'] <=> $lhs['fraction'];
return $fractionOrder !== 0 ? $fractionOrder : $lhs['order'] <=> $rhs['order'];
});
$remainder = $desiredTotal - array_sum($result);
foreach ($fractions as $fraction) {
if ($remainder <= 0) {
break;
}
$key = $fraction['key'];
if ($result[$key] >= $targets[$key]) {
continue;
}
$result[$key]++;
$remainder--;
}
return $result;
}
/**
* Returns the event stat baseline that a newly selected target receives at
* the supplied game date. Organic growth can still leave the actual stat
* above this baseline.
*
* @return array{leadership:int,strength:int,intel:int}
*/
public static function calculateUserCurrentTargetStats(
array $targetInfo,
array $env
): array {
$initialStats = self::calculateUserInitialStats($targetInfo);
$progress = self::calculateProgress(
(int) $env['startyear'],
(int) $env['year'],
(int) $env['month']
);
$result = [];
foreach (self::STAT_KEYS as $key) {
$target = min(
GameConst::$maxLevel,
max(0, (int) ($targetInfo[$key] ?? 0))
);
$result[$key] = max(
$initialStats[$key],
CentennialAllStarGrowth::statFloor(
$target,
GameConst::$defaultStatMin,
$progress
)
);
}
return $result;
}
public static function prepareInitialUser(
GeneralBuilder $builder,
array $targetInfo
): void {
$initialStats = self::calculateUserInitialStats($targetInfo);
$builder->setStat(
$initialStats['leadership'],
$initialStats['strength'],
$initialStats['intel']
);
$builder->setAuxVar(
self::AUX_KEY,
self::initialAux($targetInfo, $initialStats)
);
}
/**
* Old 100th-season characters did not distinguish their form-entered
* initial stats from organic growth. Before their first reselection, treat
* the ordinary creation range as the replaceable initial allocation.
*/
public static function prepareLegacyUserReselection(General $general): void
{
$aux = $general->getAuxVar(self::AUX_KEY);
if (!is_array($aux) || is_array($aux['userInitialStats'] ?? null)) {
return;
}
$granted = is_array($aux['granted'] ?? null)
? $aux['granted']
: array_fill_keys(array_merge(self::STAT_KEYS, self::DEX_KEYS), 0);
$legacyInitialStats = [];
foreach (self::STAT_KEYS as $key) {
$current = (int) $general->getVar($key);
$granted[$key] = self::calculateLegacyUserGrant(
$current,
(int) ($granted[$key] ?? 0)
);
$beforeEventGrant = max(
0,
$current - max(0, (int) ($aux['granted'][$key] ?? 0))
);
$legacyInitialStats[$key] = min(
$beforeEventGrant,
GameConst::$defaultStatMax
);
}
$aux['granted'] = $granted;
$aux['userInitialStats'] = $legacyInitialStats;
$general->setAuxVar(self::AUX_KEY, $aux);
}
public static function calculateLegacyUserGrant(int $current, int $eventGrant): int
{
$eventGrant = max(0, $eventGrant);
$beforeEventGrant = max(0, $current - $eventGrant);
$replaceableInitialGrant = max(
0,
min($beforeEventGrant, GameConst::$defaultStatMax)
- min($beforeEventGrant, GameConst::$defaultStatMin)
);
return $eventGrant + $replaceableInitialGrant;
}
public static function calculateProgress(
int $startYear,
int $year,
int $month,
float $progressMultiplier = 1.0
): float {
if ($progressMultiplier < 0 || $progressMultiplier > 1) {
throw new \InvalidArgumentException('progress multiplier must be between 0 and 1');
}
return min(
1,
CentennialAllStarGrowth::progress($startYear, $year, $month)
* $progressMultiplier
);
}
public static function calculateDexTargetFloor(
int $target,
array $env,
float $targetRatio = 1.0
): int {
if ($targetRatio < 0 || $targetRatio > 1) {
throw new \InvalidArgumentException('dex target ratio must be between 0 and 1');
}
$target = min(GameConst::$dexLimit, max(0, $target));
$scaledTarget = (int) floor($target * $targetRatio);
$progress = self::calculateProgress(
(int) $env['startyear'],
(int) $env['year'],
(int) $env['month']
);
return CentennialAllStarGrowth::dexFloor($scaledTarget, $progress);
}
/**
* Mutates the General object but leaves persistence to the caller.
*
* @return array{progress:float,milestone:int,previousMilestone:int,targetChanged:bool,changed:bool}
*/
public static function applyTarget(
General $general,
array $targetInfo,
array $env,
float $progressMultiplier = 1.0,
float $dexTargetRatio = 1.0
): array
{
$startYear = (int) $env['startyear'];
$year = (int) $env['year'];
$month = (int) $env['month'];
$progress = self::calculateProgress(
$startYear,
$year,
$month,
$progressMultiplier
);
$progressMonth = (int) floor(
max(0, ($year - $startYear) * 12 + $month - 1)
* $progressMultiplier
);
$targetId = (string) ($targetInfo['uniqueName'] ?? '');
$aux = $general->getAuxVar(self::AUX_KEY);
if (!is_array($aux)) {
$aux = self::initialAux($targetInfo);
}
$granted = is_array($aux['granted'] ?? null)
? $aux['granted']
: array_fill_keys(array_merge(self::STAT_KEYS, self::DEX_KEYS), 0);
$targetChanged = ($aux['targetId'] ?? '') !== $targetId;
$dexConsumed = is_array($aux['dexConsumed'] ?? null)
? $aux['dexConsumed']
: array_fill_keys(self::DEX_KEYS, 0);
if ($targetChanged) {
$dexConsumed = array_fill_keys(self::DEX_KEYS, 0);
}
$dexFloor = is_array($aux['dexFloor'] ?? null)
? $aux['dexFloor']
: array_fill_keys(self::DEX_KEYS, 0);
$isUserTarget = is_array($aux['userInitialStats'] ?? null);
$nextUserInitialStats = $targetChanged && $isUserTarget
? self::calculateUserInitialStats($targetInfo)
: ($aux['userInitialStats'] ?? null);
$previousDexTargetRatio = is_numeric($aux['dexTargetRatio'] ?? null)
? (float) $aux['dexTargetRatio']
: 1.0;
$dexTargetRatioChanged = $previousDexTargetRatio !== $dexTargetRatio;
$userCurrentTargetStats = $isUserTarget
? self::calculateUserCurrentTargetStats($targetInfo, $env)
: null;
$changed = $dexTargetRatioChanged;
foreach (self::STAT_KEYS as $key) {
if (!array_key_exists($key, $targetInfo)) {
continue;
}
$target = min(GameConst::$maxLevel, max(0, (int) $targetInfo[$key]));
$floor = CentennialAllStarGrowth::statFloor(
$target,
GameConst::$defaultStatMin,
$progress
);
if ($userCurrentTargetStats !== null) {
$floor = $userCurrentTargetStats[$key];
}
$current = (int) $general->getVar($key);
if ($targetChanged) {
$result = CentennialAllStarGrowth::replaceTarget(
$current,
(int) ($granted[$key] ?? 0),
$floor
);
} else {
$result = CentennialAllStarGrowth::advance(
$current,
(int) ($granted[$key] ?? 0),
$floor
);
}
if ($result['value'] !== $current) {
$general->updateVar($key, $result['value']);
$changed = true;
}
$granted[$key] = $result['granted'];
}
$targetDex = $targetInfo['dex'] ?? [];
foreach (self::DEX_KEYS as $idx => $key) {
if (!array_key_exists($idx, $targetDex)) {
continue;
}
$floor = max(
0,
self::calculateDexTargetFloor(
(int) $targetDex[$idx],
$env,
$dexTargetRatio
) - max(0, (int) ($dexConsumed[$key] ?? 0))
);
$dexFloor[$key] = $floor;
$current = (int) $general->getVar($key);
if ($targetChanged || $dexTargetRatioChanged) {
$result = CentennialAllStarGrowth::replaceTarget(
$current,
(int) ($granted[$key] ?? 0),
$floor
);
} else {
$result = CentennialAllStarGrowth::advance(
$current,
(int) ($granted[$key] ?? 0),
$floor
);
}
if ($result['value'] !== $current) {
$general->updateVar($key, $result['value']);
$changed = true;
}
$granted[$key] = $result['granted'];
}
$oldEventSpecial = $aux['eventSpecialDomestic'] ?? null;
if ($targetChanged && $oldEventSpecial !== null
&& $general->getVar('special') === $oldEventSpecial
) {
$general->updateVar(
'special',
$aux['naturalSpecialDomestic'] ?? GameConst::$defaultSpecialDomestic
);
$changed = true;
$aux['eventSpecialDomestic'] = null;
}
$targetSpecial = $targetInfo['specialDomestic'] ?? null;
if ($progress >= self::TRAIT_UNLOCK_PROGRESS && is_string($targetSpecial) && $targetSpecial !== '') {
if (($aux['naturalSpecialDomestic'] ?? null) === null) {
$aux['naturalSpecialDomestic'] = $general->getVar('special');
}
if ($general->getVar('special') !== $targetSpecial) {
$general->updateVar('special', $targetSpecial);
$changed = true;
}
$aux['eventSpecialDomestic'] = $targetSpecial;
}
$previousMilestone = (int) ($aux['milestone'] ?? 0);
$milestone = min(5, (int) floor($progress * 5 + 0.0000001));
$aux['targetId'] = $targetId;
$aux['granted'] = $granted;
$aux['dexConsumed'] = $dexConsumed;
$aux['dexFloor'] = $dexFloor;
$aux['progressMonth'] = max((int) ($aux['progressMonth'] ?? -1), $progressMonth);
$aux['milestone'] = max($previousMilestone, $milestone);
$aux['userInitialStats'] = $nextUserInitialStats;
$aux['dexTargetRatio'] = $dexTargetRatio;
$general->setAuxVar(self::AUX_KEY, $aux);
return [
'progress' => $progress,
'milestone' => $milestone,
'previousMilestone' => $previousMilestone,
'targetChanged' => $targetChanged,
'changed' => $changed || $targetChanged || $milestone > $previousMilestone,
];
}
public static function progressMultiplierFor(General $general): float
{
return self::progressMultiplierForNPCType($general->getNPCType());
}
public static function progressMultiplierForNPCType(int $npcType): float
{
return in_array($npcType, [3, 4], true)
? self::NPC_PROGRESS_MULTIPLIER
: 1.0;
}
public static function dexTargetRatioForNPCType(int $npcType): float
{
return in_array($npcType, [3, 4], true)
? GameConst::$centennialNpcDexTargetRatio
: 1.0;
}
public static function applyCurrentTargetToBuiltNPC(
\MeekroDB $db,
GeneralBuilder $builder,
array $targetInfo,
array $env
): ?array {
if (!self::isActive()) {
return null;
}
$general = General::createObjFromDB($builder->getGeneralID());
if (!in_array($general->getNPCType(), [3, 4], true)) {
return null;
}
self::initializeGeneratedNPC($general, $targetInfo);
$result = self::applyTarget(
$general,
$targetInfo,
$env,
self::NPC_PROGRESS_MULTIPLIER,
GameConst::$centennialNpcDexTargetRatio
);
$general->applyDB($db);
return $result;
}
/**
* Keeps the ordinary M/G-general stat total while aligning its strong,
* middle, and weak stats with the selected all-star target. The target's
* creation-date growth floor is applied immediately afterwards.
*/
public static function initializeGeneratedNPC(
General $general,
array $targetInfo
): void {
$generatedStats = [];
foreach (self::STAT_KEYS as $key) {
$generatedStats[$key] = (int) $general->getVar($key);
}
$initialStats = self::calculateGeneratedNPCInitialStats(
$targetInfo,
$generatedStats
);
foreach (self::STAT_KEYS as $key) {
$general->updateVar($key, $initialStats[$key]);
}
foreach (self::DEX_KEYS as $key) {
$general->updateVar($key, 0);
}
$general->setAuxVar(self::AUX_KEY, self::initialAux($targetInfo));
}
/**
* @param array<string, mixed> $targetInfo
* @param array{leadership:int,strength:int,intel:int} $generatedStats
* @return array{leadership:int,strength:int,intel:int}
*/
public static function calculateGeneratedNPCInitialStats(
array $targetInfo,
array $generatedStats
): array {
$targetOrder = self::STAT_KEYS;
$keyOrder = array_flip(self::STAT_KEYS);
usort(
$targetOrder,
static function (string $lhs, string $rhs) use ($targetInfo, $keyOrder): int {
$targetCompare = (int) ($targetInfo[$rhs] ?? 0)
<=> (int) ($targetInfo[$lhs] ?? 0);
return $targetCompare !== 0
? $targetCompare
: $keyOrder[$lhs] <=> $keyOrder[$rhs];
}
);
$generatedValues = array_map(
static fn (string $key): int => (int) ($generatedStats[$key] ?? 0),
self::STAT_KEYS
);
rsort($generatedValues, SORT_NUMERIC);
$result = array_fill_keys(self::STAT_KEYS, 0);
foreach ($targetOrder as $idx => $key) {
$result[$key] = $generatedValues[$idx];
}
return $result;
}
/**
* Moves the event-backed part of a dex conversion with the converted
* value and consumes any guaranteed floor crossed by the source value.
* This keeps the monthly floor from refilling points already converted.
*/
public static function reconcileDexConversion(
General $general,
string $sourceKey,
string $destinationKey,
int $sourceBefore,
int $sourceAfter,
int $destinationBefore,
int $destinationAfter,
float $convertCoeff
): void {
if (!in_array($sourceKey, self::DEX_KEYS, true)
|| !in_array($destinationKey, self::DEX_KEYS, true)
|| $sourceKey === $destinationKey
) {
throw new \InvalidArgumentException('invalid dex conversion keys');
}
if ($convertCoeff < 0 || $convertCoeff > 1) {
throw new \InvalidArgumentException('dex conversion coefficient must be between 0 and 1');
}
$sourceDecrease = max(0, $sourceBefore - $sourceAfter);
$destinationIncrease = max(0, $destinationAfter - $destinationBefore);
if ($sourceDecrease === 0 && $destinationIncrease === 0) {
return;
}
$aux = $general->getAuxVar(self::AUX_KEY);
if (!is_array($aux)) {
return;
}
$granted = is_array($aux['granted'] ?? null)
? $aux['granted']
: array_fill_keys(array_merge(self::STAT_KEYS, self::DEX_KEYS), 0);
$dexConsumed = is_array($aux['dexConsumed'] ?? null)
? $aux['dexConsumed']
: array_fill_keys(self::DEX_KEYS, 0);
$dexFloor = is_array($aux['dexFloor'] ?? null)
? $aux['dexFloor']
: [];
$sourceGrantedBefore = min(
max(0, $sourceBefore),
max(0, (int) ($granted[$sourceKey] ?? 0))
);
/*
* A dex value can contain both organic and event-backed points. Split
* the actual command deltas by their share of the source total instead
* of consuming either bucket first. This keeps the combined ownership
* stable when conversion and target reselection are interleaved.
*/
$eventGrantRemoved = $sourceBefore > 0
? intdiv($sourceDecrease * $sourceGrantedBefore, $sourceBefore)
: 0;
$sourceGrantedAfter = max(0, $sourceGrantedBefore - $eventGrantRemoved);
$destinationGrantedBefore = min(
max(0, $destinationBefore),
max(0, (int) ($granted[$destinationKey] ?? 0))
);
$eventGrantTransferred = $sourceBefore > 0
? intdiv($destinationIncrease * $sourceGrantedBefore, $sourceBefore)
: 0;
$eventGrantTransferred = min(
$destinationIncrease,
$eventGrantRemoved,
$eventGrantTransferred
);
$granted[$sourceKey] = $sourceGrantedAfter;
$granted[$destinationKey] = min(
max(0, $destinationAfter),
$destinationGrantedBefore + $eventGrantTransferred
);
$sourceFloor = max(
0,
(int) ($dexFloor[$sourceKey] ?? $sourceBefore)
);
$gapBefore = max(0, $sourceFloor - $sourceBefore);
$gapAfter = max(0, $sourceFloor - $sourceAfter);
$dexConsumed[$sourceKey] = max(
0,
(int) ($dexConsumed[$sourceKey] ?? 0)
+ max(0, $gapAfter - $gapBefore)
);
$aux['granted'] = $granted;
$aux['dexConsumed'] = $dexConsumed;
$general->setAuxVar(self::AUX_KEY, $aux);
}
public static function recordableValue(General $general, string $key): int
{
$aux = $general->getAuxVar(self::AUX_KEY);
$granted = is_array($aux) && is_array($aux['granted'] ?? null)
? (int) ($aux['granted'][$key] ?? 0)
: 0;
return CentennialAllStarGrowth::recordableValue((int) $general->getVar($key), $granted);
}
public static function recordableRawValue(array $general, string $key): int
{
$aux = Json::decode($general['aux'] ?? '{}');
$eventAux = is_array($aux[self::AUX_KEY] ?? null) ? $aux[self::AUX_KEY] : [];
$granted = is_array($eventAux['granted'] ?? null)
? (int) ($eventAux['granted'][$key] ?? 0)
: 0;
return CentennialAllStarGrowth::recordableValue((int) ($general[$key] ?? 0), $granted);
}
}
+14 -7
View File
@@ -5,6 +5,7 @@ use \sammo\{
Util, JosaUtil, DB, Util, JosaUtil, DB,
General, GameConst, General, GameConst,
ActionLogger, ActionLogger,
Json,
LastTurn, LastTurn,
NotInheritedMethodException, NotInheritedMethodException,
RandUtil RandUtil
@@ -118,6 +119,7 @@ abstract class BaseCommand{
$nationID = $this->generalObj->getNationID(); $nationID = $this->generalObj->getNationID();
if($nationID == 0){ if($nationID == 0){
$this->nation = $this->generalObj->getStaticNation(); $this->nation = $this->generalObj->getStaticNation();
$this->nation['aux'] = [];
return; return;
} }
@@ -138,7 +140,8 @@ abstract class BaseCommand{
'gold'=>0, 'gold'=>0,
'rice'=>2000, 'rice'=>2000,
'tech'=>0, 'tech'=>0,
'gennum'=>1 'gennum'=>1,
'aux'=>'{}',
]; ];
if($this->nation && $this->nation['nation'] === $nationID){ if($this->nation && $this->nation['nation'] === $nationID){
@@ -170,6 +173,10 @@ abstract class BaseCommand{
else{ else{
$this->nation = $nation; $this->nation = $nation;
} }
if(is_string($this->nation['aux'] ?? null)){
$this->nation['aux'] = Json::decode($this->nation['aux']);
}
} }
protected function setDestGeneral(General $destGeneralObj){ protected function setDestGeneral(General $destGeneralObj){
@@ -312,12 +319,12 @@ abstract class BaseCommand{
$this->generalObj->unpackAux(); $this->generalObj->unpackAux();
$constraintInput = [ $constraintInput = [
'general'=>$this->generalObj->getRaw(), 'general'=>$this->generalObj,
'city'=>$this->city, 'city'=>$this->city,
'nation'=>$this->nation, 'nation'=>$this->nation,
'cmd_arg'=>$this->arg, 'cmd_arg'=>$this->arg,
'destGeneral'=>$this->destGeneralObj?$this->destGeneralObj->getRaw():null, 'destGeneral'=>$this->destGeneralObj??null,
'destCity'=>$this->destCity, 'destCity'=>$this->destCity,
'destNation'=>$this->destNation, 'destNation'=>$this->destNation,
]; ];
@@ -346,12 +353,12 @@ abstract class BaseCommand{
$this->generalObj->unpackAux(); $this->generalObj->unpackAux();
$constraintInput = [ $constraintInput = [
'general'=>$this->generalObj->getRaw(), 'general'=>$this->generalObj,
'city'=>$this->city, 'city'=>$this->city,
'nation'=>$this->nation, 'nation'=>$this->nation,
'cmd_arg'=>$this->arg, 'cmd_arg'=>$this->arg,
'destGeneral'=>$this->destGeneralObj?$this->destGeneralObj->getRaw():null, 'destGeneral'=>$this->destGeneralObj??null,
'destCity'=>$this->destCity, 'destCity'=>$this->destCity,
'destNation'=>$this->destNation, 'destNation'=>$this->destNation,
]; ];
@@ -384,12 +391,12 @@ abstract class BaseCommand{
$this->generalObj->unpackAux(); $this->generalObj->unpackAux();
$constraintInput = [ $constraintInput = [
'general'=>$this->generalObj->getRaw(), 'general'=>$this->generalObj,
'city'=>$this->city, 'city'=>$this->city,
'nation'=>$this->nation, 'nation'=>$this->nation,
'cmd_arg'=>$this->arg, 'cmd_arg'=>$this->arg,
'destGeneral'=>$this->destGeneralObj?$this->destGeneralObj->getRaw():null, 'destGeneral'=>$this->destGeneralObj??null,
'destCity'=>$this->destCity, 'destCity'=>$this->destCity,
'destNation'=>$this->destNation, 'destNation'=>$this->destNation,
]; ];
+1 -1
View File
@@ -183,7 +183,7 @@ class che_건국 extends Command\GeneralCommand
$general->addExperience($exp); $general->addExperience($exp);
$general->addDedication($ded); $general->addDedication($ded);
$aux = Json::decode($this->nation['aux']) ?? []; $aux = $this->nation['aux'];
$aux['can_국기변경'] = 1; $aux['can_국기변경'] = 1;
$db->update('city', [ $db->update('city', [
@@ -181,7 +181,7 @@ class che_랜덤임관 extends Command\GeneralCommand
if ($notIn) { if ($notIn) {
$rawGeneralsCnt = $db->query( $rawGeneralsCnt = $db->query(
"SELECT g.`nation`, n.`gennum`, n.name, SUM((ra.value + 1000)/(rb.value + 1000)*(CASE WHEN g.`npc` < 2 THEN 1.2 ELSE 1 END)*(CASE WHEN g.`leadership` >= 40 THEN g.`leadership` ELSE 0 END)) AS warpower, SUM(SQRT(g.intel * g.strength) * 2 + g.leadership / 2)/5 AS develpower "SELECT g.`nation`, n.`gennum`, n.name, SUM((ra.value + 50000)/(rb.value + 50000)*(CASE WHEN g.`npc` < 2 THEN 1.15 ELSE 1 END)*(CASE WHEN g.`leadership` >= 40 THEN g.`leadership` ELSE 0 END)) AS warpower, SUM(SQRT(g.intel * g.strength) * 2 + g.leadership / 2)/5 AS develpower
FROM general AS g FROM general AS g
LEFT JOIN `rank_data` AS ra ON g.`no` = ra.general_id AND ra.`type` = 'killcrew_person' LEFT JOIN `rank_data` AS ra ON g.`no` = ra.general_id AND ra.`type` = 'killcrew_person'
LEFT JOIN `rank_data` AS rb ON g.`no` = rb.general_id AND rb.`type` = 'deathcrew_person' LEFT JOIN `rank_data` AS rb ON g.`no` = rb.general_id AND rb.`type` = 'deathcrew_person'
@@ -193,7 +193,7 @@ class che_랜덤임관 extends Command\GeneralCommand
); );
} else { } else {
$rawGeneralsCnt = $db->query( $rawGeneralsCnt = $db->query(
"SELECT g.`nation`, n.`gennum`, n.name, SUM((ra.value + 100)/(rb.value + 100)*(CASE WHEN g.`npc` < 2 THEN 1.2 ELSE 1 END)*(CASE WHEN g.`leadership` >= 40 THEN g.`leadership` ELSE 0 END)) AS warpower, SUM(SQRT(g.intel * g.strength) * 2 + g.leadership / 2)/5 AS develpower "SELECT g.`nation`, n.`gennum`, n.name, SUM((ra.value + 50000)/(rb.value + 50000)*(CASE WHEN g.`npc` < 2 THEN 1.15 ELSE 1 END)*(CASE WHEN g.`leadership` >= 40 THEN g.`leadership` ELSE 0 END)) AS warpower, SUM(SQRT(g.intel * g.strength) * 2 + g.leadership / 2)/5 AS develpower
FROM general AS g FROM general AS g
LEFT JOIN `rank_data` AS ra ON g.`no` = ra.general_id AND ra.`type` = 'killcrew_person' LEFT JOIN `rank_data` AS ra ON g.`no` = ra.general_id AND ra.`type` = 'killcrew_person'
LEFT JOIN `rank_data` AS rb ON g.`no` = rb.general_id AND rb.`type` = 'deathcrew_person' LEFT JOIN `rank_data` AS rb ON g.`no` = rb.general_id AND rb.`type` = 'deathcrew_person'
@@ -208,7 +208,7 @@ class che_랜덤임관 extends Command\GeneralCommand
foreach ($rawGeneralsCnt as $nation) { foreach ($rawGeneralsCnt as $nation) {
$calcCnt = $nation['warpower'] + $nation['develpower']; $calcCnt = $nation['warpower'] + $nation['develpower'];
if ($general->getNPCType() < 2 && Util::starts_with($nation['name'], 'ⓤ')) { if ($general->getNPCType() < 2 && str_starts_with($nation['name'], 'ⓤ')) {
$calcCnt *= 100; $calcCnt *= 100;
} }
@@ -194,7 +194,7 @@ class che_무작위건국 extends Command\GeneralCommand
$general->addExperience($exp); $general->addExperience($exp);
$general->addDedication($ded); $general->addDedication($ded);
$aux = Json::decode($this->nation['aux']) ?? []; $aux = $this->nation['aux'];
$aux['can_국기변경'] = 1; $aux['can_국기변경'] = 1;
$aux['can_무작위수도이전'] = 1; $aux['can_무작위수도이전'] = 1;

Some files were not shown because too many files have changed in this diff Show More