Compare commits

...
19 changed files with 156 additions and 26 deletions
+21 -5
View File
@@ -37,12 +37,12 @@ function processWar(string $warSeed, General $attackerGeneral, array $rawAttacke
$city = new WarUnitCity($rng, $rawDefenderCity, $rawDefenderNation, $year, $month, $startYear);
$defenderIDList = $db->queryFirstColumn('SELECT no FROM general WHERE nation=%i AND city=%i AND nation!=0 and crew > 0 and rice>(crew/100) and train>=defence_train and atmos>=defence_train', $city->getVar('nation'), $city->getVar('city'));
$defenderGeneralList = General::createObjListFromDB($defenderIDList, null);
$defenderCityGeneralIDList = $db->queryFirstColumn('SELECT no FROM general WHERE nation=%i AND city=%i AND nation!=0', $city->getVar('nation'), $city->getVar('city'));
$defenderCityGeneralList = General::createObjListFromDB($defenderCityGeneralIDList, null);
/** @var WarUnit[] */
$defenderList = [];
foreach($defenderGeneralList as $defenderGeneral){
foreach($defenderCityGeneralList as $defenderGeneral){
$defenderGeneral->setRawCity($rawDefenderCity);
$defenderCandidate = new WarUnitGeneral($rng, $defenderGeneral, $rawDefenderNation, false);
if(extractBattleOrder($defenderCandidate, $attacker) <= 0){
@@ -186,7 +186,7 @@ function processWar(string $warSeed, General $attackerGeneral, array $rawAttacke
'year' => $year,
'month' => $month,
'join_mode' => $joinMode,
], $attacker->getGeneral(), $city->getRaw());
], $attacker->getGeneral(), $city->getRaw(), $defenderCityGeneralList);
}
function extractBattleOrder(WarUnit $defender, WarUnit $attacker)
@@ -529,8 +529,9 @@ function getConquerNation($city): int
return Util::array_first_key($conflict);
}
function ConquerCity(array $admin, General $general, array $city)
function ConquerCity(array $admin, General $general, array $city, array $defenderCityGeneralList)
{
/** @var General[] $defenderCityGeneralList */
$db = DB::db();
$year = $admin['year'];
@@ -585,6 +586,21 @@ function ConquerCity(array $admin, General $general, array $city)
if(TurnExecutionHelper::runEventHandler($db, $gameStor, EventTarget::OccupyCity)){
$gameStor->cacheAll();
}
$rng = new RandUtil(new LiteHashDRBG(Util::simpleSerialize(
UniqueConst::$hiddenSeed,
'ConquerCity',
$year,
$month,
$attackerNationID,
$attackerID,
$cityID
)));
foreach($defenderCityGeneralList as $defenderCityGeneral){
$defenderCityGeneral->onArbitraryAction($defenderCityGeneral, $rng, 'ConquerCity', null, [
'attacker' => $general
]);
$defenderCityGeneral->applyDB($db);
}
// 국가 멸망시
@@ -0,0 +1,68 @@
<?php
namespace sammo\API\InheritAction;
use DateTimeImmutable;
use sammo\Session;
use DateTimeInterface;
use sammo\DB;
use sammo\Enums\APIRecoveryType;
use sammo\Enums\RankColumn;
use sammo\GameConst;
use sammo\General;
use sammo\KVStorage;
use sammo\LiteHashDRBG;
use sammo\RandUtil;
use sammo\TimeUtil;
use sammo\UniqueConst;
use sammo\UserLogger;
use sammo\Util;
class CalcResetTurnTimeRange extends \sammo\BaseAPI
{
public function validateArgs(): ?string
{
return null;
}
public function getRequiredSessionMode(): int
{
return static::REQ_GAME_LOGIN | static::REQ_READ_ONLY;
}
public function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag): null | string | array | APIRecoveryType
{
$userID = $session->userID;
$generalID = $session->generalID;
$general = General::createObjFromDB($generalID);
if ($userID != $general->getVar('owner')) {
return '로그인 상태가 이상합니다. 다시 로그인해 주세요.';
}
$db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env');
[$turnTerm, $serverTurnTime] = $gameStor->getValuesAsArray(['turnterm', 'turntime']);
$currTurnTime = new DateTimeImmutable($general->getTurnTime());
$serverTurnTimeObj = new DateTimeImmutable($serverTurnTime);
$minTurnTime = $currTurnTime->add(TimeUtil::secondsToDateInterval($turnTerm * -30));
$maxTurnTime = $currTurnTime->add(TimeUtil::secondsToDateInterval($turnTerm * 30));
$timeDiff = $serverTurnTimeObj->diff($minTurnTime);
$timeDiffSec = TimeUtil::DateIntervalToSeconds($timeDiff);
if($timeDiffSec > 0){
$minTurnTime = $minTurnTime->add(TimeUtil::secondsToDateInterval($timeDiffSec));
$maxTurnTime = $maxTurnTime->add(TimeUtil::secondsToDateInterval($timeDiffSec));
}
return [
'result' => true,
'timeDiffSec' => $timeDiffSec,
'minTurnTime' => TimeUtil::format($minTurnTime, false),
'maxTurnTime' => TimeUtil::format($maxTurnTime, false),
];
}
}
@@ -21,7 +21,7 @@ class che_무기_02_단궁 extends \sammo\BaseStatItem{
public function getBattlePhaseSkillTriggerList(WarUnit $unit):?WarUnitTriggerCaller{
return new WarUnitTriggerCaller(
new che_저격시도($unit, che_저격시도::TYPE_ITEM + che_저격시도::TYPE_DEDUP_TYPE_BASE * 102, 0.01, 10, 30),
new che_저격발동($unit, che_저격발동::TYPE_ITEM)
new che_저격발동($unit, che_저격시도::TYPE_ITEM + che_저격시도::TYPE_DEDUP_TYPE_BASE * 102)
);
}
}
@@ -20,7 +20,7 @@ class che_무기_07_맥궁 extends \sammo\BaseStatItem{
public function getBattlePhaseSkillTriggerList(WarUnit $unit):?WarUnitTriggerCaller{
return new WarUnitTriggerCaller(
new che_저격시도($unit, che_저격시도::TYPE_ITEM + che_저격시도::TYPE_DEDUP_TYPE_BASE * 107, 0.2, 20, 40),
new che_저격발동($unit, che_저격발동::TYPE_ITEM)
new che_저격발동($unit, che_저격시도::TYPE_ITEM + che_저격시도::TYPE_DEDUP_TYPE_BASE * 107)
);
}
}
@@ -20,7 +20,7 @@ class che_무기_09_동호비궁 extends \sammo\BaseStatItem{
public function getBattlePhaseSkillTriggerList(WarUnit $unit):?WarUnitTriggerCaller{
return new WarUnitTriggerCaller(
new che_저격시도($unit, che_저격시도::TYPE_ITEM + che_저격시도::TYPE_DEDUP_TYPE_BASE * 109, 0.2, 20, 40, 20),
new che_저격발동($unit, che_저격발동::TYPE_ITEM)
new che_저격발동($unit, che_저격시도::TYPE_ITEM + che_저격시도::TYPE_DEDUP_TYPE_BASE * 109)
);
}
}
@@ -20,7 +20,7 @@ class che_무기_11_이광궁 extends \sammo\BaseStatItem{
public function getBattlePhaseSkillTriggerList(WarUnit $unit):?WarUnitTriggerCaller{
return new WarUnitTriggerCaller(
new che_저격시도($unit, che_저격시도::TYPE_ITEM + che_저격시도::TYPE_DEDUP_TYPE_BASE * 111, 0.10, 20, 40, 10),
new che_저격발동($unit, che_저격발동::TYPE_ITEM)
new che_저격발동($unit, che_저격시도::TYPE_ITEM + che_저격시도::TYPE_DEDUP_TYPE_BASE * 111)
);
}
}
@@ -20,7 +20,7 @@ class che_무기_13_양유기궁 extends \sammo\BaseStatItem{
public function getBattlePhaseSkillTriggerList(WarUnit $unit):?WarUnitTriggerCaller{
return new WarUnitTriggerCaller(
new che_저격시도($unit, che_저격시도::TYPE_ITEM + che_저격시도::TYPE_DEDUP_TYPE_BASE * 113, 0.1, 20, 40, 10),
new che_저격발동($unit, che_저격발동::TYPE_ITEM)
new che_저격발동($unit, che_저격시도::TYPE_ITEM + che_저격시도::TYPE_DEDUP_TYPE_BASE * 113)
);
}
}
@@ -21,7 +21,7 @@ class che_저격_매화수전 extends \sammo\BaseItem{
public function getBattlePhaseSkillTriggerList(WarUnit $unit):?WarUnitTriggerCaller{
return new WarUnitTriggerCaller(
new che_저격시도($unit, che_저격시도::TYPE_ITEM+BaseWarUnitTrigger::TYPE_DEDUP_TYPE_BASE*304, 0.5, 20, 40),
new che_저격발동($unit, che_저격발동::TYPE_ITEM)
new che_저격발동($unit, che_저격시도::TYPE_ITEM+BaseWarUnitTrigger::TYPE_DEDUP_TYPE_BASE*304)
);
}
}
+1 -1
View File
@@ -21,7 +21,7 @@ class che_저격_비도 extends \sammo\BaseItem{
public function getBattlePhaseSkillTriggerList(WarUnit $unit):?WarUnitTriggerCaller{
return new WarUnitTriggerCaller(
new che_저격시도($unit, che_저격시도::TYPE_ITEM+BaseWarUnitTrigger::TYPE_DEDUP_TYPE_BASE*305, 0.5, 20, 40),
new che_저격발동($unit, che_저격발동::TYPE_ITEM)
new che_저격발동($unit, che_저격시도::TYPE_ITEM+BaseWarUnitTrigger::TYPE_DEDUP_TYPE_BASE*305)
);
}
}
@@ -25,7 +25,7 @@ class che_event_저격 extends \sammo\BaseSpecial{
public function getBattlePhaseSkillTriggerList(WarUnit $unit):?WarUnitTriggerCaller{
return new WarUnitTriggerCaller(
new che_저격시도($unit, che_저격시도::TYPE_NONE, 0.5, 20, 40),
new che_저격발동($unit)
new che_저격발동($unit, che_저격시도::TYPE_NONE)
);
}
}
@@ -169,7 +169,7 @@ class che_등용수락 extends Command\GeneralCommand{
$logger->pushGeneralActionLog("<D>{$destNationName}</>{$josaRo} 망명하여 수도로 이동합니다.");
$destLogger->pushGeneralActionLog("<Y>{$generalName}</> 등용에 성공했습니다.");
$logger->pushGeneralHistoryLog("<D>{$destNationName}</>{$josaRo} 망명");
$logger->pushGeneralHistoryLog("<D><b>{$destNationName}</b></>{$josaRo} 망명");
$destLogger->pushGeneralHistoryLog("<Y>{$generalName}</> 등용에 성공");
$logger->pushGlobalActionLog("<Y>{$generalName}</>{$josaYi} <D><b>{$destNationName}</b></>{$josaRo} <S>망명</>하였습니다.");
@@ -212,4 +212,4 @@ class che_등용수락 extends Command\GeneralCommand{
return true;
}
}
}
+1
View File
@@ -182,6 +182,7 @@ class che_급습 extends Command\NationCommand
$destNationLogger->pushNationalHistoryLog("<D><b>{$nationName}</b></>의 <Y>{$generalName}</>{$josaYi} 아국에 <M>{$commandName}</>{$josaUl} 발동");
$destNationLogger->flush();
$logger->pushGeneralHistoryLog("<D><b>{$destNationName}</b></>에 <M>{$commandName}</>{$josaUl} 발동");
$logger->pushNationalHistoryLog("<Y>{$generalName}</>{$josaYi} <D><b>{$destNationName}</b></>에 <M>{$commandName}</>{$josaUl} 발동");
$db->update('nation', [
@@ -156,7 +156,7 @@ class che_백성동원 extends Command\NationCommand
'wall' => $db->sqleval('GREATEST(wall_max * 0.8, wall)'),
], 'city=%i', $destCityID);
$logger->pushGeneralHistoryLog('<M>백성동원</>을 발동');
$logger->pushGeneralHistoryLog("<G><b>{$destCityName}</b></>에 <M>백성동원</>을 발동");
$logger->pushNationalHistoryLog("<Y>{$generalName}</>{$josaYi} <G><b>{$destCityName}</b></>에 <M>백성동원</>을 발동");
$db->update('nation', [
@@ -146,6 +146,7 @@ class che_선전포고 extends Command\NationCommand
$destLogger = new ActionLogger(0, $destNationID, $env['year'], $env['month']);
$logger->pushGeneralActionLog("<D><b>{$destNationName}</b></>에 선전 포고 했습니다.<1>$date</>");
$logger->pushGeneralHistoryLog("<D><b>{$destNationName}</b></>에 선전 포고");
$logger->pushNationalHistoryLog("<Y>{$generalName}</>{$josaYi} <D><b>{$destNationName}</b></>에 선전 포고");
$destLogger->pushNationalHistoryLog("<D><b>{$nationName}</b></>의 <Y>{$generalName}</>{$josaYi} 아국에 선전 포고");
@@ -179,6 +179,7 @@ class che_이호경식 extends Command\NationCommand
$destNationLogger->pushNationalHistoryLog("<D><b>{$nationName}</b></>의 <Y>{$generalName}</>{$josaYi} 아국에 <M>{$commandName}</>{$josaUl} 발동");
$destNationLogger->flush();
$logger->pushGeneralHistoryLog("<D><b>{$destNationName}</b></>에 <M>{$commandName}</>{$josaUl} 발동");
$logger->pushNationalHistoryLog("<Y>{$generalName}</>{$josaYi} <D><b>{$destNationName}</b></>에 <M>{$commandName}</>{$josaUl} 발동");
$db->update('nation', [
@@ -228,6 +228,7 @@ class che_피장파장 extends Command\NationCommand
$destNationLogger->pushNationalHistoryLog("<D><b>{$nationName}</b></>의 <Y>{$generalName}</>{$josaYi} 아국에 <G><b>{$cmd->getName()}</b></> <M>{$commandName}</>{$josaUl} 발동");
$destNationLogger->flush();
$logger->pushGeneralHistoryLog("<D><b>{$destNationName}</b></>에 <G><b>{$cmd->getName()}</b></> <M>{$commandName}</>{$josaUl} 발동");
$logger->pushNationalHistoryLog("<Y>{$generalName}</>{$josaYi} <D><b>{$destNationName}</b></>에 <G><b>{$cmd->getName()}</b></> <M>{$commandName}</>{$josaUl} 발동");
$nationStor = KVStorage::getStorage($db, $nationID, 'nation_env');
+1 -1
View File
@@ -2709,7 +2709,7 @@ class GeneralAI
$city = $this->city;
$nation = $this->nation;
if (($nation['rice'] < GameConst::$baserice) && $this->rng->nextBool(0.7)) {
if (($nation['rice'] < GameConst::$baserice) && $general->getNPCType() >= 2 && $this->rng->nextBool(0.7)) {
return null;
}
+46 -9
View File
@@ -107,11 +107,11 @@
<div class="col col-lg-4 col-sm-6 col-12 py-2">
<div class="row px-4">
<div class="a-right col-6 align-self-center">랜덤 초기화</div>
<BButton class="col-6" variant="primary" @click="buySimple('ResetTurnTime')"> 구입 </BButton>
<BButton class="col-6" variant="primary" @click="tryResestTurnTime()"> 구입 </BButton>
</div>
<div class="a-right">
<small class="form-text text-muted"
>다음 랜덤으로 바뀝니다. (필요 포인트가 피보나치식으로 증가합니다)<br /><span style="color: white"
>다음 시간이 , 랜덤하게 바뀝니다. (필요 포인트가 피보나치식으로 증가합니다)<br /><span style="color: white"
>필요 포인트: {{ inheritActionCost.resetTurnTime }}</span
></small
>
@@ -234,6 +234,8 @@ declare const staticValues: {
};
resetTurnTimeLevel: number;
resetSpecialWarLevel: number;
ternTurm: number;
turnTime: string;
availableSpecialWar: Record<
string,
@@ -263,6 +265,7 @@ import type { inheritBuffType, InheritPointLogItem } from "./defs/API/InheritAct
import * as JosaUtil from "@/util/JosaUtil";
import { BButton } from "bootstrap-vue-next";
import { unwrap } from "./util/unwrap";
import { add as dateAdd } from 'date-fns';
const inheritanceViewText: Record<InheritanceViewType, { title: string; info: string }> = {
sum: {
@@ -446,9 +449,42 @@ async function buyInheritBuff(buffKey: inheritBuffType) {
//TODO: 페이지 새로고침 필요없이 하도록
location.reload();
}
async function buySimple(type: "ResetTurnTime" | "BuyRandomUnique" | "ResetSpecialWar") {
async function tryResestTurnTime(){
const cost = inheritActionCost.resetTurnTime;
if (items.value.previous < cost) {
alert("유산 포인트가 부족합니다.");
return;
}
const { minTurnTime, maxTurnTime } = await SammoAPI.InheritAction.CalcResetTurnTimeRange();
//YYYY-MM-DD hh:mm:ss 에서 hh:mm:ss 까지만 보여줄 예정
const textMinTurnTime = minTurnTime.substring(11, 19);
const textMaxTurnTime = maxTurnTime.substring(11, 19);
const msg = `${cost} 포인트로 턴을 초기화 하시겠습니까?\n${textMinTurnTime} ~ ${textMaxTurnTime} 사이의 시간으로 초기화 됩니다.`;
if (!confirm(msg)) {
return;
}
try {
await SammoAPI.InheritAction.ResetTurnTime();
} catch (e) {
console.error(e);
alert(`실패했습니다: ${e}`);
return;
}
alert("성공했습니다.");
//TODO: 페이지 새로고침 필요없이 하도록
location.reload();
}
async function buySimple(type: "BuyRandomUnique" | "ResetSpecialWar") {
const costMap: Record<typeof type, number> = {
ResetTurnTime: inheritActionCost.resetTurnTime,
ResetSpecialWar: inheritActionCost.resetSpecialWar,
BuyRandomUnique: inheritActionCost.randomUnique,
};
@@ -459,15 +495,16 @@ async function buySimple(type: "ResetTurnTime" | "BuyRandomUnique" | "ResetSpeci
return;
}
const messageMap: Record<typeof type, string> = {
ResetTurnTime: `${cost} 포인트로 턴을 초기화 하시겠습니까?`,
ResetSpecialWar: `${cost} 포인트로 전투 특기를 초기화 하시겠습니까?`,
BuyRandomUnique: `${cost} 포인트로 랜덤 유니크를 구입하시겠습니까?`,
};
if (items.value.previous < cost) {
alert("유산 포인트가 부족합니다.");
return;
}
const messageMap: Record<typeof type, string> = {
ResetSpecialWar: `${cost} 포인트로 전투 특기를 초기화 하시겠습니까?`,
BuyRandomUnique: `${cost} 포인트로 랜덤 유니크를 구입하시겠습니까?`,
};
if (!confirm(messageMap[type])) {
return;
}
+5
View File
@@ -174,6 +174,11 @@ const apiRealPath = {
BuyRandomUnique: PUT as APICallT<undefined>,
ResetSpecialWar: PUT as APICallT<undefined>,
ResetTurnTime: PUT as APICallT<undefined>,
CalcResetTurnTimeRange: GET as APICallT<undefined, {
result: true,
minTurnTime: string,
maxTurnTime: string,
}>,
SetNextSpecialWar: PUT as APICallT<{
type: string;
}>,