Compare commits

...
78 Commits
Author SHA1 Message Date
Hide_D 96643567d4 game: 애니집결 시나리오의 대사 빈도를 낮춤 2023-06-24 16:53:21 +00:00
Hide_D 0a6db3d21f fix: 이민족 발동시 서버 시간 변경 문제 2023-06-23 11:27:59 +00:00
Hide_D 8a4cbbb976 game: 애니집결 10차 시나리오 2023-06-23 19:28:28 +09:00
Hide_D 696ba0b341 fix(game): 병가의 세율에 따른 인구 증감이 '정수'로 고정되는 문제
- onCalcNationalIncome에서 pop은 0~0.125 사이임
2023-06-18 13:23:01 +09:00
Hide_D 4efcbaed6a misc: d_setting에 관련 코드가 추가되어 TODO 제거 2023-06-11 21:57:45 +09:00
Hide_D eda5ecb124 feat: d_setting에 ServerEnv 추가 2023-06-11 21:56:26 +09:00
Hide_D 0ef69178a5 feat: 이민족 발동 시 장수수에 따라 턴시간 조절 2023-06-11 21:44:22 +09:00
Hide_D 5596eca326 feat: 서버 제어용 변수 공간 추가 2023-06-11 21:40:07 +09:00
Hide_D af2dec7e2c refac: 턴시간 변경에 락 무시하는 기능 추가 2023-06-11 21:27:14 +09:00
Hide_D 435dae801f refac: _admin1 턴시간 변경을 changeServerTerm으로 이전 2023-06-11 21:25:53 +09:00
Hide_D 320c8191dc feat: ServerTool::changeServerTerm
- 서버 턴 시간을 바꾸는 명령
- 기존 _admin1.php
2023-06-11 21:24:53 +09:00
Hide_D 803e18e3c3 fix: 이민족 이벤트 종료시 메시지 2023-06-11 21:02:57 +09:00
Hide_D fbac218c34 fix: 이민족 발동 시, 초기 이성이 없으면 동작하지 않도록 2023-06-10 22:41:19 +09:00
Hide_D 2e6ea3a00d fix: isunited warning 수정 2023-06-08 23:31:46 +09:00
Hide_D 1f8812c939 fix: MAX_RNG_SUPPORT_BIT warning 2023-06-08 23:23:05 +09:00
Hide_D 44c2323883 fix: 임시 땜빵 베팅 2023-06-07 00:23:36 +09:00
Hide_D 82fb532e4b fix: 조사 (으)로 미 반영부분 수정 2023-06-06 16:51:42 +09:00
Hide_D ae6ca5c1ec fix: 성벽 선제 시 분쟁 대상이 도시로 나타나지 않는 버그 수정 2023-06-06 16:41:13 +09:00
Hide_D 30ac7da4d7 misc: 이민족 생성 시, 전쟁 금지 및 임관 금지 해제 2023-06-04 20:22:45 +09:00
Hide_D 8c30634fd1 fix: 리셋 시 최근 읽은 메시지 번호를 초기화해야 함 2023-06-03 10:58:15 +09:00
Hide_D 59761743dc misc: 이민족 소환 권한을 2명에게 부여 2023-05-22 21:56:15 +09:00
Hide_D 5952dba533 fix: 천통시 이민족 소환에 npc 여부를 제대로 계산하지 않는 문제 2023-05-22 21:52:35 +09:00
Hide_D 2518ac021d fix: ProvideNPCTroopLeader 경고 우회 2023-05-22 02:48:41 +09:00
Hide_D e656c1e567 fix: 천통시 이민족 권한을 유저에게 부여
- 군주가 NPC인 경우 참모
- 참모도 NPC라면 각 수뇌까지
2023-05-22 02:43:54 +09:00
Hide_D b89c045f85 feat: 천하통일 시 군주에세 이민족이 침입 이벤트 권한 부여
- 세가지 종류 난이도
  - 천통국가 기준 [장수 수, 종합 스탯, 기술 수치, 숙련 수치]
  - 어려움:  2배, 1.2배, 최고등급, 1배
  - 보통: 2배, 1.2배, 동일 기술, 0.5배
  - 쉬움: 1배, 1배, 20% 낮은 기술, 숙련 없음
- 이민족 멸망시 타 이민족으로 임관
2023-05-22 02:41:17 +09:00
Hide_D d347054ad0 feat: 이민족 침입 메시지 타입 추가 2023-05-22 02:38:35 +09:00
Hide_D 3ebc44ea5a fix: RaiseInvader의 중첩 호줄 대비
- isunited 변수를 빠르게 1로 설정
- 만약 RaiseInavder를 빠르게 두번 호출하더라도 isunited 변수를 검사하게 하여 대응 가능
2023-05-22 02:19:47 +09:00
Hide_D 57c8c3f6a4 feat: MessageType 간이 생성 함수 추가
- General 객체에서 생성
- 왜 여태까지 이 함수가 없었을까?
2023-05-22 02:15:45 +09:00
Hide_D fee769714d feat: System MessageTarget를 빠르게 생성하는 함수 2023-05-22 01:55:09 +09:00
Hide_D 9fcb24533a feat: sendPrivateMsgAsNotice 인자로 MessageTarget 추가 2023-05-22 01:44:25 +09:00
Hide_D 1061632290 feat: 단순한 공지용 개인 메시지 전송 기능 2023-05-22 01:41:35 +09:00
Hide_D 7dc297a266 fix: 성벽 선제 후 전투 종료시 점유율 관련 문제 2023-05-20 13:04:37 +09:00
Hide_D f019a642a2 fix: 구형 관리자 도구 warning 수정 2023-05-20 12:56:57 +09:00
Hide_D 5554ac3003 fix: 성벽 전투 반영 2023-05-20 03:37:09 +09:00
Hide_D 7f9ad8b7d5 fix: 정란/벽력의 성벽선제가 정상적으로 반영되지 않는 문제
- 수비/성벽
- 점유율
2023-05-20 01:03:19 +09:00
Hide_D 82af3a2e2d fix: 수비자에 수비 도시 정보가 입력되지 않는 버그 수정 2023-05-19 19:16:39 +09:00
Hide_D 7a4fcb30dc fix: 거울세계에서 국가 규모 승급이 일어나지 않던 버그 수정 2023-05-12 01:21:12 +09:00
Hide_D 256326f5d4 game: 정란, 벽력거 설명 추가 2023-05-12 00:07:01 +09:00
Hide_D 505f615203 game: 성벽선제인 경우에만 성벽도 선제공격 2023-05-12 00:07:01 +09:00
Hide_D 4e504e9679 game: 성벽이 선제공격 2023-05-12 00:07:01 +09:00
Hide_D 18f9d87a29 fix: 성벽 선제가 호출조차 되지 않는 버그 수정 2023-05-12 00:07:01 +09:00
Hide_D 2ad0c6e509 fix: 도시 관련 전투 제어 2023-05-12 00:07:00 +09:00
Hide_D 74b9e0b4a5 feat: CrewType에 iAction 개념을 넣고 처리할 수 있도록
- GameUnitDetail에 iAction 리스트 추가

fix: 누락분 수정

fix: crewtype 미지정시 기본 병종
2023-05-12 00:05:25 +09:00
Hide_D 486ac65767 wip: process_war에서 defenderList 변경
- defenderList를 General[] 에서 WarUnit[]으로 변경
- extractBattleOrder를 WarUnit을 받도록 변경
2023-05-10 02:31:43 +09:00
Hide_D 483641bcd5 feat: extractBattleOrder 함수가 공격자를 받아서 처리할 수 있도록 변경 2023-05-10 01:28:00 +09:00
Hide_D ffdacc2cfb game: 차병종 상성 변경
- 정란: 현행 유지
- 충차: 현행 유지
- 벽력거: 받는 피해 44% 증가
  - 정란의 상성과 동일하게 변경
- 목우: 주는 피해 20% 감소
2023-05-10 00:59:56 +09:00
Hide_D 4eac86da58 game: 저지시 쌀을 소모하도록 변경
- 오르는 숙련 대비 0.25배
- 즉, 일반 공격력 대비 0.9 * 0.25만큼
2023-05-10 00:39:10 +09:00
Hide_D 77fc38c899 misc: 세력장수에서 숫자 셀에는 숫자 대소비교 필터 설정 2023-04-30 19:37:52 +09:00
Hide_D 0bf49ebfb9 game: 능력치가 상한에 도달하면 더이상 오르지 않음 2023-04-30 00:29:15 +09:00
Hide_D 93f0516121 Revert "game: 스탯 제한을 순수 스탯 대상으로만 변경"
This reverts commit 71a3ffa21c.
2023-04-30 00:22:14 +09:00
Hide_D 36406c8001 game: 물자조달도 전방 내정 감소가 적용 2023-04-30 00:15:41 +09:00
Hide_D 836adcfe3c game: 전방 내정시, 수도가 전방이어도 감쇠되도록 설정
- 185년까지는 해당 없음(디버프 * 0%)
- 이후 1년마다 실제 적용량 대비 5%씩 가산 적용됨
- 205년이 되면 수도여도 전방 내정 감쇠가 그대로 적용됨
2023-04-30 00:15:17 +09:00
Hide_D 71a3ffa21c game: 스탯 제한을 순수 스탯 대상으로만 변경 2023-04-29 23:47:47 +09:00
Hide_D 38f10b5f09 game: 최대 레벨, 능력치 제한을 255로 변경 2023-04-29 23:46:22 +09:00
Hide_D da6b1fe7f1 game: 내부 스탯 제한을 2중으로 2023-04-29 23:40:30 +09:00
Hide_D 034e6374cc game: 최대 환산 능력치를 300으로 제한 2023-04-29 23:30:11 +09:00
Hide_D 9b95163f24 fix: 명성 최대 레벨 지정이 없었음 2023-04-29 23:18:55 +09:00
Hide_D 91b7934ee3 fix: KakaoUtil의 저장 키 문제 2023-04-25 23:09:01 +09:00
Hide_D 41216893a1 fix: 감찰부 대상 API 순서에 따라 잘못 표기 되는 문제
- API 결과값과 현재 대상 비교하는 코드 추가
2023-04-23 16:53:17 +09:00
Hide_D 3553b87748 fix: 지도 제목에 툴팁 표기 고장 수정
- 동작하지 않는 vMyTooltip 제거
2023-03-30 02:39:57 +09:00
Hide_D 3e5e584fce fix: 오타 수정
- 영향받는 코드는 없었을 것으로.
2023-03-30 01:19:52 +09:00
Hide_D afb53d121b fix: 특기 초기화시 가득차면 사용 풀이 리셋되지 않는 버그 수정 2023-03-28 10:05:48 +09:00
Hide_D 1780122936 fix: 모든 내특/전특 사용시 에러나는 문제 수정 2023-03-28 09:54:24 +09:00
sars 64aa5d041f Revert "긴급 버그 패치, 내특 줄 수 있는게 하나도 없으면 강제로 상재로 주고 턴 넘기는것으로 긴급패치, 히데님이 알아서 제대로 고칠것, (2로 해두면 상재가 아니라 아에 버그가 떠서 그냥 롤백시키고 히데님이 다 고치는걸로 방치하기로 결정)"
This reverts commit fbccfbb65e.
2023-03-27 09:54:37 -07:00
sars fbccfbb65e 긴급 버그 패치, 내특 줄 수 있는게 하나도 없으면 강제로 상재로 주고 턴 넘기는것으로 긴급패치, 히데님이 알아서 제대로 고칠것 2023-03-27 09:49:00 -07:00
Hide_D 13eb0d4eae fix: 토너먼트 표기 문구 수정 2023-03-26 15:56:04 +09:00
Hide_D 3969c47f07 Revert "fix: 임시로 대문에서 로그인 체크를 직접 하도록 변경"
This reverts commit c7e0d6d2cb.
2023-03-26 15:55:38 +09:00
Hide_D c7e0d6d2cb fix: 임시로 대문에서 로그인 체크를 직접 하도록 변경 2023-03-26 15:52:06 +09:00
Hide_D 9e997a7ee2 feat: 도시 장수 정의에 nationID를 추가 2023-03-26 00:58:53 +09:00
Hide_D 9a8f70d87b fix: 감찰부 안보이는 버그 수정
- query로 옮겨서 생기는 문제
2023-03-26 00:58:32 +09:00
Hide_D 7132aeab0c fix: installVue3Components() 호출부 통일 2023-03-26 00:48:05 +09:00
Hide_D 994c73adb2 feat: 설문조사 정보도 DB정보를 이용하여 한번만 처리 2023-03-25 03:07:38 +09:00
Hide_D 9206b74500 feat: 이미 읽은 메시지를 DB에서 처리 2023-03-25 02:09:23 +09:00
Hide_D 5294aefbbd misc: 동향 변경 안내 시간 단축 2023-03-24 03:03:50 +09:00
Hide_D 8f0f586af9 fix: 버그 수정 2023-03-24 02:44:14 +09:00
Hide_D 9121533b93 fix: 국가 승급을 Event로 떼내어야 함
- 부대장 지급보다 앞으로
2023-03-24 02:43:07 +09:00
Hide_D 90bff7b210 misc: 연도 업데이트 2023-03-22 23:22:46 +09:00
Hide_D 55f7b87ced misc: GetConst의 cache key를 getVersion으로 2023-03-22 23:19:28 +09:00
72 changed files with 1870 additions and 1119 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
The MIT License
Copyright (c) 2022 Hide_D, 62che
Copyright (c) 2023 Hide_D, 62che
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+1 -22
View File
@@ -74,28 +74,7 @@ switch ($btn) {
case "120분턴": $turnterm = 120; break;
default: throw new \Exception("알 수 없는 턴 기간");
}
$oldunit = $admin['turnterm'] * 60;
$unit = $turnterm * 60;
$unitDiff = $unit / $oldunit;
$servTurnTime = new \DateTimeImmutable($admin['turntime']);
foreach ($db->query('SELECT no,turntime FROM general') as $gen) {
$genTurnTime = new \DateTimeImmutable($gen['turntime']);
$timeDiff = TimeUtil::DateIntervalToSeconds($genTurnTime->diff($servTurnTime));
$timeDiff *= $unitDiff;
$newGenTurnTime = $servTurnTime->add(TimeUtil::secondsToDateInterval($timeDiff));
$db->update('general', [
'turntime'=>$newGenTurnTime->format('Y-m-d H:i:s.u')
], 'no=%i', $gen['no']);
}
$turn = ($admin['year'] - $admin['startyear']) * 12 + $admin['month'] - 1;
$starttime = $servTurnTime->sub(TimeUtil::secondsToDateInterval($turn * $unit))->format('Y-m-d H:i:s');
$starttime = cutTurn($starttime, $turnterm, false);
$gameStor->turnterm = $turnterm;
$gameStor->starttime = $starttime;
pushGlobalHistoryLog(["<R>★</>턴시간이 <C>$btn</>으로 변경됩니다."]);
ServerTool::changeServerTerm($turnterm);
break;
}
+1 -9
View File
@@ -23,14 +23,6 @@ $ipGroupList = Util::arrayGroupBy(
'ip_c'
);
function colorBlockedName($general)
{
if (!$general['blocked']) {
return $general['name'];
}
return "<span style='color:magenta;'>{$general['name']}</span>";
}
?>
<!DOCTYPE html>
<html>
@@ -109,7 +101,7 @@ function colorBlockedName($general)
</tr>
<?php foreach ($ipGroupList as $ipGroupC => $users) : ?>
<tr>
<td><?= join('<br>', array_map('\sammo\colorBlockedName', $users)) ?></td>
<td><?= join('<br>', array_column($users, 'name')) ?></td>
<td><?= join('<br>', array_column($users, 'lastconnect')) ?></td>
<td><?= join('<br>', array_column($users, 'ip')) ?></td>
<td><?= join('<br>', array_column($users, 'owner')) ?></td>
+6
View File
@@ -0,0 +1,6 @@
<?php
namespace sammo;
class ServerEnv extends ServerDefaultEnv {
//ServerEnv 기본값을 변경하고 싶다면 orig를 제거한 복사본을 만들고 수정
}
+37
View File
@@ -326,6 +326,41 @@ function buildGeneralSpecialWarClass(?string $type):BaseSpecial{
return $obj;
}
function getActionCrewTypeClass(?string $type){
if($type === null || $type === ''){
$type = 'None';
}
static $basePath = __NAMESPACE__.'\\ActionCrewType\\';
$classPath = ($basePath.$type);
if(class_exists($classPath)){
return $classPath;
}
$classPath = ($basePath.'che_'.$type);
if(class_exists($classPath)){
return $classPath;
}
throw new \InvalidArgumentException("{$type}은 올바른 병종 효과가 아님");
}
function buildActionCrewTypeClass(?string $type):iAction{
static $cache = [];
if($type === null){
$type = 'None';
}
if(key_exists($type, $cache)){
return $cache[$type];
}
$class = getActionCrewTypeClass($type);
$obj = new $class();
$cache[$type]= $obj;
return $obj;
}
function getGeneralCommandClass(?string $type){
if($type === null || $type === ''){
$type = '휴식';
@@ -542,6 +577,8 @@ function getExpLevel($experience) {
$level = Util::toInt(sqrt($experience/10));
}
$level = Util::clamp($level, 0, GameConst::$maxLevel);
return $level;
}
+29 -206
View File
@@ -423,8 +423,6 @@ function postUpdateMonthly(RandUtil $rng)
if ($admin['year'] >= $admin['startyear'] + 2) {
checkWander($rng);
}
// 작위 업데이트
updateNationState();
updateGeneralNumber();
refreshNationStaticInfo();
// 천통여부 검사
@@ -467,209 +465,6 @@ function checkWander(RandUtil $rng)
}
}
function updateNationState()
{
$db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env');
$admin = $gameStor->getValues(['killturn', 'year', 'month', 'fiction', 'startyear', 'show_img_level', 'turnterm', 'turntime']);
$year = $admin['year'];
$month = $admin['month'];
$startYear = $admin['startyear'];
$nationCityCounts = [];
foreach($db->queryAllLists('SELECT nation, count(*) FROM city WHERE LEVEL>=4 GROUP BY nation') as [$nationID, $cityCnt]){
$nationCityCounts[$nationID] = $cityCnt;
}
$nationLevelByCityCnt = [
0, //방랑군
1, //호족
2, //군벌
5, //주자사
8, //주목
11, //공
16, //왕
21, //황제
];
foreach ($db->query('SELECT nation,name,level,tech,aux FROM nation') as $nation) {
//TODO: level이 진관수이소중대특 체계를 벗어날 수 있음
$nationID = $nation['nation'];
$cityCnt = $nationCityCounts[$nationID] ?? 0;
/** @var int */
$nationlevel = 0;
foreach($nationLevelByCityCnt as $cmpNationLevel => $cmpCityCnt){
if($cityCnt < $cmpCityCnt){
break;
}
$nationlevel = $cmpNationLevel;
}
if ($nationlevel > $nation['level']) {
$levelDiff = $nationlevel - $nation['level'];
$oldLevel = $nation['level'];
$nation['level'] = $nationlevel;
$updateVals = [
'level' => $nationlevel,
'gold' => $db->sqleval('gold + %i', $nationlevel * 1000),
'rice' => $db->sqleval('rice + %i', $nationlevel * 1000),
];
$nationName = $nation['name'];
$lordName = $db->queryFirstField('SELECT name FROM general WHERE nation = %i AND officer_level = 12', $nationID);
$oldNationLevelText = getNationLevel($oldLevel);
$nationLevelText = getNationLevel($nationlevel);
$logger = new ActionLogger(0, $nationID, $year, $month, false);
$josaYi = JosaUtil::pick($lordName, '이');
switch ($nationlevel) {
case 7: //황제
$josaRo = JosaUtil::pick($nationLevelText, '로');
$logger->pushGlobalHistoryLog("<Y><b>【작위】</b></><D><b>{$nationName}</b></> {$oldNationLevelText} <Y>{$lordName}</>{$josaYi} <C>{$nationLevelText}</>{$josaRo} 옹립되었습니다.");
$logger->pushNationalHistoryLog("<D><b>{$nationName}</b></> {$oldNationLevelText} <Y>{$lordName}</>{$josaYi} <C>{$nationLevelText}</>{$josaRo} 옹립");
$auxVal = Json::decode($nation['aux']);
$auxVal['can_국기변경'] = 1;
$auxVal['can_국호변경'] = 1;
$updateVals['aux'] = Json::encode($auxVal);
break;
case 6: //왕
$josaRo = JosaUtil::pick($nationLevelText, '로');
$logger->pushGlobalHistoryLog("<Y><b>【작위】</b></><D><b>{$nationName}</b></>의 <Y>{$lordName}</>{$josaYi} <C>$nationLevelText</>{$josaRo} 책봉되었습니다.");
$logger->pushNationalHistoryLog("<D><b>{$nationName}</b></>의 <Y>{$lordName}</>{$josaYi} <C>$nationLevelText</>{$josaRo} 책봉");
break;
case 5: //공
case 4: //주목
case 3: //주자사
$josaRo = JosaUtil::pick($nationLevelText, '로');
$logger->pushGlobalHistoryLog("<Y><b>【작위】</b></><D><b>{$nationName}</b></>의 <Y>{$lordName}</>{$josaYi} <C>$nationLevelText</>{$josaRo} 임명되었습니다.");
$logger->pushNationalHistoryLog("<D><b>{$nationName}</b></>의 <Y>{$lordName}</>{$josaYi} <C>$nationLevelText</>{$josaRo} 임명됨");
break;
case 2: //군벌
$josaRa = JosaUtil::pick($nationName, '라');
$josaRo = JosaUtil::pick($nationLevelText, '로');
$logger->pushGlobalHistoryLog("<Y><b>【작위】</b></><Y>{$lordName}</>{$josaYi} 독립하여 <D><b>{$nationName}</b></>{$josaRa}는 <C>$nationLevelText</>{$josaRo} 나섰습니다.");
$logger->pushNationalHistoryLog("<Y>{$lordName}</>{$josaYi} 독립하여 <D><b>{$nationName}</b></>{$josaRa}는 <C>$nationLevelText</>{$josaRo} 나서다");
break;
}
$db->update('nation', $updateVals, 'nation=%i', $nation['nation']);
$logger->flush();
$turnRows = [];
foreach (Util::range(getNationChiefLevel($nation['level']), 12) as $chiefLevel) {
foreach (Util::range(GameConst::$maxChiefTurn) as $turnIdx) {
$turnRows[] = [
'nation_id' => $nation['nation'],
'officer_level' => $chiefLevel,
'turn_idx' => $turnIdx,
'action' => '휴식',
'arg' => null,
'brief' => '휴식'
];
}
}
$db->insertIgnore('nation_turn', $turnRows);
if ($levelDiff) {
//유니크 아이템 하나 돌리자
$targetKillTurn = $admin['killturn'];
$targetKillTurn -= 24 * 60 / $admin['turnterm'];
$nationGenIDList = $db->queryFirstColumn(
'SELECT no FROM general WHERE nation = %i AND killturn >= %i AND npc < 2',
$nation['nation'],
$targetKillTurn
);
$nationGenList = General::createGeneralObjListFromDB($nationGenIDList, ['belong', 'npc', 'aux'], 2);
$chiefID = null;
$uniqueLotteryWeightList = [];
$relYear = $year - $startYear;
$maxTrialCountByYear = 1;
foreach (GameConst::$maxUniqueItemLimit as $tmpVals) {
[$targetYear, $targetTrialCnt] = $tmpVals;
if ($relYear < $targetYear) {
break;
}
$maxTrialCountByYear = $targetTrialCnt;
}
foreach ($nationGenList as $nationGen) {
if ($nationGen->getVar('officer_level') == 12) {
$chiefID = $nationGen->getID();
}
$trialCnt = min($maxTrialCountByYear, count(GameConst::$allItems));
foreach ($nationGen->getItems() as $item) {
if (!$item->isBuyable()) {
$trialCnt -= 1;
}
}
if ($trialCnt <= 0) {
continue;
}
$score = $nationGen->getVar('belong') + 10;
if ($nationGen->getVar('officer_level') == 12) {
$score += 60;
} else if ($nationGen->getVar('officer_level') == 11) {
$score += 30;
} else if ($nationGen->getVar('officer_level') > 4) {
$score += 15;
}
$score *= 2 ** $trialCnt;
$uniqueLotteryWeightList[$nationGen->getID()] = [$nationGen, $score];
}
$nationLevelUpRNG = new RandUtil(new LiteHashDRBG(Util::simpleSerialize(
UniqueConst::$hiddenSeed,
'nationLevelUp',
$year,
$month,
$nationID
)));
foreach (Util::range($levelDiff) as $idx) {
if (!$uniqueLotteryWeightList) {
break;
}
/** @var General */
$winnerObj = $nationLevelUpRNG->choiceUsingWeightPair($uniqueLotteryWeightList);
unset($uniqueLotteryWeightList[$winnerObj->getID()]);
$givenUniqueRNG = new RandUtil(new LiteHashDRBG(Util::simpleSerialize(
UniqueConst::$hiddenSeed,
'givenUnique',
$year,
$month,
$nationID,
$winnerObj->getID(),
)));
giveRandomUniqueItem($givenUniqueRNG, $winnerObj, '작위보상');
$winnerObj->applyDB($db);
}
if ($chiefID) {
$chiefObj = General::createGeneralObjFromDB($chiefID, ['belong', 'npc', 'aux'], 2);
$chiefObj->increaseInheritancePoint(InheritanceKey::unifier, 250 * $levelDiff);
$chiefObj->applyDB($db);
}
}
}
}
}
function checkStatistic()
{
$db = DB::db();
@@ -976,7 +771,7 @@ function checkEmperior()
$chiefs = Util::convertArrayToDict(
$db->query(
'SELECT no,name,picture,belong,officer_level FROM general WHERE nation=%i AND officer_level >= 5',
'SELECT no,npc,name,picture,belong,officer_level FROM general WHERE nation=%i AND officer_level >= 5',
$nationID
),
'officer_level'
@@ -1113,6 +908,34 @@ function checkEmperior()
//연감 월결산
LogHistory();
$availableInvaderGame = false;
foreach(CityConst::all() as $city){
if($city->level == 4){
$availableInvaderGame = true;
break;
}
}
if($availableInvaderGame){
$invaderMsgCnt = 2;
foreach(range(12, 5, -1) as $chiefLevel){
if(!key_exists($chiefLevel, $chiefs)){
continue;
}
$targetChief = $chiefs[$chiefLevel];
if($targetChief['npc'] >= 2){
continue;
}
$invaderMsgs = RaiseInvaderMessage::buildRaiseInvaderMessage($targetChief['no']);
foreach($invaderMsgs as $invaderMsg){
$invaderMsg->send();
}
$invaderMsgCnt--;
if($invaderMsgCnt <= 0){
break;
}
}
}
}
function updateMaxDomesticCritical(General $general, $score)
+277 -220
View File
@@ -1,4 +1,5 @@
<?php
namespace sammo;
use Ds\Map;
@@ -15,48 +16,48 @@ $userID = Session::getUserID();
increaseRefresh("시뮬레이터", 0);
$query = Util::getPost('query');
if($query === null){
if ($query === null) {
Json::die([
'result'=>false,
'reason'=>'입력값이 없습니다.'
'result' => false,
'reason' => '입력값이 없습니다.'
]);
}
$action = Util::getPost('action');
if($action === null || !in_array($action, ['reorder', 'battle'])){
if ($action === null || !in_array($action, ['reorder', 'battle'])) {
Json::die([
'result'=>false,
'reason'=>'원하는 동작이 지정되지 않았습니다.'
'result' => false,
'reason' => '원하는 동작이 지정되지 않았습니다.'
]);
}
$query = Json::decode($query);
if($query === null){
if ($query === null) {
Json::die([
'result'=>false,
'reason'=>'올바르지 않은 JSON입니다.'
'result' => false,
'reason' => '올바르지 않은 JSON입니다.'
]);
}
$defaultCheck = [
'required'=>[
'required' => [
'attackerGeneral', 'attackerCity', 'attackerNation',
'defenderGenerals', 'defenderCity', 'defenderNation',
'year', 'month', 'repeatCnt'
],
'integer'=>[
'year','month','repeatCnt'
'integer' => [
'year', 'month', 'repeatCnt'
],
'between'=>[
'between' => [
['month', [1, 12]]
],
'in'=>[
'in' => [
['repeatCnt', [1, 1000]]
],
'min'=>[
'min' => [
['year', 0]
],
'array'=>[
'array' => [
'attackerGeneral', 'attackerCity', 'attackerNation',
'defenderGenerals', 'defenderCity', 'defenderNation'
],
@@ -64,10 +65,10 @@ $defaultCheck = [
$v = new Validator($query);
$v->rules($defaultCheck);
if(!$v->validate()){
if (!$v->validate()) {
Json::die([
'result'=>false,
'reason'=>$v->errorStr()
'result' => false,
'reason' => $v->errorStr()
]);
}
@@ -86,20 +87,110 @@ $rawDefenderNation = $query['defenderNation'];
$warSeed = $query['seed'] ?? '';
$cityCheck = [
'required' => [
'city', 'nation', 'supply', 'name',
'pop', 'agri', 'comm', 'secu', 'def', 'wall',
'trust', 'level',
'pop_max', 'agri_max', 'comm_max', 'secu_max', 'def_max', 'wall_max',
'dead', 'state', 'conflict',
],
'numeric' => [
'pop', 'agri', 'comm', 'secu', 'def', 'wall', 'trust', 'dead'
],
'integer' => [
'city', 'nation', 'supply',
'pop_max', 'agri_max', 'comm_max', 'secu_max', 'def_max', 'wall_max',
'state',
],
'min' => [
['def', 0],
['wall', 0],
['trust', 0],
['pop', 0],
['comm', 0],
['secu', 0],
['city', 1],
['nation', 0]
],
'in' => [
['level', array_keys(getCityLevelList())]
]
];
$v = new Validator($rawAttackerCity);
$v->rules($cityCheck);
if (!$v->validate()) {
Json::die([
'result' => false,
'reason' => '[출병도시]' . $v->errorStr()
]);
}
$v = new Validator($rawDefenderCity);
$v->rules($cityCheck);
if (!$v->validate()) {
Json::die([
'result' => false,
'reason' => '[수비도시]' . $v->errorStr()
]);
}
$nationCheck = [
'required' => [
'type', 'tech', 'level', 'capital',
'nation', 'name', 'gold', 'rice', 'gennum'
],
'integer' => [
'level', 'capital', 'nation', 'gennum',
],
'numeric' => [
'tech', 'gold', 'rice'
],
'min' => [
['tech', 0],
['gold', 0],
['rice', 0],
['gennum', 1],
],
'in' => [
['type', GameConst::$availableNationType],
['level', array_keys(getNationLevelList())]
]
];
$v = new Validator($rawAttackerNation);
$v->rules($nationCheck);
if (!$v->validate()) {
Json::die([
'result' => false,
'reason' => '[출병국]' . $v->errorStr()
]);
}
$v = new Validator($rawDefenderNation);
$v->rules($nationCheck);
if (!$v->validate()) {
Json::die([
'result' => false,
'reason' => '[수비국]' . $v->errorStr()
]);
}
$generalCheck = [
'required'=>[
'required' => [
'no', 'name', 'nation', 'turntime', 'personal', 'special2', 'crew', 'crewtype', 'atmos', 'train',
'intel', 'intel_exp', 'book', 'strength', 'strength_exp', 'weapon', 'injury', 'leadership', 'leadership_exp', 'horse', 'item',
'explevel', 'experience', 'dedication', 'officer_level', 'officer_city', 'gold', 'rice', 'dex1', 'dex2', 'dex3', 'dex4', 'dex5',
'recent_war', 'warnum', 'killnum', 'killcrew',
],
'integer'=>[
'integer' => [
'no', 'nation', 'crew', 'crewtype', 'atmos', 'train',
'intel', 'intel_exp', 'strength', 'strength_exp', 'injury', 'leadership', 'leadership_exp',
'explevel', 'experience', 'dedication', 'officer_level', 'officer_city', 'gold', 'rice', 'dex1', 'dex2', 'dex3', 'dex4', 'dex5',
'warnum', 'killnum', 'killcrew',
],
'min'=>[
'min' => [
['no', 1],
['nation', 1],
['crew', 0],
@@ -118,14 +209,14 @@ $generalCheck = [
['killnum', 0],
['killcrew', 0],
],
'between'=>[
'between' => [
['train', [40, GameConst::$maxTrainByWar]],
['atmos', [40, GameConst::$maxAtmosByWar]],
['explevel', [0, 300]],
['injury', [0, 80]],
['officer_level', [1, 12]]
],
'in'=>[
'in' => [
['personal', array_merge(GameConst::$availablePersonality, GameConst::$optionalPersonality)],
['special2', array_merge(GameConst::$availableSpecialWar, GameConst::$optionalSpecialWar)],
['crewtype', array_keys(GameUnitConst::all())],
@@ -134,11 +225,11 @@ $generalCheck = [
['book', array_merge(array_keys(GameConst::$allItems['book']), ['None'])],
['item', array_merge(array_keys(GameConst::$allItems['item']), ['None'])],
],
'array'=>['inheritBuff'],
'array' => ['inheritBuff'],
];
$inheritBuffCheck = [
'between'=>[
'between' => [
[TriggerInheritBuff::WAR_AVOID_RATIO, [0, 5]],
[TriggerInheritBuff::WAR_CRITICAL_RATIO, [0, 5]],
[TriggerInheritBuff::WAR_MAGIC_TRIAL_PROB, [0, 5]],
@@ -149,49 +240,67 @@ $inheritBuffCheck = [
]
];
if(!$warSeed){
$warSeed = bin2hex(random_bytes(16));
}
else {
$repeatCnt = 1;
}
$tmpRNG = new RandUtil(new LiteHashDRBG($warSeed));
$v = new Validator($rawAttacker);
$v->rules($generalCheck);
if(!$v->validate()){
if (!$v->validate()) {
Json::die([
'result'=>false,
'reason'=>'[출병자]'.$v->errorStr()
'result' => false,
'reason' => '[출병자]' . $v->errorStr()
]);
}
$rawAttacker['aux'] = [];
if(key_exists('inheritBuff', $rawAttacker)){
if (key_exists('inheritBuff', $rawAttacker)) {
$v = new Validator($rawAttacker['inheritBuff']);
$v->rules($inheritBuffCheck);
if(!$v->validate()){
if (!$v->validate()) {
Json::die([
'result'=>false,
'reason'=>'[출병자]'.$v->errorStr()
'result' => false,
'reason' => '[출병자]' . $v->errorStr()
]);
}
$rawAttacker['aux']['inheritBuff'] = $rawAttacker['inheritBuff'];
}
$rawAttacker['aux'] = Json::encode($rawAttacker['aux']);
$rawAttacker['owner'] = 0;
$attackerGeneral = new General($rawAttacker, extractRankVar($rawAttacker), $rawAttackerCity, $rawAttackerNation, $year, $month);
$attacker = new WarUnitGeneral(
$tmpRNG,
$attackerGeneral,
$rawAttackerNation,
true
);
/** @var WarUnit[] */
$defenderList = [];
foreach($rawDefenderList as $idx=>$rawDefenderGeneral){
foreach ($rawDefenderList as $idx => $rawDefenderGeneral) {
$v = new Validator($rawDefenderGeneral);
$v->rules($generalCheck);
if(!$v->validate()){
$idx+=1;
if (!$v->validate()) {
$idx += 1;
Json::die([
'result'=>false,
'reason'=>"[수비자{$idx}]".$v->errorStr()
'result' => false,
'reason' => "[수비자{$idx}]" . $v->errorStr()
]);
}
$rawDefenderGeneral['aux'] = [];
if(key_exists('inheritBuff', $rawDefenderGeneral)){
if (key_exists('inheritBuff', $rawDefenderGeneral)) {
$v = new Validator($rawDefenderGeneral['inheritBuff']);
$v->rules($inheritBuffCheck);
if(!$v->validate()){
$idx+=1;
if (!$v->validate()) {
$idx += 1;
Json::die([
'result'=>false,
'reason'=>"[수비자{$idx}]".$v->errorStr()
'result' => false,
'reason' => "[수비자{$idx}]" . $v->errorStr()
]);
}
$rawDefenderGeneral['aux']['inheritBuff'] = $rawDefenderGeneral['inheritBuff'];
@@ -199,135 +308,54 @@ foreach($rawDefenderList as $idx=>$rawDefenderGeneral){
$rawDefenderGeneral['aux'] = Json::encode($rawDefenderGeneral['aux']);
$rawDefenderGeneral['owner'] = 0;
$defenderList[] = new General($rawDefenderGeneral, extractRankVar($rawDefenderGeneral), $rawDefenderCity, $rawAttackerNation, $year, $month, true);
$defenderGeneral = new General($rawDefenderGeneral, extractRankVar($rawDefenderGeneral), $rawDefenderCity, $rawAttackerNation, $year, $month, true);
$defenderList[] = new WarUnitGeneral(
$tmpRNG,
$defenderGeneral,
$rawDefenderNation,
false
);
}
if ($action == 'reorder') {
$noRng = NoRNG::rngInstance();
$cityCheck = [
'required'=>[
'city', 'nation', 'supply', 'name',
'pop', 'agri', 'comm', 'secu', 'def', 'wall',
'trust', 'level',
'pop_max', 'agri_max', 'comm_max', 'secu_max', 'def_max', 'wall_max',
'dead', 'state', 'conflict',
],
'numeric'=>[
'pop', 'agri', 'comm', 'secu', 'def', 'wall', 'trust', 'dead'
],
'integer'=>[
'city', 'nation', 'supply',
'pop_max', 'agri_max', 'comm_max', 'secu_max', 'def_max', 'wall_max',
'state',
],
'min'=>[
['def', 0],
['wall', 0],
['trust', 0],
['pop', 0],
['comm', 0],
['secu', 0],
['city', 1],
['nation', 0]
],
'in'=>[
['level', array_keys(getCityLevelList())]
]
];
$v = new Validator($rawAttackerCity);
$v->rules($cityCheck);
if(!$v->validate()){
Json::die([
'result'=>false,
'reason'=>'[출병도시]'.$v->errorStr()
]);
}
$v = new Validator($rawDefenderCity);
$v->rules($cityCheck);
if(!$v->validate()){
Json::die([
'result'=>false,
'reason'=>'[수비도시]'.$v->errorStr()
]);
}
$nationCheck = [
'required'=>[
'type', 'tech', 'level', 'capital',
'nation', 'name', 'gold', 'rice', 'gennum'
],
'integer'=>[
'level', 'capital', 'nation', 'gennum',
],
'numeric'=>[
'tech', 'gold', 'rice'
],
'min'=>[
['tech', 0],
['gold', 0],
['rice', 0],
['gennum', 1],
],
'in'=>[
['type', GameConst::$availableNationType],
['level', array_keys(getNationLevelList())]
]
];
$v = new Validator($rawAttackerNation);
$v->rules($nationCheck);
if(!$v->validate()){
Json::die([
'result'=>false,
'reason'=>'[출병국]'.$v->errorStr()
]);
}
$v = new Validator($rawDefenderNation);
$v->rules($nationCheck);
if(!$v->validate()){
Json::die([
'result'=>false,
'reason'=>'[수비국]'.$v->errorStr()
]);
}
if($action == 'reorder'){
usort($defenderList, function(General $lhs, General $rhs){
return -(extractBattleOrder($lhs) <=> extractBattleOrder($rhs));
usort($defenderList, function (WarUnit $lhs, WarUnit $rhs) use ($attacker) {
return - (extractBattleOrder($lhs, $attacker) <=> extractBattleOrder($rhs, $attacker));
});
$order = [];
foreach($defenderList as $defenderGeneral){
$order[] = $defenderGeneral->getID();
foreach ($defenderList as $defender) {
$order[] = $defender->getGeneral()->getID();
}
Json::die([
'result'=>true,
'reason'=>'success',
'order'=>$order
'result' => true,
'reason' => 'success',
'order' => $order
]);
}
usort($defenderList, function(General $lhs, General $rhs){
return -(extractBattleOrder($lhs) <=> extractBattleOrder($rhs));
});
$rawDefenderList = array_map(function(General $general){
return $general->getRaw();
}, $defenderList);
$rawDefenderList = [];
foreach($defenderList as $unit){
if(extractBattleOrder($unit, $attacker) <= 0){
continue;
}
$rawDefenderList[] = $unit->getRaw();
}
unset($defenderList);
$db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env');
$startYear = $gameStor->startyear;
function extractRankVar(array $rawVal):Map{
function extractRankVar(array $rawVal): Map
{
$rankVars = new Map;
foreach($rawVal as $rawKey=>$rawVal){
foreach ($rawVal as $rawKey => $rawVal) {
$key = RankColumn::tryFrom($rawKey);
if($key === null){
if ($key === null) {
continue;
}
$rankVars[$key] = $rawVal;
@@ -336,80 +364,104 @@ function extractRankVar(array $rawVal):Map{
}
function simulateBattle(
$rawAttacker, $rawAttackerCity, $rawAttackerNation,
$rawDefenderList, $rawDefenderCity, $rawDefenderNation,
$startYear, $year, $month, $warSeed
){
$rawAttacker,
$rawAttackerCity,
$rawAttackerNation,
$rawDefenderList,
$rawDefenderCity,
$rawDefenderNation,
$startYear,
$year,
$month,
$warSeed
) {
if(!$warSeed){
if (!$warSeed) {
$warSeed = bin2hex(random_bytes(16));
}
$warRng = new RandUtil(new LiteHashDRBG($warSeed));
$attackerGeneral = new General($rawAttacker, extractRankVar($rawAttacker), $rawAttackerCity, $rawAttackerNation, $year, $month);
$attacker = new WarUnitGeneral(
$warRng,
new General($rawAttacker, extractRankVar($rawAttacker), $rawAttackerCity, $rawAttackerNation, $year, $month),
$attackerGeneral,
$rawAttackerNation,
true
);
$city = new WarUnitCity($warRng, $rawDefenderCity, $rawDefenderNation, $year, $month, $startYear);
$iterDefender = new \ArrayIterator($rawDefenderList);
/** @var WarUnit[] */
$defenderList = [];
foreach ($rawDefenderList as $rawDefenderGeneral) {
$defenderGeneral = new General($rawDefenderGeneral, extractRankVar($rawDefenderGeneral), $rawDefenderCity, $rawAttackerNation, $year, $month, true);
$defenderList[] = new WarUnitGeneral(
$warRng,
$defenderGeneral,
$rawDefenderNation,
false
);
}
if(count($defenderList) && extractBattleOrder($city, $attacker) > 0){
$defenderList[] = $city;
}
usort($defenderList, function (WarUnit $lhs, WarUnit $rhs) use ($attacker) {
return - (extractBattleOrder($lhs, $attacker) <=> extractBattleOrder($rhs, $attacker));
});
$iterDefender = new \ArrayIterator($defenderList);
$iterDefender->rewind();
$battleResult = [];
$attackerRice = $rawAttacker['rice'];
$attackerRice = $attacker->getGeneral()->getVar('rice');
$defenderRice = 0;
$getNextDefender = function(?WarUnit $prevDefender, bool $reqNext)
use ($warRng, $iterDefender, $rawDefenderCity, $rawDefenderNation, $year, $month, &$battleResult, &$defenderRice) {
if($prevDefender !== null){
$getNextDefender = function (?WarUnit $prevDefender, bool $reqNext)
use ($iterDefender, &$battleResult, &$defenderRice, $attacker): ?WarUnit {
if ($prevDefender !== null) {
$prevDefender->getLogger()->rollback();
$battleResult[] = $prevDefender;
if($prevDefender instanceof WarUnitGeneral){
if ($prevDefender instanceof WarUnitGeneral) {
$defenderRice -= $prevDefender->getVar('rice');
}
}
if(!$reqNext){
if (!$reqNext) {
return null;
}
if(!$iterDefender->valid()){
if (!$iterDefender->valid()) {
return null;
}
$defenderObj = new General($iterDefender->current(), extractRankVar($iterDefender->current()), $rawDefenderCity, $rawDefenderNation, $year, $month);
if(extractBattleOrder($defenderObj) <= 0){
/** @var WarUnit */
$defender = $iterDefender->current();
if (extractBattleOrder($defender, $attacker) <= 0) {
return null;
}
$defenderRice += $defenderObj->getVar('rice');
if($defender instanceof WarUnitGeneral){
$defenderRice += $defender->getVar('rice');
}
$retVal = new WarUnitGeneral(
$warRng,
$defenderObj,
$rawDefenderNation,
false
);
$iterDefender->next();
return $retVal;
return $defender;
};
$conquerCity = processWar_NG($warSeed, $attacker, $getNextDefender, $city, $year - $startYear);
$rawDefenderCity = $city->getRaw();
$updateAttackerNation = [];
$updateDefenderNation = [];
$conquerCity = processWar_NG($warSeed, $attacker, $getNextDefender, $city);
$attackerRice -= $attacker->getVar('rice');
if($city->getPhase() > 0){
if ($city->getPhase() > 0) {
$rice = $city->getKilled() / 100 * 0.8;
$rice *= $city->getCrewType()->rice;
$rice *= getTechCost($rawDefenderNation['tech']);
$rice *= getTechCost($city->getRawNation()['tech']);
$rice *= $city->getCityTrainAtmos() / 100 - 0.2;
Util::setRound($rice);
@@ -417,8 +469,8 @@ function simulateBattle(
}
$totalDead = $attacker->getKilled() + $attacker->getDead();
$attackerCityDead = $totalDead * 0.4;
$defenderCityDead = $totalDead * 0.6;
//$attackerCityDead = $totalDead * 0.4;
//$defenderCityDead = $totalDead * 0.6;
return [$attacker, $city, $battleResult, $conquerCity, $attackerRice, $defenderRice];
}
@@ -444,18 +496,26 @@ $avgWar = 0;
$attackerActivatedSkills = [];
$defendersActivatedSkills = [];
if($warSeed){
$repeatCnt = 1;
}
foreach(Util::range($repeatCnt) as $repeatIdx){
foreach (Util::range($repeatCnt) as $repeatIdx) {
if($repeatIdx > 0){
$warSeed = bin2hex(random_bytes(16));
}
/** @var WarUnit $attacker */
[$attacker, $city, $battleResult, $conquerCity, $attackerRice, $defenderRice] = simulateBattle(
$rawAttacker, $rawAttackerCity, $rawAttackerNation,
$rawDefenderList, $rawDefenderCity, $rawDefenderNation,
$startYear, $year, $month, $warSeed
$rawAttacker,
$rawAttackerCity,
$rawAttackerNation,
$rawDefenderList,
$rawDefenderCity,
$rawDefenderNation,
$startYear,
$year,
$month,
$warSeed
);
$lastWarLog = Util::mapWithKey(function($key, $values){
$lastWarLog = Util::mapWithKey(function ($key, $values) {
return ConvertLog(join('<br>', $values));
}, $attacker->getLogger()->rollback());
@@ -478,48 +538,45 @@ foreach(Util::range($repeatCnt) as $repeatIdx){
$avgWar += count($battleResult) / $repeatCnt;
foreach($attacker->getActivatedSkillLog() as $skillName => $skillCnt){
if(!key_exists($skillName, $attackerActivatedSkills)){
foreach ($attacker->getActivatedSkillLog() as $skillName => $skillCnt) {
if (!key_exists($skillName, $attackerActivatedSkills)) {
$attackerActivatedSkills[$skillName] = $skillCnt / $repeatCnt;
}
else{
} else {
$attackerActivatedSkills[$skillName] += $skillCnt / $repeatCnt;
}
}
foreach($battleResult as $idx=>$defender){
while($idx >= count($defendersActivatedSkills)){
foreach ($battleResult as $idx => $defender) {
while ($idx >= count($defendersActivatedSkills)) {
$defendersActivatedSkills[] = [];
}
$activatedSkills = &$defendersActivatedSkills[$idx];
foreach($defender->getActivatedSkillLog() as $skillName => $skillCnt){
if(!key_exists($skillName, $activatedSkills)){
foreach ($defender->getActivatedSkillLog() as $skillName => $skillCnt) {
if (!key_exists($skillName, $activatedSkills)) {
$activatedSkills[$skillName] = $skillCnt / $repeatCnt;
}
else{
} else {
$activatedSkills[$skillName] += $skillCnt / $repeatCnt;
}
}
}
}
Json::die([
'result'=>true,
'datetime'=>$rawAttacker['turntime'],
'reason'=>'success',
'lastWarLog'=>$lastWarLog,
'avgWar'=>$avgWar,
'phase'=>$avgPhase,
'killed'=>$attackerKilled,
'maxKilled'=>$attackerMaxKilled,
'minKilled'=>$attackerMinKilled,
'dead'=>$attackerDead,
'maxDead'=>$attackerMaxDead,
'minDead'=>$attackerMinDead,
'attackerRice'=>$attackerAvgRice,
'defenderRice'=>$defenderAvgRice,
'attackerSkills'=>$attackerActivatedSkills,
'defendersSkills'=>$defendersActivatedSkills,
'result' => true,
'datetime' => $rawAttacker['turntime'],
'reason' => 'success',
'lastWarLog' => $lastWarLog,
'avgWar' => $avgWar,
'phase' => $avgPhase,
'killed' => $attackerKilled,
'maxKilled' => $attackerMaxKilled,
'minKilled' => $attackerMinKilled,
'dead' => $attackerDead,
'maxDead' => $attackerMaxDead,
'minDead' => $attackerMinDead,
'attackerRice' => $attackerAvgRice,
'defenderRice' => $defenderAvgRice,
'attackerSkills' => $attackerActivatedSkills,
'defendersSkills' => $defendersActivatedSkills,
]);
+64 -32
View File
@@ -37,16 +37,32 @@ 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'));
$defenderList = General::createGeneralObjListFromDB($defenderIDList, null, 2);
$defenderGeneralList = General::createGeneralObjListFromDB($defenderIDList, null, 2);
usort($defenderList, function (General $lhs, General $rhs) {
return - (extractBattleOrder($lhs) <=> extractBattleOrder($rhs));
/** @var WarUnit[] */
$defenderList = [];
foreach($defenderGeneralList as $defenderGeneral){
$defenderGeneral->setRawCity($rawDefenderCity);
$defenderCandidate = new WarUnitGeneral($rng, $defenderGeneral, $rawDefenderNation, false);
if(extractBattleOrder($defenderCandidate, $attacker) <= 0){
continue;
}
$defenderList[] = $defenderCandidate;
}
if(count($defenderList) && extractBattleOrder($city, $attacker) > 0){
$defenderList[] = $city;
}
usort($defenderList, function (WarUnit $lhs, WarUnit $rhs) use ($attacker) {
return - (extractBattleOrder($lhs, $attacker) <=> extractBattleOrder($rhs, $attacker));
});
$iterDefender = new \ArrayIterator($defenderList);
$iterDefender->rewind();
$getNextDefender = function (?WarUnit $prevDefender, bool $reqNext) use ($rng, $iterDefender, $rawDefenderNation, $rawDefenderCity, $db) {
$getNextDefender = function (?WarUnit $prevDefender, bool $reqNext) use ($iterDefender, $db, $attacker) {
if ($prevDefender !== null) {
$prevDefender->applyDB($db);
}
@@ -59,24 +75,16 @@ function processWar(string $warSeed, General $attackerGeneral, array $rawAttacke
return null;
}
/** @var WarUnit */
$nextDefender = $iterDefender->current();
$nextDefender->setRawCity($rawDefenderCity);
if (extractBattleOrder($nextDefender) <= 0) {
if (extractBattleOrder($nextDefender, $attacker) <= 0) {
return null;
}
$retVal = new WarUnitGeneral(
$rng,
$nextDefender,
$rawDefenderNation,
false
);
$iterDefender->next();
return $retVal;
return $nextDefender;
};
$conquerCity = processWar_NG($warSeed, $attacker, $getNextDefender, $city, $year - $startYear);
$conquerCity = processWar_NG($warSeed, $attacker, $getNextDefender, $city);
$attacker->applyDB($db);
@@ -180,8 +188,17 @@ function processWar(string $warSeed, General $attackerGeneral, array $rawAttacke
], $attacker->getGeneral(), $city->getRaw());
}
function extractBattleOrder(General $general)
function extractBattleOrder(WarUnit $defender, WarUnit $attacker)
{
if($defender instanceof WarUnitCity){
if(!($attacker instanceof WarUnitGeneral)){
return 0;
}
$attackerGeneral = $attacker->getGeneral();
return $attackerGeneral->onCalcOpposeStat($defender->getGeneral(), 'cityBattleOrder', -1);
}
$general = $defender->getGeneral();
if ($general->getVar('crew') == 0) {
return 0;
}
@@ -212,7 +229,6 @@ function processWar_NG(
WarUnitGeneral $attacker,
callable $getNextDefender,
WarUnitCity $city,
int $relYear
): bool {
$templates = new \League\Plates\Engine(__DIR__ . '/templates');
@@ -225,6 +241,7 @@ function processWar_NG(
$attackerNationUpdate = [];
$defenderNationUpdate = [];
/** @var WarUnit */
$defender = ($getNextDefender)(null, true);
$conquerCity = false;
@@ -240,6 +257,7 @@ function processWar_NG(
$logWritten = false;
if ($defender === null) {
$defender = $city;
$defender->setSiege();
if ($city->getNationVar('rice') <= 0 && $city->getVar('supply')) {
//병량 패퇴
@@ -406,21 +424,28 @@ function processWar_NG(
$attacker->logBattleResult();
$defender->logBattleResult();
$attacker->addWin();
$defender->addLose();
if (!($defender instanceof WarUnitCity) || $defender->isSiege()){
$attacker->addWin();
$defender->addLose();
$attacker->tryWound();
$defender->tryWound();
$attacker->tryWound();
$defender->tryWound();
if ($defender === $city) {
$attacker->addLevelExp(1000);
$conquerCity = true;
break;
if ($defender === $city) {
$attacker->addLevelExp(1000);
$conquerCity = true;
break;
}
}
$josaYi = JosaUtil::pick($defender->getCrewTypeName(), '이');
if ($noRice) {
if ($defender instanceof WarUnitCity && !$defender->isSiege()){
//실제 공성을 위해 다시 초기화
$defender->setOppose(null);
}
else if ($noRice) {
$logger->pushGlobalActionLog("<Y>{$defender->getName()}</>의 {$defender->getCrewTypeName()}{$josaYi} 패퇴했습니다.");
$attacker->getLogger()->pushGeneralActionLog("<Y>{$defender->getName()}</>의 {$defender->getCrewTypeName()}{$josaYi} 패퇴했습니다.", ActionLogger::PLAIN);
$defender->getLogger()->pushGeneralActionLog("군량 부족으로 패퇴합니다.", ActionLogger::PLAIN);
@@ -455,12 +480,18 @@ function processWar_NG(
$attacker->finishBattle();
$defender->finishBattle();
if ($defender instanceof WarUnitCity) {
$newConflict = $defender->addConflict();
if($city->getDead() || $defender instanceof WarUnitCity){
if($city !== $defender){
$city->setOppose($attacker);
$city->setSiege();
$city->finishBattle();
}
$newConflict = $city->addConflict();
if ($newConflict) {
$nationName = $attacker->getNationVar('name');
$josaYi = JosaUtil::pick($nationName, '이');
$logger->pushGlobalHistoryLog("<M><b>【분쟁】</b></><D><b>{$nationName}</b></>{$josaYi} <G><b>{$defender->getName()}</b></> 공략에 가담하여 분쟁이 발생하고 있습니다.");
$logger->pushGlobalHistoryLog("<M><b>【분쟁】</b></><D><b>{$nationName}</b></>{$josaYi} <G><b>{$city->getName()}</b></> 공략에 가담하여 분쟁이 발생하고 있습니다.");
}
}
@@ -664,11 +695,12 @@ function ConquerCity(array $admin, General $general, array $city)
$minCity = findNextCapital($cityID, $defenderNationID);
$minCityName = CityConst::byID($minCity)->name;
$josaRo = JosaUtil::pick($minCityName, '로');
$josaYi = JosaUtil::pick($defenderNationName, '이');
$attackerLogger->pushGlobalHistoryLog("<M><b>【긴급천도】</b></><D><b>{$defenderNationName}</b></>{$josaYi} 수도가 함락되어 <G><b>$minCityName</b></>으로 긴급천도하였습니다.");
$attackerLogger->pushGlobalHistoryLog("<M><b>【긴급천도】</b></><D><b>{$defenderNationName}</b></>{$josaYi} 수도가 함락되어 <G><b>$minCityName</b></>{$josaRo} 긴급천도하였습니다.");
$moveLog = "수도가 함락되어 <G><b>$minCityName</b></>으로 <M>긴급천도</>합니다.";
$moveLog = "수도가 함락되어 <G><b>$minCityName</b></>{$josaRo} <M>긴급천도</>합니다.";
//아국 수뇌부에게 로그 전달
foreach ($db->queryFirstColumn(
'SELECT no FROM general WHERE nation=%i AND officer_level>=5',
+9
View File
@@ -569,6 +569,14 @@ class GetFrontInfo extends \sammo\BaseAPI
//TODO: 마지막 투표, 토너먼트, 베팅을 했는지 정보를 별도로 가져와야 함. aux?
$auxInfo = [];
if ($globalInfo['lastVote']) {
$myLastVoteID = $db->queryFirstField('SELECT vote_id FROM vote WHERE general_id = %i ORDER BY vote_id DESC LIMIT 1', $generalID);
if($myLastVoteID) {
$auxInfo['myLastVote'] = $myLastVoteID;
}
}
return [
'result' => true,
@@ -577,6 +585,7 @@ class GetFrontInfo extends \sammo\BaseAPI
'nation' => $nationInfo,
'general' => $generalInfo,
'city' => $cityInfo,
'aux' => $auxInfo,
];
}
}
+2 -2
View File
@@ -86,10 +86,10 @@ class GetConst extends \sammo\BaseAPI
if ($lastModified !== null) {
$versionHash = "lt{$lastModified}";
} else {
$versionHash = VersionGit::getHash();
$versionHash = VersionGit::getVersion();
}
} else {
$versionHash = VersionGit::getHash();
$versionHash = VersionGit::getVersion();
}
$apiVersion = static::CONST_API_VERSION;
@@ -6,8 +6,10 @@ use sammo\Session;
use DateTimeInterface;
use sammo\DB;
use sammo\Enums\APIRecoveryType;
use sammo\Enums\GeneralStorKey;
use sammo\Enums\MessageType;
use sammo\Json;
use sammo\KVStorage;
use sammo\Message;
use sammo\TimeUtil;
use sammo\Util;
@@ -140,9 +142,19 @@ class GetRecentMessage extends \sammo\BaseAPI
array_pop($result[$lastType]);
}
$generalStor = KVStorage::getStorage($db, "general_{$generalID}");
[$latestReadDiplomacyMsg, $latestReadPrivateMsg] = $generalStor->getValuesAsArray([
GeneralStorKey::latestReadDiplomacyMsg,
GeneralStorKey::latestReadPrivateMsg
]);
$result['sequence'] = $nextSequence;
$result['nationID'] = $nationID;
$result['generalName'] = $generalName;
$result['latestRead'] = [
'diplomacy' => $latestReadDiplomacyMsg ?? 0,
'private' => $latestReadPrivateMsg ?? 0,
];
return $result;
}
@@ -0,0 +1,52 @@
<?php
namespace sammo\API\Message;
use sammo\DB;
use sammo\Enums\APIRecoveryType;
use sammo\Enums\GeneralStorKey;
use sammo\Enums\MessageType;
use sammo\KVStorage;
use sammo\Session;
use sammo\Validator;
class ReadLatestMessage extends \sammo\BaseAPI
{
public function validateArgs(): ?string
{
//type이 개인 메시지 혹은 외교 메시지여야함
$v = new Validator($this->args);
$v->rule('required', ['type', 'msgID'])
->rule('in', 'type', [MessageType::private->value, MessageType::diplomacy->value])
->rule('int', 'msgID');
if (!$v->validate()) {
return $v->errorStr();
}
return null;
}
public function getRequiredSessionMode(): int
{
return static::REQ_GAME_LOGIN | static::REQ_READ_ONLY;
}
public function launch(Session $session, ?\DateTimeInterface $modifiedSince, ?string $reqEtag): null | string | array | APIRecoveryType
{
$db = DB::db();
$generalID = $session->generalID;
$generalStor = KVStorage::getStorage($db, "general_{$generalID}");
$type = MessageType::from($this->args['type']);
$msgID = $this->args['msgID'];
$storKey = GeneralStorKey::latestReadPrivateMsg;
if($type == MessageType::diplomacy){
$storKey = GeneralStorKey::latestReadDiplomacyMsg;
}
$oldValue = $generalStor->getValue($storKey);
if($oldValue === null || $oldValue < $msgID){
$generalStor->setValue($storKey, $msgID);
}
return null;
}
}
+12
View File
@@ -0,0 +1,12 @@
<?php
namespace sammo\ActionCrewType;
use \sammo\iAction;
use \sammo\General;
class None implements iAction{
use \sammo\DefaultAction;
protected $id = -1;
protected $name = '-';
protected $info = '';
}
@@ -0,0 +1,20 @@
<?php
namespace sammo\ActionCrewType;
use \sammo\iAction;
use \sammo\General;
class che_성벽선제 implements iAction{
use \sammo\DefaultAction;
protected $name = '성벽선제';
protected $info = '전투 가능한 성벽이라면 선제공격을 합니다.';
public function onCalcOpposeStat(General $general, string $statName, $value, $aux = null)
{
if($statName == 'cityBattleOrder'){
// battleOrder는 스탯과 유사한 수치를 가지므로, 아주 충분히 높은값을 설정한다.
return 10000;
}
return $value;
}
}
+3 -3
View File
@@ -25,15 +25,15 @@ class che_병가 extends \sammo\BaseNation{
if($varType == 'score') return $value * 0.9;
if($varType == 'cost') return $value * 1.2;
}
return $value;
}
public function onCalcNationalIncome(string $type, $amount):int{
if($type == 'pop' && $amount > 0){
return Util::toInt($amount * 0.8);
return $amount * 0.8;
}
return $amount;
}
}
+1 -1
View File
@@ -243,7 +243,7 @@ class Betting
return $this->_calcRewardExclusive($winnerType);
}
if ($this->info->isExlusive) {
if ($this->info->isExclusive) {
return $this->_calcRewardExclusive($winnerType);
}
//아래는 2개 이상, 복합 보상 옵션
+24 -4
View File
@@ -21,6 +21,7 @@ use \sammo\Constraint\ConstraintHelper;
class che_물자조달 extends Command\GeneralCommand{
static protected $actionName = '물자조달';
static protected $debuffFront = 0.5;
protected function argTest():bool{
$this->arg = null;
@@ -95,10 +96,32 @@ class che_물자조달 extends Command\GeneralCommand{
$score = $general->onCalcDomestic('조달', 'score', $score);
$score = Util::round($score);
$scoreText = number_format($score, 0);
$exp = $score * 0.7 / 3;
$ded = $score * 1.0 / 3;
$logger = $general->getLogger();
if(in_array($this->city['front'], [1, 3])){
$debuffFront = static::$debuffFront;
if($this->nation['capital'] == $this->city['city']){
$gameStor = \sammo\KVStorage::getStorage($db, 'game_env');
[$year, $startYear] = $gameStor->getValuesAsArray(['year', 'startyear']);
$relYear = $year - $startYear;
if($relYear < 25){
$debuffScale = Util::clamp($relYear - 5, 0, 20) * 0.05;
$debuffFront = ($debuffScale * $debuffFront) + (1 - $debuffScale);
}
}
$score *= $debuffFront;
}
$scoreText = number_format($score, 0);
if($pick == 'fail'){
$logger->pushGeneralActionLog("조달을 <span class='ev_failed'>실패</span>하여 {$resName}을 <C>$scoreText</> 조달했습니다. <1>$date</>");
}
@@ -109,9 +132,6 @@ class che_물자조달 extends Command\GeneralCommand{
$logger->pushGeneralActionLog("{$resName}을 <C>$scoreText</> 조달했습니다. <1>$date</>");
}
$exp = $score * 0.7 / 3;
$ded = $score * 1.0 / 3;
$incStat = $rng->choiceUsingWeight([
'leadership_exp'=>$general->getLeadership(false, false, false, false),
'strength_exp'=>$general->getStrength(false, false, false, false),
+15 -2
View File
@@ -185,8 +185,21 @@ class che_상업투자 extends Command\GeneralCommand{
$logger->pushGeneralActionLog(static::$actionName."{$josaUl} 하여 <C>$scoreText</> 상승했습니다. <1>$date</>");
}
if(in_array($this->city['front'], [1, 3]) && $this->nation['capital'] != $this->city['city']){
$score *= static::$debuffFront;
if(in_array($this->city['front'], [1, 3])){
$debuffFront = static::$debuffFront;
if($this->nation['capital'] == $this->city['city']){
$gameStor = \sammo\KVStorage::getStorage($db, 'game_env');
[$year, $startYear] = $gameStor->getValuesAsArray(['year', 'startyear']);
$relYear = $year - $startYear;
if($relYear < 25){
$debuffScale = Util::clamp($relYear - 5, 0, 20) * 0.05;
$debuffFront = ($debuffScale * $debuffFront) + (1 - $debuffScale);
}
}
$score *= $debuffFront;
}
//NOTE: 내정량 상승시 초과 가능?
@@ -87,6 +87,12 @@ class che_전투특기초기화 extends Command\GeneralCommand{
$yearMonth = Util::joinYearMonth($env['year'], $env['month']);
$oldSpecialList = $general->getAuxVar($oldTypeKey)??[];
$oldSpecialList[] = $general->getVar(static::$specialType);
if(static::$specialType == 'special' && count($oldSpecialList) == count(GameConst::$availableSpecialDomestic)){
$oldSpecialList = [$general->getVar(static::$specialType)];
}
else if(static::$specialType == 'special2' && count($oldSpecialList) == count(GameConst::$availableSpecialWar)){
$oldSpecialList = [$general->getVar(static::$specialType)];
}
$general->setAuxVar($oldTypeKey, $oldSpecialList);
$general->setVar(static::$specialType, 'None');
@@ -165,6 +165,7 @@ class che_불가침제의 extends Command\NationCommand
$nation = $this->nation;
$nationID = $nation['nation'];
$nationName = $nation['name'];
$josaRo = JosaUtil::pick($nationName, '로');
$destNation = $this->destNation;
$destNationID = $destNation['nation'];
@@ -176,7 +177,7 @@ class che_불가침제의 extends Command\NationCommand
$logger = $general->getLogger();
$destLogger = new ActionLogger(0, $destNationID, $env['year'], $env['month']);
$logger->pushGeneralActionLog("<D><b>{$destNationName}</b></>으로 불가침 제의 서신을 보냈습니다.<1>$date</>");
$logger->pushGeneralActionLog("<D><b>{$destNationName}</b></>{$josaRo} 불가침 제의 서신을 보냈습니다.<1>$date</>");
// 상대에게 발송
$src = new MessageTarget(
@@ -122,11 +122,12 @@ class che_불가침파기제의 extends Command\NationCommand{
$destNation = $this->destNation;
$destNationID = $destNation['nation'];
$destNationName = $destNation['name'];
$josaRo = JosaUtil::pick($destNationName, '로');
$logger = $general->getLogger();
$destLogger = new ActionLogger(0, $destNationID, $env['year'], $env['month']);
$logger->pushGeneralActionLog("<D><b>{$destNationName}</b></>으로 불가침 파기 제의 서신을 보냈습니다.<1>$date</>");
$logger->pushGeneralActionLog("<D><b>{$destNationName}</b></>{$josaRo} 불가침 파기 제의 서신을 보냈습니다.<1>$date</>");
// 상대에게 발송
$src = new MessageTarget(
@@ -120,11 +120,12 @@ class che_종전제의 extends Command\NationCommand{
$destNation = $this->destNation;
$destNationID = $destNation['nation'];
$destNationName = $destNation['name'];
$josaRo = JosaUtil::pick($destNationName, '로');
$logger = $general->getLogger();
$destLogger = new ActionLogger(0, $destNationID, $env['year'], $env['month']);
$logger->pushGeneralActionLog("<D><b>{$destNationName}</b></>으로 종전 제의 서신을 보냈습니다.<1>$date</>");
$logger->pushGeneralActionLog("<D><b>{$destNationName}</b></>{$josaRo} 종전 제의 서신을 보냈습니다.<1>$date</>");
// 상대에게 발송
$src = new MessageTarget(
+12
View File
@@ -37,13 +37,25 @@ class DummyGeneral extends General
}
}
public function setCrewType(?GameUnitDetail $crewType){
$this->crewType = $crewType;
}
public function getBattleInitSkillTriggerList(WarUnit $unit): ?WarUnitTriggerCaller
{
//crewType에만 반응하자
if($this->crewType !== null){
return $this->crewType->getBattleInitSkillTriggerList($unit);
}
return new WarUnitTriggerCaller();
}
public function getBattlePhaseSkillTriggerList(WarUnit $unit): ?WarUnitTriggerCaller
{
//crewType에만 반응하자
if($this->crewType !== null){
return $this->crewType->getBattlePhaseSkillTriggerList($unit);
}
return new WarUnitTriggerCaller();
}
+7
View File
@@ -0,0 +1,7 @@
<?php
namespace sammo\Enums;
enum GeneralStorKey: string{
case latestReadPrivateMsg = 'latestReadPrivateMsg';
case latestReadDiplomacyMsg = 'latestReadDiplomacyMsg';
}
+20 -5
View File
@@ -31,20 +31,35 @@ class InvaderEnding extends \sammo\Event\Action{
$logger = new ActionLogger(0, 0, $env['year'], $env['month']);
$needStop = false;
$userWin = false;
$cityCnt = $db->queryFirstField('SELECT count(*) FROM city WHERE nation = 0');
if($cityCnt == 0){
$needStop = true;
$nationName = $db->queryFirstField('SELECT name FROM nation LIMIT 1');
if(!\str_starts_with($nationName, 'ⓞ')){
$userWin = true;
}
}
else if($cityCnt == count(CityConst::all())){
$needStop = true;
$userWin = false;
}
if(!$needStop){
return [__CLASS__, "On Event"];
}
if($userWin){
//천통 엔딩
$logger->pushGlobalHistoryLog("<L><b>【이벤트】</b></>이민족을 모두 소탕했습니다!");
$logger->pushGlobalHistoryLog("<L><b>【이벤트】</b></>중원은 당분간 태평성대를 누릴 것입니다.");
}
else if($cityCnt == count(CityConst::all())){
else {
//이민족 엔딩
$logger->pushGlobalHistoryLog("<L><b>【이벤트】</b></>중원은 이민족에 의해 혼란에 빠졌습니다.");
$logger->pushGlobalHistoryLog("<L><b>【이벤트】</b></>백성은 언젠가 영웅이 나타나길 기다립니다.");
}
else{
return [__CLASS__, "On Event"];
}
$gameStor->setValue('isunited', 3);
$logger->flush();
@@ -53,7 +68,7 @@ class InvaderEnding extends \sammo\Event\Action{
$eventID = Util::array_get($env['currentEventID']);
$db->delete('event', 'id = %i', $eventID);
return [__CLASS__, 'Deleted'];
return [__CLASS__, 'Deleted'];
}
}
@@ -41,7 +41,7 @@ class ProvideNPCTroopLeader extends \sammo\Event\Action
foreach ($db->query('SELECT nation,name,level,tech,aux FROM nation') as $nation) {
$nationID = $nation['nation'];
$maxNPCTroopLeaderCnt = self::MaxNPCTroopLeaderCnt[$nation['level']];
$maxNPCTroopLeaderCnt = self::MaxNPCTroopLeaderCnt[$nation['level']] ?? 0;
$NPCTroopLeaderCnt = $NPCTroopLeaderCntByNation[$nationID] ?? 0;
if ($NPCTroopLeaderCnt >= $maxNPCTroopLeaderCnt) {
+31 -2
View File
@@ -12,6 +12,8 @@ use sammo\LiteHashDRBG;
use sammo\RandUtil;
use sammo\Scenario\GeneralBuilder;
use sammo\Scenario\Nation;
use sammo\ServerEnv;
use sammo\ServerTool;
use sammo\UniqueConst;
use sammo\Util;
@@ -93,6 +95,7 @@ class RaiseInvader extends \sammo\Event\Action
public function run(array $env)
{
$db = DB::db();
$npcEachCount = $this->npcEachCount;
/** @var \sammo\CityInitDetail[] */
@@ -104,6 +107,16 @@ class RaiseInvader extends \sammo\Event\Action
$cities[] = $cityObj;
}
if(!$cities){
return [__CLASS__, 0];
}
$gameStor = KVStorage::getStorage($db, 'game_env');
$gameStor->setValue('isunited', 1);
$turnterm = $gameStor->turnterm;
$generalCnt = $db->queryFirstField('SELECT count(*) FROM general');
if ($npcEachCount < 0) {
$npcEachCount =
$db->queryFirstField('SELECT count(no) from general where npc < 4') / count($cities);
@@ -111,6 +124,19 @@ class RaiseInvader extends \sammo\Event\Action
}
$npcEachCount = max(10, Util::toInt($npcEachCount));
$totalGenerals = $npcEachCount * count($cities) + $generalCnt;
if ($totalGenerals > ServerEnv::$maxGeneralsPerMinute * $turnterm) {
foreach ([1, 2, 5, 10, 20, 30, 60, 120] as $nextTurnterm) {
if ($totalGenerals > ServerEnv::$maxGeneralsPerMinute * $nextTurnterm) {
continue;
}
ServerTool::changeServerTerm($nextTurnterm, true);
break;
}
}
$specAvg = $this->specAvg;
if ($specAvg < 0) {
$specAvg = $db->queryFirstField('SELECT avg((`leadership` + `strength` + `intel`)) from general where npc < 4');
@@ -140,6 +166,11 @@ class RaiseInvader extends \sammo\Event\Action
$month,
)));
$db->update('nation', [
'war' => 0,
'scout' => 0,
], '1');
$disabledInvaderCity = $this->moveCapital($rng);
$serverID = UniqueConst::$serverID;
$existNations = $db->queryFirstColumn("SELECT nation FROM `nation`");
@@ -260,8 +291,6 @@ class RaiseInvader extends \sammo\Event\Action
'secu' => $db->sqleval('secu_max'),
], true);
$gameStor = KVStorage::getStorage($db, 'game_env');
$gameStor->setValue('isunited', 1);
refreshNationStaticInfo();
$logger = new ActionLogger(0, 0, $year, $env['month']);
@@ -0,0 +1,225 @@
<?php
namespace sammo\Event\Action;
use sammo\ActionLogger;
use sammo\CityConst;
use sammo\DB;
use sammo\Enums\InheritanceKey;
use sammo\GameConst;
use sammo\General;
use sammo\JosaUtil;
use sammo\Json;
use sammo\KVStorage;
use sammo\LiteHashDRBG;
use sammo\RandUtil;
use sammo\UniqueConst;
use sammo\Util;
use function sammo\getNationChiefLevel;
use function sammo\getNationLevel;
use function sammo\giveRandomUniqueItem;
class UpdateNationLevel extends \sammo\Event\Action
{
public function run(array $env)
{
$db = DB::db();
$year = $env['year'];
$month = $env['month'];
$startYear = $env['startyear'];
$nationCityCounts = [];
foreach ($db->queryAllLists('SELECT nation, count(*) FROM city WHERE LEVEL>=4 GROUP BY nation') as [$nationID, $cityCnt]) {
$nationCityCounts[$nationID] = $cityCnt;
}
$nationLevelByCityCnt = [
0, //방랑군
1, //호족
2, //군벌
5, //주자사
8, //주목
11, //공
16, //왕
21, //황제
];
foreach ($db->query('SELECT nation,name,level,tech,aux FROM nation') as $nation) {
//TODO: level이 진관수이소중대특 체계를 벗어날 수 있음
$nationID = $nation['nation'];
$cityCnt = $nationCityCounts[$nationID] ?? 0;
/** @var int */
$nationlevel = 0;
foreach ($nationLevelByCityCnt as $cmpNationLevel => $cmpCityCnt) {
if ($cityCnt < $cmpCityCnt) {
break;
}
$nationlevel = $cmpNationLevel;
}
if ($nationlevel > $nation['level']) {
$levelDiff = $nationlevel - $nation['level'];
$oldLevel = $nation['level'];
$nation['level'] = $nationlevel;
$updateVals = [
'level' => $nationlevel,
'gold' => $db->sqleval('gold + %i', $nationlevel * 1000),
'rice' => $db->sqleval('rice + %i', $nationlevel * 1000),
];
$nationName = $nation['name'];
$lordName = $db->queryFirstField('SELECT name FROM general WHERE nation = %i AND officer_level = 12', $nationID);
$oldNationLevelText = getNationLevel($oldLevel);
$nationLevelText = getNationLevel($nationlevel);
$logger = new ActionLogger(0, $nationID, $year, $month, false);
$josaYi = JosaUtil::pick($lordName, '이');
switch ($nationlevel) {
case 7: //황제
$josaRo = JosaUtil::pick($nationLevelText, '로');
$logger->pushGlobalHistoryLog("<Y><b>【작위】</b></><D><b>{$nationName}</b></> {$oldNationLevelText} <Y>{$lordName}</>{$josaYi} <C>{$nationLevelText}</>{$josaRo} 옹립되었습니다.");
$logger->pushNationalHistoryLog("<D><b>{$nationName}</b></> {$oldNationLevelText} <Y>{$lordName}</>{$josaYi} <C>{$nationLevelText}</>{$josaRo} 옹립");
$auxVal = Json::decode($nation['aux']);
$auxVal['can_국기변경'] = 1;
$auxVal['can_국호변경'] = 1;
$updateVals['aux'] = Json::encode($auxVal);
break;
case 6: //왕
$josaRo = JosaUtil::pick($nationLevelText, '로');
$logger->pushGlobalHistoryLog("<Y><b>【작위】</b></><D><b>{$nationName}</b></>의 <Y>{$lordName}</>{$josaYi} <C>$nationLevelText</>{$josaRo} 책봉되었습니다.");
$logger->pushNationalHistoryLog("<D><b>{$nationName}</b></>의 <Y>{$lordName}</>{$josaYi} <C>$nationLevelText</>{$josaRo} 책봉");
break;
case 5: //공
case 4: //주목
case 3: //주자사
$josaRo = JosaUtil::pick($nationLevelText, '로');
$logger->pushGlobalHistoryLog("<Y><b>【작위】</b></><D><b>{$nationName}</b></>의 <Y>{$lordName}</>{$josaYi} <C>$nationLevelText</>{$josaRo} 임명되었습니다.");
$logger->pushNationalHistoryLog("<D><b>{$nationName}</b></>의 <Y>{$lordName}</>{$josaYi} <C>$nationLevelText</>{$josaRo} 임명됨");
break;
case 2: //군벌
$josaRa = JosaUtil::pick($nationName, '라');
$josaRo = JosaUtil::pick($nationLevelText, '로');
$logger->pushGlobalHistoryLog("<Y><b>【작위】</b></><Y>{$lordName}</>{$josaYi} 독립하여 <D><b>{$nationName}</b></>{$josaRa}는 <C>$nationLevelText</>{$josaRo} 나섰습니다.");
$logger->pushNationalHistoryLog("<Y>{$lordName}</>{$josaYi} 독립하여 <D><b>{$nationName}</b></>{$josaRa}는 <C>$nationLevelText</>{$josaRo} 나서다");
break;
}
$db->update('nation', $updateVals, 'nation=%i', $nation['nation']);
$logger->flush();
$turnRows = [];
foreach (Util::range(getNationChiefLevel($nation['level']), 12) as $chiefLevel) {
foreach (Util::range(GameConst::$maxChiefTurn) as $turnIdx) {
$turnRows[] = [
'nation_id' => $nation['nation'],
'officer_level' => $chiefLevel,
'turn_idx' => $turnIdx,
'action' => '휴식',
'arg' => null,
'brief' => '휴식'
];
}
}
$db->insertIgnore('nation_turn', $turnRows);
if ($levelDiff) {
//유니크 아이템 하나 돌리자
$targetKillTurn = $env['killturn'];
$targetKillTurn -= 24 * 60 / $env['turnterm'];
$nationGenIDList = $db->queryFirstColumn(
'SELECT no FROM general WHERE nation = %i AND killturn >= %i AND npc < 2',
$nation['nation'],
$targetKillTurn
);
$nationGenList = General::createGeneralObjListFromDB($nationGenIDList, ['belong', 'npc', 'aux'], 2);
$chiefID = null;
$uniqueLotteryWeightList = [];
$relYear = $year - $startYear;
$maxTrialCountByYear = 1;
foreach (GameConst::$maxUniqueItemLimit as $tmpVals) {
[$targetYear, $targetTrialCnt] = $tmpVals;
if ($relYear < $targetYear) {
break;
}
$maxTrialCountByYear = $targetTrialCnt;
}
foreach ($nationGenList as $nationGen) {
if ($nationGen->getVar('officer_level') == 12) {
$chiefID = $nationGen->getID();
}
$trialCnt = min($maxTrialCountByYear, count(GameConst::$allItems));
foreach ($nationGen->getItems() as $item) {
if (!$item->isBuyable()) {
$trialCnt -= 1;
}
}
if ($trialCnt <= 0) {
continue;
}
$score = $nationGen->getVar('belong') + 10;
if ($nationGen->getVar('officer_level') == 12) {
$score += 60;
} else if ($nationGen->getVar('officer_level') == 11) {
$score += 30;
} else if ($nationGen->getVar('officer_level') > 4) {
$score += 15;
}
$score *= 2 ** $trialCnt;
$uniqueLotteryWeightList[$nationGen->getID()] = [$nationGen, $score];
}
$nationLevelUpRNG = new RandUtil(new LiteHashDRBG(Util::simpleSerialize(
UniqueConst::$hiddenSeed,
'nationLevelUp',
$year,
$month,
$nationID
)));
foreach (Util::range($levelDiff) as $idx) {
if (!$uniqueLotteryWeightList) {
break;
}
/** @var General */
$winnerObj = $nationLevelUpRNG->choiceUsingWeightPair($uniqueLotteryWeightList);
unset($uniqueLotteryWeightList[$winnerObj->getID()]);
$givenUniqueRNG = new RandUtil(new LiteHashDRBG(Util::simpleSerialize(
UniqueConst::$hiddenSeed,
'givenUnique',
$year,
$month,
$nationID,
$winnerObj->getID(),
)));
giveRandomUniqueItem($givenUniqueRNG, $winnerObj, '작위보상');
$winnerObj->applyDB($db);
}
if ($chiefID) {
$chiefObj = General::createGeneralObjFromDB($chiefID, ['belong', 'npc', 'aux'], 2);
$chiefObj->increaseInheritancePoint(InheritanceKey::unifier, 250 * $levelDiff);
$chiefObj->applyDB($db);
}
}
}
}
}
}
+4
View File
@@ -83,6 +83,9 @@ class GameConstBase
/** @var int 최대 하야 패널티 수 */
public static $maxBetrayCnt = 9;
/** @var int 최대 레벨 */
public static $maxLevel = 255;
/** @var int 최소 인구 증가량 */
public static $basePopIncreaseAmount = 5000;
/** @var int 증축시 인구 증가량 */
@@ -486,6 +489,7 @@ class GameConstBase
[
"month", 1000,
true,
["UpdateNationLevel"],
["ProvideNPCTroopLeader"]
],
[
+42 -40
View File
@@ -41,7 +41,7 @@ class GameUnitConstBase{
[],//성벽은 공격할 수 없다.
[self::T_FOOTMAN=>1.2],
['성벽입니다.','생성할 수 없습니다.'],
null, null
null, ['che_선제사격시도', 'che_선제사격발동'], null
],
[
@@ -51,7 +51,7 @@ class GameUnitConstBase{
[self::T_ARCHER=>1.2, self::T_CAVALRY=>0.8, self::T_SIEGE=>1.2],
[self::T_ARCHER=>0.8, self::T_CAVALRY=>1.2, self::T_SIEGE=>0.8],
['표준적인 보병입니다.','보병은 방어특화이며,','상대가 회피하기 어렵습니다.'],
null, ['che_방어력증가5p']
null, ['che_방어력증가5p'], null
],
[
1101, self::T_FOOTMAN, '청주병',
@@ -60,7 +60,7 @@ class GameUnitConstBase{
[self::T_ARCHER=>1.2, self::T_CAVALRY=>0.8, self::T_SIEGE=>1.2],
[self::T_ARCHER=>0.8, self::T_CAVALRY=>1.2, self::T_SIEGE=>0.8],
['저렴하고 튼튼합니다.'],
null, ['che_방어력증가5p']
null, ['che_방어력증가5p'], null
],
[
1102, self::T_FOOTMAN, '수병',
@@ -69,7 +69,7 @@ class GameUnitConstBase{
[self::T_ARCHER=>1.2, self::T_CAVALRY=>0.8, self::T_SIEGE=>1.2],
[self::T_ARCHER=>0.8, self::T_CAVALRY=>1.2, self::T_SIEGE=>0.8],
['저렴하고 강력합니다.'],
null, ['che_방어력증가5p']
null, ['che_방어력증가5p'], null
],
[
1103, self::T_FOOTMAN, '자객병',
@@ -78,7 +78,7 @@ class GameUnitConstBase{
[self::T_ARCHER=>1.2, self::T_CAVALRY=>0.8, self::T_SIEGE=>1.2],
[self::T_ARCHER=>0.8, self::T_CAVALRY=>1.2, self::T_SIEGE=>0.8],
['은밀하고 날쌥니다.'],
null, ['che_방어력증가5p']
null, ['che_방어력증가5p'], null
],
[
1104, self::T_FOOTMAN, '근위병',
@@ -87,7 +87,7 @@ class GameUnitConstBase{
[self::T_ARCHER=>1.2, self::T_CAVALRY=>0.8, self::T_SIEGE=>1.2],
[self::T_ARCHER=>0.8, self::T_CAVALRY=>1.2, self::T_SIEGE=>0.8],
['최강의 보병입니다.'],
null, ['che_방어력증가5p']
null, ['che_방어력증가5p'], null
],
[
1105, self::T_FOOTMAN, '등갑병',
@@ -96,7 +96,7 @@ class GameUnitConstBase{
[self::T_ARCHER=>1.2, self::T_CAVALRY=>0.8, self::T_SIEGE=>1.2],
[self::T_ARCHER=>0.8, self::T_CAVALRY=>1.2, self::T_SIEGE=>0.8],
['등갑을 두른 보병입니다.'],
null, ['che_방어력증가5p']
null, ['che_방어력증가5p'], null
],
[
1106, self::T_FOOTMAN, '백이병',
@@ -105,7 +105,7 @@ class GameUnitConstBase{
[self::T_ARCHER=>1.1, self::T_CAVALRY=>0.9, self::T_SIEGE=>1.1],
[self::T_ARCHER=>0.9, self::T_CAVALRY=>1.1, self::T_SIEGE=>0.9],
['정예 보병입니다. 불리한 싸움도 버텨냅니다.'],
null, ['che_방어력증가5p']
null, ['che_방어력증가5p'], null
],
[
@@ -115,7 +115,7 @@ class GameUnitConstBase{
[self::T_CAVALRY=>1.2, self::T_FOOTMAN=>0.8, self::T_SIEGE=>1.2],
[self::T_CAVALRY=>0.8, self::T_FOOTMAN=>1.2, self::T_SIEGE=>0.8],
['표준적인 궁병입니다.','궁병은 선제사격을 하는 병종입니다.'],
null, ['che_선제사격시도', 'che_선제사격발동']
null, ['che_선제사격시도', 'che_선제사격발동'], null
],
[
1201, self::T_ARCHER, '궁기병',
@@ -124,7 +124,7 @@ class GameUnitConstBase{
[self::T_CAVALRY=>1.2, self::T_FOOTMAN=>0.9, self::T_SIEGE=>1.2],
[self::T_CAVALRY=>0.8, self::T_FOOTMAN=>1.1, self::T_SIEGE=>0.8],
['말을 타고 잘 피합니다. 특히 다른 궁병보다 보병에게 조금 더 강합니다.'],
null, ['che_선제사격시도', 'che_선제사격발동']
null, ['che_선제사격시도', 'che_선제사격발동'], null
],
[
1202, self::T_ARCHER, '연노병',
@@ -133,7 +133,7 @@ class GameUnitConstBase{
[self::T_CAVALRY=>1.2, self::T_FOOTMAN=>0.8, self::T_SIEGE=>1.2],
[self::T_CAVALRY=>0.8, self::T_FOOTMAN=>1.2, self::T_SIEGE=>0.8],
['화살을 연사합니다.'],
null, ['che_선제사격시도', 'che_선제사격발동']
null, ['che_선제사격시도', 'che_선제사격발동'], null
],
[
1203, self::T_ARCHER, '강궁병',
@@ -142,7 +142,7 @@ class GameUnitConstBase{
[self::T_CAVALRY=>1.2, self::T_FOOTMAN=>0.8, self::T_SIEGE=>1.2],
[self::T_CAVALRY=>0.8, self::T_FOOTMAN=>1.2, self::T_SIEGE=>0.8],
['강건한 궁병입니다.'],
null, ['che_선제사격시도', 'che_선제사격발동']
null, ['che_선제사격시도', 'che_선제사격발동'], null
],
[
1204, self::T_ARCHER, '석궁병',
@@ -151,7 +151,7 @@ class GameUnitConstBase{
[self::T_CAVALRY=>1.2, self::T_FOOTMAN=>0.8, self::T_SIEGE=>1.2],
[self::T_CAVALRY=>0.8, self::T_FOOTMAN=>1.2, self::T_SIEGE=>0.8],
['강력한 화살을 쏩니다.'],
null, ['che_선제사격시도', 'che_선제사격발동']
null, ['che_선제사격시도', 'che_선제사격발동'], null
],
[
@@ -161,7 +161,7 @@ class GameUnitConstBase{
[self::T_FOOTMAN=>1.2, self::T_ARCHER=>0.8, self::T_SIEGE=>1.2],
[self::T_FOOTMAN=>0.8, self::T_ARCHER=>1.2, self::T_SIEGE=>0.8],
['표준적인 기병입니다.','기병은 공격특화입니다.'],
null, ['che_기병병종전투']
null, ['che_기병병종전투'], null
],
[
1301, self::T_CAVALRY, '백마병',
@@ -170,7 +170,7 @@ class GameUnitConstBase{
[self::T_FOOTMAN=>1.2, self::T_ARCHER=>0.8, self::T_SIEGE=>1.2],
[self::T_FOOTMAN=>0.8, self::T_ARCHER=>1.2, self::T_SIEGE=>0.8],
['백마의 위용을 보여줍니다.'],
null, ['che_기병병종전투']
null, ['che_기병병종전투'], null
],
[
1302, self::T_CAVALRY, '중장기병',
@@ -179,7 +179,7 @@ class GameUnitConstBase{
[self::T_FOOTMAN=>1.2, self::T_ARCHER=>0.8, self::T_SIEGE=>1.2],
[self::T_FOOTMAN=>0.8, self::T_ARCHER=>1.2, self::T_SIEGE=>0.8],
['갑주를 두른 기병입니다.'],
null, ['che_기병병종전투']
null, ['che_기병병종전투'], null
],
[
1303, self::T_CAVALRY, '돌격기병',
@@ -188,7 +188,7 @@ class GameUnitConstBase{
[self::T_FOOTMAN=>1.2, self::T_ARCHER=>0.8, self::T_SIEGE=>1.2],
[self::T_FOOTMAN=>0.8, self::T_ARCHER=>1.2, self::T_SIEGE=>0.8],
['저돌적으로 공격합니다.'],
null, ['che_기병병종전투']
null, ['che_기병병종전투'], null
],
[
1304, self::T_CAVALRY, '철기병',
@@ -197,7 +197,7 @@ class GameUnitConstBase{
[self::T_FOOTMAN=>1.2, self::T_ARCHER=>0.8, self::T_SIEGE=>1.2],
[self::T_FOOTMAN=>0.8, self::T_ARCHER=>1.2, self::T_SIEGE=>0.8],
['철갑을 두른 기병입니다.'],
null, ['che_기병병종전투']
null, ['che_기병병종전투'], null
],
[
1305, self::T_CAVALRY, '수렵기병',
@@ -206,7 +206,7 @@ class GameUnitConstBase{
[self::T_FOOTMAN=>1.2, self::T_ARCHER=>0.8, self::T_SIEGE=>1.2],
[self::T_FOOTMAN=>0.8, self::T_ARCHER=>1.2, self::T_SIEGE=>0.8],
['날쎄고 빠른 기병입니다.'],
null, ['che_기병병종전투']
null, ['che_기병병종전투'], null
],
[
1306, self::T_CAVALRY, '맹수병',
@@ -215,7 +215,7 @@ class GameUnitConstBase{
[self::T_FOOTMAN=>1.2, self::T_ARCHER=>0.8, self::T_SIEGE=>1.2],
[self::T_FOOTMAN=>0.8, self::T_ARCHER=>1.2, self::T_SIEGE=>0.8],
['어느 누구보다 강력합니다.'],
null, ['che_기병병종전투']
null, ['che_기병병종전투'], null
],
[
1307, self::T_CAVALRY, '호표기병',
@@ -224,7 +224,7 @@ class GameUnitConstBase{
[self::T_FOOTMAN=>1.2, self::T_ARCHER=>0.8, self::T_SIEGE=>1.2],
[self::T_FOOTMAN=>0.8, self::T_ARCHER=>1.2, self::T_SIEGE=>0.8],
['정예 기병입니다.'],
null, ['che_기병병종전투']
null, ['che_기병병종전투'], null
],
[
@@ -234,7 +234,7 @@ class GameUnitConstBase{
[self::T_SIEGE=>1.2],
[self::T_SIEGE=>0.8],
['계략을 사용하는 병종입니다.'],
null, null
null, null, null
],
[
1401, self::T_WIZARD, '신귀병',
@@ -243,7 +243,7 @@ class GameUnitConstBase{
[self::T_SIEGE=>1.2],
[self::T_SIEGE=>0.8],
['신출귀몰한 귀병입니다.'],
null, null
null, null, null
],
[
1402, self::T_WIZARD, '백귀병',
@@ -252,7 +252,7 @@ class GameUnitConstBase{
[self::T_SIEGE=>1.2],
[self::T_SIEGE=>0.8],
['저렴하고 튼튼합니다.'],
null, null
null, null, null
],
[
1403, self::T_WIZARD, '흑귀병',
@@ -261,7 +261,7 @@ class GameUnitConstBase{
[self::T_SIEGE=>1.2],
[self::T_SIEGE=>0.8],
['저렴하고 강력합니다.'],
null, null
null, null, null
],
[
1404, self::T_WIZARD, '악귀병',
@@ -270,7 +270,7 @@ class GameUnitConstBase{
[self::T_SIEGE=>1.2],
[self::T_SIEGE=>0.8],
['백병전에도 능숙합니다.'],
null, null
null, null, null
],
[
1405, self::T_WIZARD, '남귀병',
@@ -279,7 +279,7 @@ class GameUnitConstBase{
[self::T_SIEGE=>1.2],
[self::T_SIEGE=>0.8],
['전투를 포기하고 계략에 몰두합니다.'],
null, null
null, null, null
],
[
1406, self::T_WIZARD, '황귀병',
@@ -288,7 +288,7 @@ class GameUnitConstBase{
[self::T_SIEGE=>1.2],
[self::T_SIEGE=>0.8],
['고도로 훈련된 귀병입니다.'],
null, null
null, null, null
],
[
1407, self::T_WIZARD, '천귀병',
@@ -297,7 +297,7 @@ class GameUnitConstBase{
[self::T_SIEGE=>1.2],
[self::T_SIEGE=>0.8],
['갑주를 두른 귀병입니다.'],
null, null
null, null, null
],
[
1408, self::T_WIZARD, '마귀병',
@@ -306,7 +306,7 @@ class GameUnitConstBase{
[self::T_SIEGE=>1.2],
[self::T_SIEGE=>0.8],
['날카로운 무기를 가진 귀병입니다.'],
null, null
null, null, null
],
[
@@ -314,9 +314,9 @@ class GameUnitConstBase{
100, 100, 6, 0, 0, 14, 5,
0, null, null, 3,
[self::T_FOOTMAN=>1.25, self::T_ARCHER=>1.25, self::T_CAVALRY=>1.25, self::T_WIZARD=>1.25, self::T_CASTLE=>1.8, 1106=>1.112],
[self::T_FOOTMAN=>1.2, self::T_ARCHER=>1.2, self::T_CAVALRY=>1.2, self::T_WIZARD=>1.2],
['높은 구조물 위에서 공격합니다.'],
['che_성벽부상무효'], ['che_선제사격시도', 'che_선제사격발동']
[self::T_FOOTMAN=>1.2, self::T_ARCHER=>1.2, self::T_CAVALRY=>1.2, self::T_WIZARD=>1.2, 1106=>1.067],
['높은 구조물 위에서 공격합니다. 첫 공격은 성벽을 향합니다.'],
['che_성벽부상무효'], ['che_선제사격시도', 'che_선제사격발동'], ['che_성벽선제']
],
[
1501, self::T_SIEGE, '충차',
@@ -325,25 +325,25 @@ class GameUnitConstBase{
[self::T_FOOTMAN=>0.8, self::T_ARCHER=>0.8, self::T_CAVALRY=>0.8, self::T_WIZARD=>0.8, self::T_CASTLE=>2.4],
[self::T_FOOTMAN=>1.2, self::T_ARCHER=>1.2, self::T_CAVALRY=>1.2, self::T_WIZARD=>1.2],
['엄청난 위력으로 성벽을 부수어버립니다.'],
['che_성벽부상무효'], null
['che_성벽부상무효'], null, null
],
[
1502, self::T_SIEGE, '벽력거',
150, 100, 6, 5, 0, 20, 5,
3000, ['업'], null, 0,
[self::T_FOOTMAN=>1.25, self::T_ARCHER=>1.25, self::T_CAVALRY=>1.25, self::T_WIZARD=>1.25, self::T_CASTLE=>1.8, 1106=>1.112],
[self::T_FOOTMAN=>0.833, self::T_ARCHER=>0.833, self::T_CAVALRY=>0.833, self::T_WIZARD=>0.833, 1106=>0.909],
['상대에게 돌덩이를 날립니다.'],
['che_성벽부상무효'], ['che_선제사격시도', 'che_선제사격발동']
[self::T_FOOTMAN=>1.2, self::T_ARCHER=>1.2, self::T_CAVALRY=>1.2, self::T_WIZARD=>1.2, 1106=>1.067],
['상대에게 돌덩이를 날립니다. 첫 공격은 성벽을 향합니다.'],
['che_성벽부상무효'], ['che_선제사격시도', 'che_선제사격발동'], ['che_성벽선제']
],
[
1503, self::T_SIEGE, '목우',
50, 200, 5, 0, 0, 15, 5,
3000, ['성도'], null, 0,
[self::T_FOOTMAN=>1.25, self::T_ARCHER=>1.25, self::T_CAVALRY=>1.25, self::T_WIZARD=>1.25, self::T_CASTLE=>1.8, 1106=>1.112],
[self::T_FOOTMAN=>1, self::T_ARCHER=>1, self::T_CAVALRY=>1, self::T_WIZARD=>1, self::T_CASTLE=>1.8],
[self::T_FOOTMAN=>1, self::T_ARCHER=>1, self::T_CAVALRY=>1, self::T_WIZARD=>1, 1106=>1],
['상대를 저지하는 특수병기입니다.'],
['che_성벽부상무효'], ['che_저지시도', 'che_저지발동']
['che_성벽부상무효'], ['che_저지시도', 'che_저지발동'], null
]
];
@@ -466,6 +466,7 @@ class GameUnitConstBase{
$info,
$initSkillTrigger,
$phaseSkillTrigger,
$iActionList,
] = $rawUnit;
if($reqYear > 0){
@@ -509,7 +510,8 @@ class GameUnitConstBase{
$defenceCoef,
$info,
$initSkillTrigger,
$phaseSkillTrigger
$phaseSkillTrigger,
$iActionList,
);
$constID[$id] = $unit;
+167 -66
View File
@@ -1,7 +1,9 @@
<?php
namespace sammo;
class GameUnitDetail implements iAction{
class GameUnitDetail implements iAction
{
use DefaultAction;
public $id;
@@ -23,6 +25,8 @@ class GameUnitDetail implements iAction{
public $info;
public $initSkillTrigger;
public $phaseSkillTrigger;
/** @var iAction[]|null iActionList */
public $iActionList;
public function __construct(
int $id,
@@ -43,8 +47,9 @@ class GameUnitDetail implements iAction{
array $defenceCoef,
array $info,
?array $initSkillTrigger,
?array $phaseSkillTrigger
){
?array $phaseSkillTrigger,
?array $iActionList,
) {
$this->id = $id;
$this->name = $name;
$this->armType = $armType;
@@ -64,78 +69,91 @@ class GameUnitDetail implements iAction{
$this->info = $info;
$this->initSkillTrigger = $initSkillTrigger;
$this->phaseSkillTrigger = $phaseSkillTrigger;
$this->iActionList = [];
foreach($iActionList ?? [] as $rawAction){
$action = buildActionCrewTypeClass($rawAction);
if(!$action){
continue;
}
$this->iActionList[] = $action;
}
}
public function getInfo():string{
public function getInfo(): string
{
return join("\n<br>", $this->info);
}
public function getShortName():string{
public function getShortName(): string
{
return StringUtil::subStringForWidth($this->name, 0, 4);
}
public function riceWithTech(int $tech, int $crew=100):float{
public function riceWithTech(int $tech, int $crew = 100): float
{
return $this->rice * getTechCost($tech) * $crew / 100;
}
public function costWithTech(int $tech, int $crew=100):float{
public function costWithTech(int $tech, int $crew = 100): float
{
return $this->cost * getTechCost($tech) * $crew / 100;
}
public function getAttackCoef(GameUnitDetail $opposeCrewType):float{
public function getAttackCoef(GameUnitDetail $opposeCrewType): float
{
$opposeCrewTypeID = $opposeCrewType->id;
if(key_exists($opposeCrewTypeID, $this->attackCoef)){
if (key_exists($opposeCrewTypeID, $this->attackCoef)) {
return $this->attackCoef[$opposeCrewTypeID];
}
$opposeArmType = $opposeCrewType->armType;
return $this->attackCoef[$opposeArmType]??1;
return $this->attackCoef[$opposeArmType] ?? 1;
}
public function getDefenceCoef(GameUnitDetail $opposeCrewType):float{
public function getDefenceCoef(GameUnitDetail $opposeCrewType): float
{
$opposeCrewTypeID = $opposeCrewType->id;
if(key_exists($opposeCrewTypeID, $this->defenceCoef)){
if (key_exists($opposeCrewTypeID, $this->defenceCoef)) {
return $this->defenceCoef[$opposeCrewTypeID];
}
$opposeArmType = $opposeCrewType->armType;
return $this->defenceCoef[$opposeArmType]??1;
return $this->defenceCoef[$opposeArmType] ?? 1;
}
public function getComputedAttack(General $general, int $tech){
if($this->armType == GameUnitConst::T_WIZARD){
$ratio = $general->getIntel(true, true, true)*2 - 40;
}
else if($this->armType == GameUnitConst::T_SIEGE){
$ratio = $general->getLeadership(true, true, true)*2 - 40;
}
else if($this->armType == GameUnitConst::T_MISC){
public function getComputedAttack(General $general, int $tech)
{
if ($this->armType == GameUnitConst::T_WIZARD) {
$ratio = $general->getIntel(true, true, true) * 2 - 40;
} else if ($this->armType == GameUnitConst::T_SIEGE) {
$ratio = $general->getLeadership(true, true, true) * 2 - 40;
} else if ($this->armType == GameUnitConst::T_MISC) {
$ratio = $general->getIntel(true, true, true) +
$general->getLeadership(true, true, true) +
$general->getStrength(true, true, true);
$ratio = $ratio*2/3 - 40;
$ratio = $ratio * 2 / 3 - 40;
} else {
$ratio = $general->getStrength(true, true, true) * 2 - 40;
}
else{
$ratio = $general->getStrength(true, true, true)*2 - 40;
}
if($ratio < 10){
if ($ratio < 10) {
$ratio = 10;
}
if($ratio > 100){
$ratio = 50 + $ratio/2;
if ($ratio > 100) {
$ratio = 50 + $ratio / 2;
}
$att = $this->attack + getTechAbil($tech);
return $att * $ratio / 100;
}
public function getComputedDefence(General $general, int $tech){
public function getComputedDefence(General $general, int $tech)
{
$def = $this->defence + getTechAbil($tech);
$crew = ($general->getVar('crew') / (7000 / 30)) + 70;
return $def * $crew / 100;
}
public function getCriticalRatio(General $general){
if($this->armType == GameUnitConst::T_CASTLE){
public function getCriticalRatio(General $general)
{
if ($this->armType == GameUnitConst::T_CASTLE) {
//성벽은 필살을 사용하지 않는다.
return 0;
}
@@ -143,22 +161,19 @@ class GameUnitDetail implements iAction{
// 무장 무력 : 65 5%, 70 10%, 75 15%, 80 20%
// 지장 지력 : 65 5%, 70 8%, 75 10%, 80 13%
//충차장 통솔: 65 5%, 70 8%, 75 10%, 80 13%
if($this->armType == GameUnitConst::T_WIZARD){
if ($this->armType == GameUnitConst::T_WIZARD) {
$mainstat = $general->getIntel(false, true, true, false);
$coef = 0.4;
}
else if($this->armType == GameUnitConst::T_SIEGE){
} else if ($this->armType == GameUnitConst::T_SIEGE) {
$mainstat = $general->getLeadership(false, true, true, false);
$coef = 0.4;
}
else if($this->armType == GameUnitConst::T_MISC){
} else if ($this->armType == GameUnitConst::T_MISC) {
$mainstat = $general->getIntel(false, true, true, false) +
$general->getLeadership(false, true, true, false) +
$general->getStrength(false, true, true, false);
$general->getLeadership(false, true, true, false) +
$general->getStrength(false, true, true, false);
$mainstat /= 3;
$coef = 0.4;
}
else{
} else {
$mainstat = $general->getStrength(false, true, true, false);
$coef = 0.5;
}
@@ -169,7 +184,8 @@ class GameUnitDetail implements iAction{
return min(50, $ratio) / 100;
}
public function pickScore($tech){
public function pickScore($tech)
{
$defaultWar = GameConst::$armperphase + $this->attack + $this->defence + getTechAbil($tech) * 2;
$defaultWar *= 1 + $this->speed / 2;
$defaultWar /= Util::valueFit(1 - $this->avoid / 100, 0.1);
@@ -177,40 +193,41 @@ class GameUnitDetail implements iAction{
return $defaultWar;
}
public function isValid($ownCities, $ownRegions, $relativeYear, $tech){
public function isValid($ownCities, $ownRegions, $relativeYear, $tech)
{
//음수 없음
$relativeYear = max(0, $relativeYear);
if($relativeYear < $this->reqYear){
if ($relativeYear < $this->reqYear) {
return false;
}
if($tech < $this->reqTech){
if ($tech < $this->reqTech) {
return false;
}
if($this->reqCities !== null){
if ($this->reqCities !== null) {
$valid = false;
foreach($this->reqCities as $reqCity){
if(\key_exists($reqCity, $ownCities)){
foreach ($this->reqCities as $reqCity) {
if (\key_exists($reqCity, $ownCities)) {
$valid = true;
break;
}
}
if(!$valid){
if (!$valid) {
return false;
}
}
if($this->reqRegions !== null){
if ($this->reqRegions !== null) {
$valid = false;
foreach($this->reqRegions as $reqRegion){
if(\key_exists($reqRegion, $ownRegions)){
foreach ($this->reqRegions as $reqRegion) {
if (\key_exists($reqRegion, $ownRegions)) {
$valid = true;
break;
}
}
if(!$valid){
if (!$valid) {
return false;
}
}
@@ -219,17 +236,17 @@ class GameUnitDetail implements iAction{
}
//iAction
public function getBattleInitSkillTriggerList(WarUnit $unit):?WarUnitTriggerCaller{
if(!$this->initSkillTrigger){
public function getBattleInitSkillTriggerList(WarUnit $unit): ?WarUnitTriggerCaller
{
if (!$this->initSkillTrigger) {
return null;
}
$triggerList = [];
foreach($this->initSkillTrigger as $triggerArgs){
if(is_string($triggerArgs)){
foreach ($this->initSkillTrigger as $triggerArgs) {
if (is_string($triggerArgs)) {
$typeName = $triggerArgs;
$triggerList[] = buildWarUnitTriggerClass($typeName, $unit);
}
else{
} else {
$typeName = $triggerArgs[0];
//WarUnit 다음 인자는 $raiseType이며, 0이어야할 것이다
$triggerArgs[0] = 0;
@@ -238,17 +255,17 @@ class GameUnitDetail implements iAction{
}
return new WarUnitTriggerCaller(...$triggerList);
}
public function getBattlePhaseSkillTriggerList(WarUnit $unit):?WarUnitTriggerCaller{
if(!$this->phaseSkillTrigger){
public function getBattlePhaseSkillTriggerList(WarUnit $unit): ?WarUnitTriggerCaller
{
if (!$this->phaseSkillTrigger) {
return null;
}
$triggerList = [];
foreach($this->phaseSkillTrigger as $triggerArgs){
if(is_string($triggerArgs)){
foreach ($this->phaseSkillTrigger as $triggerArgs) {
if (is_string($triggerArgs)) {
$typeName = $triggerArgs;
$triggerList[] = buildWarUnitTriggerClass($typeName, $unit);
}
else{
} else {
$typeName = $triggerArgs[0];
//WarUnit 다음 인자는 $raiseType이며, 0이어야할 것이다
$triggerArgs[0] = 0;
@@ -256,6 +273,90 @@ class GameUnitDetail implements iAction{
}
}
return new WarUnitTriggerCaller(...$triggerList);
}
public function onCalcDomestic(string $turnType, string $varType, float $value, $aux = null): float
{
if (!$this->iActionList) {
return $value;
}
foreach ($this->iActionList as $iAction) {
$value = $iAction->onCalcDomestic($turnType, $varType, $value, $aux);
}
return $value;
}
}
public function onCalcStat(General $general, string $statName, $value, $aux = null)
{
if (!$this->iActionList) {
return $value;
}
foreach ($this->iActionList as $iAction) {
$value = $iAction->onCalcStat($general, $statName, $value, $aux);
}
return $value;
}
public function onCalcOpposeStat(General $general, string $statName, $value, $aux = null)
{
if (!$this->iActionList) {
return $value;
}
foreach ($this->iActionList as $iAction) {
$value = $iAction->onCalcOpposeStat($general, $statName, $value, $aux);
}
return $value;
}
public function onCalcStrategic(string $turnType, string $varType, $value)
{
if (!$this->iActionList) {
return $value;
}
foreach ($this->iActionList as $iAction) {
$value = $iAction->onCalcStrategic($turnType, $varType, $value);
}
return $value;
}
public function onCalcNationalIncome(string $type, $amount)
{
if (!$this->iActionList) {
return $amount;
}
foreach ($this->iActionList as $iAction) {
$amount = $iAction->onCalcNationalIncome($type, $amount);
}
return $amount;
}
public function getWarPowerMultiplier(WarUnit $unit): array
{
if (!$this->iActionList) {
return [1, 1];
}
$attack = 1;
$defence = 1;
foreach ($this->iActionList as $iAction) {
$attack *= $iAction->getWarPowerMultiplier($unit)[0];
$defence *= $iAction->getWarPowerMultiplier($unit)[1];
}
return [$attack, $defence];
}
public function onArbitraryAction(General $general, RandUtil $rng, string $actionType, ?string $phase = null, ?array $aux = null): null|array
{
if (!$this->iActionList) {
return $aux;
}
foreach ($this->iActionList as $iAction) {
$aux = $iAction->onArbitraryAction($general, $rng, $actionType, $phase, $aux);
}
return $aux;
}
}
+40 -99
View File
@@ -30,20 +30,22 @@ class General implements iAction
protected $logActivatedSkill = [];
protected $isFinished = false;
/** @var iAction */
/** @var ?iAction */
protected $nationType = null;
/** @var iAction */
/** @var ?iAction */
protected $officerLevelObj = null;
/** @var iAction */
/** @var ?iAction */
protected $specialDomesticObj = null;
/** @var iAction */
/** @var ?iAction */
protected $specialWarObj = null;
/** @var iAction */
/** @var ?iAction */
protected $personalityObj = null;
/** @var iAction[] */
/** @var ?iAction[] */
protected $itemObjs = [];
/** @var iAction */
/** @var ?iAction */
protected $inheritBuffObj = null;
/** @var ?GameUnitDetail */
protected $crewType = null;
protected $lastTurn = null;
protected $resultTurn = null;
@@ -122,6 +124,8 @@ class General implements iAction
$this->personalityObj = buildPersonalityClass($raw['personal']);
$this->crewType = GameUnitConst::byID($raw['crewtype'] ?? GameUnitConst::DEFAULT_CREWTYPE);
$this->itemObjs['horse'] = buildItemClass($raw['horse']);
$this->itemObjs['weapon'] = buildItemClass($raw['weapon']);
$this->itemObjs['book'] = buildItemClass($raw['book']);
@@ -325,11 +329,10 @@ class General implements iAction
function getCrewTypeObj(): GameUnitDetail
{
$crewType = GameUnitConst::byID($this->getVar('crewtype'));
if ($crewType === null) {
if($this->crewType === null) {
throw new \InvalidArgumentException('Invalid CrewType:' . $this->getVar('crewtype'));
}
return $crewType;
return $this->crewType;
}
function calcRecentWarTurn(int $turnTerm): int
@@ -443,6 +446,8 @@ class General implements iAction
}
}
$statValue = Util::clamp($statValue, 0, GameConst::$maxLevel);
if ($withIActionObj) {
foreach ([
$this->nationType,
@@ -466,6 +471,8 @@ class General implements iAction
$this->calcCache[$cKey] = $statValue;
$statValue = Util::clamp($statValue, 0, GameConst::$maxLevel);
if ($useFloor) {
return Util::toInt($statValue);
}
@@ -837,9 +844,11 @@ class General implements iAction
$this->increaseVar($statName, -1);
$result = true;
} else if ($this->getVar($statExpName) >= $limit) {
$logger->pushGeneralActionLog("<S>{$statNickName}</>이 <C>1</> 올랐습니다!", ActionLogger::PLAIN);
if($this->getVar($statName) < GameConst::$maxLevel) {
$logger->pushGeneralActionLog("<S>{$statNickName}</>이 <C>1</> 올랐습니다!", ActionLogger::PLAIN);
$this->increaseVar($statName, 1);
}
$this->increaseVar($statExpName, -$limit);
$this->increaseVar($statName, 1);
$result = true;
}
}
@@ -847,18 +856,22 @@ class General implements iAction
return $result;
}
public function getPreTurnExecuteTriggerList(General $general): ?GeneralTriggerCaller
{
$caller = new GeneralTriggerCaller();
foreach (array_merge([
protected function getActionList(): array{
return array_merge([
$this->nationType,
$this->officerLevelObj,
$this->specialDomesticObj,
$this->specialWarObj,
$this->personalityObj,
$this->getCrewTypeObj(),
$this->crewType,
$this->inheritBuffObj,
], $this->itemObjs) as $iObj) {
], $this->itemObjs);
}
public function getPreTurnExecuteTriggerList(General $general): ?GeneralTriggerCaller
{
$caller = new GeneralTriggerCaller();
foreach ($this->getActionList() as $iObj) {
if (!$iObj) {
continue;
@@ -871,15 +884,7 @@ class General implements iAction
}
public function onCalcDomestic(string $turnType, string $varType, float $value, $aux = null): float
{
foreach (array_merge([
$this->nationType,
$this->officerLevelObj,
$this->specialDomesticObj,
$this->specialWarObj,
$this->personalityObj,
$this->getCrewTypeObj(),
$this->inheritBuffObj,
], $this->itemObjs) as $iObj) {
foreach ($this->getActionList() as $iObj) {
if (!$iObj) {
continue;
}
@@ -892,15 +897,7 @@ class General implements iAction
public function onCalcStat(General $general, string $statName, $value, $aux = null)
{
//xxx: $general?
foreach (array_merge([
$this->nationType,
$this->officerLevelObj,
$this->specialDomesticObj,
$this->specialWarObj,
$this->personalityObj,
$this->getCrewTypeObj(),
$this->inheritBuffObj,
], $this->itemObjs) as $iObj) {
foreach ($this->getActionList() as $iObj) {
if (!$iObj) {
continue;
}
@@ -913,15 +910,7 @@ class General implements iAction
public function onCalcOpposeStat(General $general, string $statName, $value, $aux = null)
{
//xxx: $general?
foreach (array_merge([
$this->nationType,
$this->officerLevelObj,
$this->specialDomesticObj,
$this->specialWarObj,
$this->personalityObj,
$this->getCrewTypeObj(),
$this->inheritBuffObj,
], $this->itemObjs) as $iObj) {
foreach ($this->getActionList() as $iObj) {
if (!$iObj) {
continue;
}
@@ -933,15 +922,7 @@ class General implements iAction
public function onCalcStrategic(string $turnType, string $varType, $value)
{
foreach (array_merge([
$this->nationType,
$this->officerLevelObj,
$this->specialDomesticObj,
$this->specialWarObj,
$this->personalityObj,
$this->getCrewTypeObj(),
$this->inheritBuffObj,
], $this->itemObjs) as $iObj) {
foreach ($this->getActionList() as $iObj) {
if (!$iObj) {
continue;
}
@@ -953,15 +934,7 @@ class General implements iAction
public function onCalcNationalIncome(string $type, $amount)
{
foreach (array_merge([
$this->nationType,
$this->officerLevelObj,
$this->specialDomesticObj,
$this->specialWarObj,
$this->personalityObj,
$this->getCrewTypeObj(),
$this->inheritBuffObj,
], $this->itemObjs) as $iObj) {
foreach ($this->getActionList() as $iObj) {
if (!$iObj) {
continue;
}
@@ -973,15 +946,7 @@ class General implements iAction
public function onArbitraryAction(General $general, RandUtil $rng, string $actionType, ?string $phase = null, $aux = null): null|array
{
foreach (array_merge([
$this->nationType,
$this->officerLevelObj,
$this->specialDomesticObj,
$this->specialWarObj,
$this->personalityObj,
$this->getCrewTypeObj(),
$this->inheritBuffObj,
], $this->itemObjs) as $iObj) {
foreach ($this->getActionList() as $iObj) {
if (!$iObj) {
continue;
}
@@ -996,15 +961,7 @@ class General implements iAction
//xxx:$unit
$att = 1;
$def = 1;
foreach (array_merge([
$this->nationType,
$this->officerLevelObj,
$this->specialDomesticObj,
$this->specialWarObj,
$this->personalityObj,
$this->getCrewTypeObj(),
$this->inheritBuffObj,
], $this->itemObjs) as $iObj) {
foreach ($this->getActionList() as $iObj) {
if (!$iObj) {
continue;
}
@@ -1018,15 +975,7 @@ class General implements iAction
public function getBattleInitSkillTriggerList(WarUnit $unit): ?WarUnitTriggerCaller
{
$caller = new WarUnitTriggerCaller();
foreach (array_merge([
$this->nationType,
$this->officerLevelObj,
$this->specialDomesticObj,
$this->specialWarObj,
$this->personalityObj,
$this->getCrewTypeObj(),
$this->inheritBuffObj,
], $this->itemObjs) as $iObj) {
foreach ($this->getActionList() as $iObj) {
if (!$iObj) {
continue;
}
@@ -1047,15 +996,7 @@ class General implements iAction
new WarUnitTrigger\che_계략발동($unit),
new WarUnitTrigger\che_계략실패($unit)
);
foreach (array_merge([
$this->nationType,
$this->officerLevelObj,
$this->specialDomesticObj,
$this->specialWarObj,
$this->personalityObj,
$this->getCrewTypeObj(),
$this->inheritBuffObj,
], $this->itemObjs) as $iObj) {
foreach ($this->getActionList() as $iObj) {
if (!$iObj) {
continue;
}
+2 -1
View File
@@ -165,7 +165,8 @@ class InheritancePointManager
case InheritanceKey::betting:
$extractFn = function () use ($general, $multiplier) {
$betWin = $general->getRankVar(RankColumn::betwin);
$betWinRate = $general->getRankVar(RankColumn::betwingold) / max(1, $general->getRankVar(RankColumn::betgold));
//FIXME: 올바른 위치에서 은퇴 전후 베팅 기록을 처리해야함.
$betWinRate = $general->getRankVar(RankColumn::betwingold) / max(1000, $general->getRankVar(RankColumn::betgold));
return [$betWin * $multiplier * pow($betWinRate, 2), null];
};
+61
View File
@@ -126,6 +126,8 @@ class Message
$objMessage = new DiplomaticMessage(...$args);
} elseif ($action === 'scout') {
$objMessage = new ScoutMessage(...$args);
} elseif ($action === 'raiseInvader') {
$objMessage = new RaiseInvaderMessage(...$args);
} else {
$objMessage = new Message(...$args);
}
@@ -376,6 +378,65 @@ class Message
throw new \RuntimeException('이곳에 올 수 없습니다.');
}
/**
* @param int[]|MessageTarget[]|int|MessageTarget $targets
* @param string $msg
*/
public static function sendPrivateMsgAsNotice(array|int|MessageTarget $targets, string $msg): void{
$src = MessageTarget::buildSystemTarget();
if(is_int($targets)){
$targets = [$targets];
}
else if($targets instanceof MessageTarget){
$targets = [$targets];
}
$reqTargetGeneralIDList = [];
$objTargets = [];
foreach($targets as $target){
if($target instanceof MessageTarget){
$objTargets[] = $target;
continue;
}
if(!is_int($target)){
throw new \InvalidArgumentException('올바르지 않은 타입');
}
$reqTargetGeneralIDList[] = $target;
}
if($reqTargetGeneralIDList){
$db = DB::db();
$rawGenerals = $db->query('SELECT no, name, nation, imgsvr, picture FROM general WHERE no in %li', $reqTargetGeneralIDList);
foreach($rawGenerals as $rawGeneral){
$staticNation = getNationStaticInfo($rawGeneral['nation']);
$objTarget = new MessageTarget(
$rawGeneral['no'],
$rawGeneral['name'],
$rawGeneral['nation'],
$staticNation['name'],
$staticNation['color'],
GetImageURL($rawGeneral['imgsvr'], $rawGeneral['picture'])
);
$objTargets[] = $objTarget;
}
}
foreach($objTargets as $dest){
$msg = new Message(
MessageType::private,
$src,
$dest,
$msg,
new \DateTime(),
new \DateTime('9999-12-31'),
[]
);
$msg->send(true);
}
}
public function send(bool $sendDestOnly=false):int{
[$receiverMailbox, $receiveID] = $this->sendToReceiver();
if(!$receiveID && !$sendDestOnly){
+29 -13
View File
@@ -10,7 +10,7 @@ class MessageTarget extends Target {
public $color;
/** @var string */
public $icon;
public function __construct(
int $generalID,
string $generalName,
@@ -19,13 +19,13 @@ class MessageTarget extends Target {
string $color,
string $icon=''
){
parent::__construct($generalID, $nationID);
if(!$icon){
$icon = ServConfig::getSharedIconPath().'/default.jpg';
}
$this->generalName = $generalName;
$this->nationName = $nationName;
$this->color = $color;
@@ -47,28 +47,28 @@ class MessageTarget extends Target {
}
return new MessageTarget(
$arr['id'],
$arr['name'],
$arr['nation_id'],
$arr['nation'],
$arr['color'],
$arr['id'],
$arr['name'],
$arr['nation_id'],
$arr['nation'],
$arr['color'],
$arr['icon']??''
);
}
/**
* DB 부하 감수하고 일일히 찾아줌.
* DB 부하 감수하고 일일히 찾아줌.
* 게임 로직과 크게 연관이 없는 곳에서 메시지를 생성해야 할 경우에만 사용할 것을 권함.
* @return MessageTarget
*/
public static function buildQuick(int $generalID){
$db = DB::db();
list(
$generalName,
$nationID,
$imgsvr,
$generalName,
$nationID,
$imgsvr,
$picture
) = $db->queryFirstList('SELECT `name`, nation, imgsvr, picture FROM general WHERE `no`=%i', $generalID);
) = $db->queryFirstList('SELECT `name`, nation, imgsvr, picture FROM general WHERE `no`=%i', $generalID);
if($generalName === null){
throw new \RuntimeException('장수가 없음:'.$generalID);
@@ -79,6 +79,22 @@ class MessageTarget extends Target {
return new MessageTarget($generalID, $generalName, $nationID, $nation['name'], $nation['color'], $icon);
}
public static function buildFromGeneralObj(General $general): MessageTarget{
$nation = getNationStaticInfo($general->getNationID());
return new MessageTarget(
$general->getID(),
$general->getName(),
$general->getNationID(),
$nation['name'],
$nation['color'],
GetImageURL($general->getVar('imgsvr'), $general->getVar('picture')),
);
}
public static function buildSystemTarget(): MessageTarget{
return new MessageTarget(0, '', 0, 'System', '#000000');
}
public function toArray() : array{
return [
'id'=>$this->generalID,
+178
View File
@@ -0,0 +1,178 @@
<?php
namespace sammo;
use DateTime;
use sammo\Enums\MessageType;
use sammo\Event\Action\RaiseInvader;
class RaiseInvaderMessage extends Message
{
const ACCEPTED = 1;
const DECLINED = -1;
const INVALID = 0;
protected $valid = true;
public function __construct(
MessageType $msgType,
MessageTarget $src,
MessageTarget $dest,
string $msg,
\DateTime $date,
\DateTime $validUntil,
array $msgOption
) {
if ($msgType !== MessageType::private) {
throw new \InvalidArgumentException('private msgType');
}
if (Util::array_get($msgOption['action']) !== 'raiseInvader') {
throw new \InvalidArgumentException('Action !== raiseInvader');
}
parent::__construct(...func_get_args());
if (Util::array_get($msgOption['used'])) {
$this->valid = false;
}
}
protected function checkMessageValidation(int $receiverID)
{
if (!$this->valid) {
return [self::INVALID, '이미 사용하였습니다.'];
}
if ($this->mailbox !== $this->dest->generalID) {
return [self::INVALID, '송신자가 메시지를 처리할 수 없습니다.'];
}
if ($this->mailbox !== $receiverID) {
return [self::INVALID, '올바른 수신자가 아닙니다.'];
}
return [self::ACCEPTED, '성공'];
}
/**
* @return int 수행 결과 반환, ACCEPTED(등용장 소모), DECLINED(등용장 소모), INVALID 중 반환
*/
public function agreeMessage(int $receiverID, string &$reason): int
{
//NOTE: 올바른 유저가 agreeMessage() 호출을 한건지는 외부에서 체크 필요(Session->userID 등)
if (!$this->id) {
throw new \RuntimeException('전송되지 않은 메시지에 수락 진행 중');
}
$gameStor = KVStorage::getStorage(DB::db(), 'game_env');
$general = \sammo\General::createGeneralObjFromDB($receiverID, null, 2);
$logger = $general->getLogger();
[$result, $reason] = $this->checkMessageValidation($receiverID);
if($result === self::ACCEPTED && $gameStor->isunited != 2){
$result = self::INVALID;
$reason = '천하통일이 되지 않았습니다.';
}
if ($result !== self::ACCEPTED) {
if ($result === self::DECLINED) {
$this->_declineMessage();
}
else{
Message::sendPrivateMsgAsNotice($this->dest, "{$reason} 이민족 등장 불가.");
}
return $result;
}
[$npcEachCount, $specAvg, $tech, $dex] = $this->msgOption['args'];
$invaderAction = new RaiseInvader(
$npcEachCount,
$specAvg,
$tech,
$dex
);
$invaderAction->run($gameStor->getAll());
return self::ACCEPTED;
}
protected function _declineMessage()
{
$this->msgOption['used'] = true;
$this->invalidate();
$this->valid = false;
return self::DECLINED;
}
public function declineMessage(int $receiverID, string &$reason): int
{
if (!$this->id) {
throw new \RuntimeException('전송되지 않은 메시지에 거절 진행 중');
}
$db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env');
[$result, $reason] = $this->checkMessageValidation($receiverID);
if ($result === self::INVALID) {
Message::sendPrivateMsgAsNotice($this->dest, "{$reason} 취소 불가.");
return $result;
}
$this->_declineMessage();
return self::DECLINED;
}
/**
*
* @param int $destGeneralID
* @param DateTime|null $date
* @return RaiseInvaderMessage[]
*/
public static function buildRaiseInvaderMessage(int $destGeneralID, \DateTime $date = null): array
{
$srcTarget = MessageTarget::buildSystemTarget();
$destTarget = MessageTarget::buildQuick($destGeneralID);
if ($date === null) {
$date = new \DateTime();
}
/**
* @var [[float,float,float,float],string][] $argsList
*/
$argsList = [
[[-2, -1.2, 15000, -1], '어려움'],
[[-2, -1.2, -1, -0.5], '보통'],
[[-1, -1, -0.8, 0], '쉬움'],
];
//XXX: 난이도 설정을 어디서 해야하는가?
$msgList = [];
foreach($argsList as [$args, $difficulty]){
$msg = new self(
MessageType::private,
$srcTarget,
$destTarget,
"이벤트 게임으로 이민족[{$difficulty}]을 소환",
$date,
new \DateTime('9999-12-31'),
[
'action' => 'raiseInvader',
'args' => $args,
'used' => false
]
);
$msgList[] = $msg;
}
return $msgList;
}
}
+4
View File
@@ -143,6 +143,9 @@ class ResetHelper{
$voteStor = KVStorage::getStorage($db, 'vote');
$voteStor->resetValues();
//삭제하는 타입은 enum GeneralStorKey 참고
$db->delete('storage', 'namespace LIKE "general_%"');
$lastExecuteStor = KVStorage::getStorage($db, 'next_execute');
$lastExecuteStor->resetValues();
@@ -269,6 +272,7 @@ class ResetHelper{
'startyear'=>$startyear,
'year'=> $year,
'month'=> $month,
'isunited' => 0,
'init_year'=> $year,
'init_month'=>$month,
'map_theme' => $scenarioObj->getMapTheme(), //@deprecated
+13
View File
@@ -0,0 +1,13 @@
<?php
namespace sammo;
/** 서버 내 변수 모음 */
class ServerDefaultEnv
{
private function __construct()
{
}
static int $maxGeneralsPerMinute = 1000;
}
+6
View File
@@ -0,0 +1,6 @@
<?php
namespace sammo;
class ServerEnv extends ServerDefaultEnv {
//NOTE: d_setting에 ServerEnv.php가 없을때 대비.
}
+79
View File
@@ -0,0 +1,79 @@
<?php
namespace sammo;
final class ServerTool
{
private function __construct()
{
}
public static function changeServerTerm(int $turnterm, ?bool $ignoreLock = null): ?string
{
// 하루에 연 단위로 게임 시간이 흘러야 함.
if ((120 % $turnterm) != 0) {
return 'invalid minute';
}
$db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env');
$admin = $gameStor->getValues(['turntime', 'turnterm', 'year', 'startyear', 'month', 'isunited']);
$reqGameLock = $admin['isunited'] != 2 && !$ignoreLock;
$locked = false;
if($reqGameLock){
for ($i = 0; $i < 5; $i++) {
$locked = tryLock();
if ($locked) {
break;
}
usleep(500000);
}
if (!$locked) {
return 'server busy';
}
}
else{
$locked = tryLock();
}
$oldunit = $admin['turnterm'] * 60;
$unit = $turnterm * 60;
if($unit == $oldunit){
if($locked){
unlock();
}
return null;
}
$unitDiff = $unit / $oldunit;
$servTurnTime = new \DateTimeImmutable($admin['turntime']);
foreach ($db->query('SELECT no,turntime FROM general') as $gen) {
$genTurnTime = new \DateTimeImmutable($gen['turntime']);
$timeDiff = TimeUtil::DateIntervalToSeconds($genTurnTime->diff($servTurnTime));
$timeDiff *= $unitDiff;
$newGenTurnTime = $servTurnTime->add(TimeUtil::secondsToDateInterval($timeDiff));
$db->update('general', [
'turntime' => $newGenTurnTime->format('Y-m-d H:i:s.u')
], 'no=%i', $gen['no']);
}
$turn = ($admin['year'] - $admin['startyear']) * 12 + $admin['month'] - 1;
$starttime = $servTurnTime->sub(TimeUtil::secondsToDateInterval($turn * $unit))->format('Y-m-d H:i:s');
$starttime = cutTurn($starttime, $turnterm, false);
$gameStor->turnterm = $turnterm;
$gameStor->starttime = $starttime;
pushGlobalHistoryLog(["<R>★</>턴시간이 <C>{$turnterm}분</>으로 변경됩니다."]);
if($locked){
unlock();
}
return null;
}
}
+10 -2
View File
@@ -198,6 +198,7 @@ class SpecialityHelper{
public static function pickSpecialDomestic(RandUtil $rng, array $general, array $prevSpecials=[]) : string{
$pAbs = [];
$pRel = [];
$id = null;
$myCond = static::calcCondGeneric($general);
@@ -240,7 +241,10 @@ class SpecialityHelper{
}
}
$id = $rng->choiceUsingWeight($pRel);
if($pRel){
$id = $rng->choiceUsingWeight($pRel);
}
if($id){
return $id;
}
@@ -256,6 +260,7 @@ class SpecialityHelper{
$reqDex = [];
$pAbs = [];
$pRel = [];
$id = null;
$myCond = static::calcCondGeneric($general);
$myCond |= static::calcCondDexterity($rng, $general);
@@ -307,7 +312,10 @@ class SpecialityHelper{
}
}
$id = $rng->choiceUsingWeight($pRel);
if($pRel){
$id = $rng->choiceUsingWeight($pRel);
}
if($id){
return $id;
}
+26 -3
View File
@@ -7,6 +7,7 @@ class WarUnitCity extends WarUnit{
protected $hp;
protected $cityTrainAtmos;
protected $onSiege = false;
function __construct(public readonly RandUtil $rng, $raw, $rawNation, int $year, int $month, int $startYear){
$general = new DummyGeneral(false);
@@ -24,6 +25,7 @@ class WarUnitCity extends WarUnit{
$this->logger = $general->getLogger();
$this->crewType = GameUnitConst::byID(GameUnitConst::CREWTYPE_CASTLE);
$general->setCrewType($this->crewType);
$this->hp = $this->getCityVar('def') * 10;
@@ -77,6 +79,21 @@ class WarUnitCity extends WarUnit{
return $this->hp;
}
function setSiege(){
$this->onSiege = true;
$this->currPhase = 0;
$this->prePhase = 0;
$this->bonusPhase = 0;
$this->isFinished = false;
/** @var DummyGeneral $general */
$general = $this->general;
$general->setCrewType(null);
}
function isSiege(): bool{
return $this->onSiege;
}
function getDex(GameUnitDetail $crewType){
return ($this->cityTrainAtmos - 60) * 7200;
}
@@ -94,6 +111,12 @@ class WarUnitCity extends WarUnit{
function continueWar(&$noRice):bool{
//전투가 가능하면 true
$noRice = false;
//본 공성이 아닌 경우에는 한대만 맞아줌
if(!$this->onSiege){
return false;
}
if($this->getHP() <= 0){
return false;
}
@@ -109,15 +132,15 @@ class WarUnitCity extends WarUnit{
}
function finishBattle(){
if($this->isFinished){
return;
}
$this->clearActivatedSkill();
$this->isFinished = true;
$this->updateVar('def', Util::round($this->getHP() / 10));
$this->updateVar('wall', Util::round($this->getVar('wall')));
if($this->isFinished || !$this->onSiege){
return;
}
//NOTE: 전투로 인한 사망자는 여기서 처리하지 않음
$decWealth = $this->getKilled() / 20;
+11 -4
View File
@@ -262,11 +262,8 @@ class WarUnitGeneral extends WarUnit
return $general->getVar('crew');
}
function increaseKilled(int $damage): int
function calcRiceConsumption(int $damage): float
{
$general = $this->general;
$this->addLevelExp($damage / 50);
$rice = $damage / 100;
if (!$this->isAttacker) {
$rice *= 0.8;
@@ -279,6 +276,16 @@ class WarUnitGeneral extends WarUnit
$rice *= getTechCost($this->getNationVar('tech'));
$rice = $this->general->onCalcStat($this->general, 'killRice', $rice);
return $rice;
}
function increaseKilled(int $damage): int
{
$general = $this->general;
$this->addLevelExp($damage / 50);
$rice = $this->calcRiceConsumption($damage);
$general->increaseVarWithLimit('rice', -$rice, 0);
$addDex = $damage;
@@ -9,6 +9,8 @@ use sammo\Util;
use sammo\ObjectTrigger;
use sammo\ActionLogger;
use function sammo\getTechCost;
class che_저지발동 extends BaseWarUnitTrigger{
protected $priority = ObjectTrigger::PRIORITY_POST; //최우선 순위
@@ -25,10 +27,15 @@ class che_저지발동 extends BaseWarUnitTrigger{
$self->getLogger()->pushGeneralBattleDetailLog("상대를 <C>저지</>했다!", ActionLogger::PLAIN);
$oppose->getLogger()->pushGeneralBattleDetailLog("저지</>당했다!", ActionLogger::PLAIN);
$calcDamage = $oppose->getWarPower() * 0.9;
$self->getGeneral()->addDex($oppose->getCrewType(), $oppose->getWarPower() * 0.9);
$self->getGeneral()->addDex($self->getCrewType(), $self->getWarPower() * 0.9);
$self->getGeneral()->addDex($self->getCrewType(), $calcDamage);
if($self instanceof WarUnitGeneral){
$self->addLevelExp($self->getWarPower() * 0.9 / 50);
$self->addLevelExp($calcDamage / 50);
$rice = $self->calcRiceConsumption($calcDamage);
$rice *= 0.25;
$self->getGeneral()->increaseVarWithLimit('rice', -$rice, 0);
}
$self->setWarPowerMultiply(0);
+123 -114
View File
@@ -12,7 +12,8 @@
"max": 90
},
"const":{
"defaultMaxGeneral":1000
"defaultMaxGeneral":1000,
"npcMessageFreqByDay":1
},
"iconPath": ".",
"fiction": 1,
@@ -22,52 +23,52 @@
"diplomacy":[],
"general":[
[0, "기타노시오리", null, 0, null, 96, 92, 93, 0, 160, 333, null, "위압", "언니는 내가 지켜….!"],
[0, "카나메 마도카", null, 0, null, 86, 92, 77, 0, 160, 300, null, "집중", "자아! 이루어줘 인큐베이터!"],
[0, "토모에 마미", null, 0, null, 62, 84, 60, 0, 160, 300, null, null, "마법소녀가 마녀가 된다면 죽을수밖에 없잖아..!! 너도! 나도!"],
[0, "아케미 호무라", null, 0, null, 76, 71, 80, 0, 160, 333, null, "집중", "원래 기적이라는건 인간의 생명으로도 사고팔수 있는게 아니야"],
[0, "사쿠라 쿄코", null, 0, null, 74, 85, 54, 0, 160, 240, null, null, "이제 겨우…친구가 되었는데…."],
[0, "미키 사야카", null, 0, null, 77, 81, 62, 0, 160, 240, null, null, "이거 실례네~? 나는 네가 알고 있는 그대로의 나야 전.학.,생~?"],
[0, "모모에 나기사", null, 0, null, 66, 83, 70, 0, 160, 240, null, null, "나기사는 단지 치즈가 먹고싶엇던것 뿐이에요!"],
[0, "미쿠니 오리코", null, 0, null, 83, 40, 94, 0, 160, 240, null, null, "당신에게 제대로 말하고 싶었어 미안해. 그리고 고마워."],
[0, "쿠레 키리카", null, 0, null, 50, 80, 50, 0, 160, 240, null, null],
[0, "치토세 유마", null, 0, null, 76, 67, 78, 0, 160, 333, null, null, "언젠가 모두 마녀가 되지만. 그건 지금이 아니야."],
[0, "큐베", null, 0, null, 22, 14, 64, 0, 160, 333, null, null, "나랑 계약해서 마녀가 되어줘!"],
[0, "카나메 마도카", null, 0, null, 90, 97, 83, 0, 160, 300, null, "집중", "자아! 이루어줘 인큐베이터!"],
[0, "토모에 마미", null, 0, null, 71, 88, 60, 0, 160, 300, null, null, "마법소녀가 마녀가 된다면 죽을수밖에 없잖아..!! 너도! 나도!"],
[0, "아케미 호무라", null, 0, null, 85, 86, 80, 0, 160, 333, null, "집중", "원래 기적이라는건 인간의 생명으로도 사고팔수 있는게 아니야"],
[0, "사쿠라 쿄코", null, 0, null, 80, 89, 54, 0, 160, 240, null, null, "이제 겨우…친구가 되었는데…."],
[0, "미키 사야카", null, 0, null, 86, 85, 77, 0, 160, 240, null, null, "이거 실례네~? 나는 네가 알고 있는 그대로의 나야 전.학.,생~?"],
[0, "모모에 나기사", null, 0, null, 66, 80, 70, 0, 160, 240, null, null, "나기사는 단지 치즈가 먹고싶엇던것 뿐이에요!"],
[0, "미쿠니 오리코", null, 0, null, 83, 40, 97, 0, 160, 240, null, null, "당신에게 제대로 말하고 싶었어 미안해. 그리고 고마워."],
[0, "쿠레 키리카", null, 0, null, 41, 85, 50, 0, 160, 240, null, null, "곤란한 일 따위 없어! 내가 할 수 없는 일 따위도 없어!"],
[0, "치토세 유마", null, 0, null, 64, 78, 78, 0, 160, 333, null, null, "언젠가 모두 마녀가 되지만. 그건 지금이 아니야."],
[0, "큐베", null, 0, null, 78, 14, 80, 0, 160, 333, null, null, "나랑 계약해서 마녀가 되어줘!"],
[0, "유키 사사", null, 0, null, 64, 78, 72, 0, 160, 333, null, null, "마녀라니 말도 안돼!"],
[0, "코이토", null, 0, null, 65, 70, 74, 0, 160, 333, null, null, "미쿠니 선배가 증오해야할 적인가 구세주인가 그답이 나오질 않아."],
[0, "코마키", null, 0, null, 80, 75, 49, 0, 160, 333, null, null, "너 말이야 냉담한 주제에 이상한 곳에서 사려 깊구나"],
[0, "코이토", null, 0, null, 65, 73, 74, 0, 160, 333, null, null, "미쿠니 선배가 증오해야할 적인가 구세주인가 그답이 나오질 않아."],
[0, "코마키", null, 0, null, 80, 80, 49, 0, 160, 333, null, null, "너 말이야 냉담한 주제에 이상한 곳에서 사려 깊구나"],
[0, "파브", null, 0, null, 40, 3, 32, 0, 160, 300, null, null, "지금부터 마을의 마법소녀를 반으로 줄여야 한다퐁!"],
[0, "스노우화이트", null, 0, null, 86, 84, 79, 0, 160, 333, null, "돌격", "사람들을 도울수 있는게 기뻐서!"],
[0, "프흐레", null, 0, null, 84, 25, 100, 0, 160, 333, null, "집중", "마법나라'가 원하는 마법소녀다운 마법소녀란 건 말이지. 여차 할 때 주저없이 자기 ​​자신을 희생 할 수 있는 성인이야. 제대로 돼먹은 정신 머리로는 못해 먹을 짓거리지"],
[0, "섀도게일", null, 0, null, 52, 75, 74, 0, 160, 333, null, null],
[0, "나나코상", null, 0, null, 64, 19, 84, 0, 160, 333, null, null, "전 인사부문소속의 7753이라고 합니다"],
[0, "프흐레", null, 0, null, 95, 35, 100, 0, 160, 333, null, "집중", "마법나라'가 원하는 마법소녀다운 마법소녀란 건 말이지. 여차 할 때 주저없이 자기 ​​자신을 희생 할 수 있는 성인이야. 제대로 돼먹은 정신 머리로는 못해 먹을 짓거리지"],
[0, "섀도게일", null, 0, null, 52, 75, 74, 0, 160, 333, null, null, "이름이 정해진 그순간부터 나는 히토코우지 카노에를 위해 존재하니까."],
[0, "나나코상", null, 0, null, 77, 19, 84, 0, 160, 333, null, null, "전 인사부문소속의 7753이라고 합니다"],
[0, "인페르노", null, 0, null, 82, 85, 60, 0, 160, 399, null, null, "저녀석..해버려줘,,,,마법소녀…사냥….그럼….가.."],
[0, "룰러", null, 0, null, 70, 30, 80, 0, 160, 333, null, null, "룰러의 이름으로 명한다"],
[0, "네무링", null, 0, null, 71, 63, 58, 0, 160, 333, null, null, "공주님이 되어버리면 되는거야."],
[0, "룰러", null, 0, null, 83, 30, 80, 0, 160, 333, null, null, "룰러의 이름으로 명한다"],
[0, "네무링", null, 0, null, 78, 63, 58, 0, 160, 333, null, null, "공주님이 되어버리면 되는거야."],
[0, "크랑테일", null, 0, null, 75, 86, 72, 0, 160, 333, null, null, "라즐리느와 페치카가 살려준 난...... 못 죽어"],
[0, "리플", null, 0, null, 59, 81, 70, 0, 160, 299, null, null, "톱스피드를 그런식으로 말하지마…!!!"],
[0, "리플", null, 0, null, 86, 84, 70, 0, 160, 299, null, null, "톱스피드를 그런식으로 말하지마…!!!"],
[0, "글라시아네", null, 0, null, 42, 20, 85, 0, 160, 333, null, null, "죽이지마! 죽고싶지 않아!!"],
[0, "페치카", null, 0, null, 71, 33, 75, 0, 160, 333, null, null, "그래도…이번엔 누군가를 구할수있어서..행복해요…"],
[0, "하무엘", null, 0, null, 73, 23, 65, 0, 160, 333, null, null, "푸크푹이 유적안으로..윽…들어가고…있습니다…"],
[0, "이누보자키 타마", null, 0, null, 76, 88, 27, 0, 160, 300, null, null, "어디서 부터 잘못된걸까…."],
[0, "파르", null, 0, null, 30, 10, 82, 0, 160, 300, null, null, "여기서 벌어진 일들을 모두 마법나라에 고발했퐁!"],
[0, "타카마치 나노하", null, 0, null, 85, 105, 85, 0, 160, 300, "패권", "위압", "잠깐 머리좀 식힐까?"],
[0, "페이트 하라오운", null, 0, null, 75, 89, 73, 0, 160, 300, null, null],
[0, "타카마치 비비오", null, 0, null, 75, 70, 70, 0, 160, 300, null, null, "엄마를 돌려줘!!"],
[0, "린디 하라오운", null, 0, null, 78, 50, 50, 0, 160, 300, null, null],
[0, "크로노 하라오운", null, 0, null, 73, 27, 58, 0, 160, 300, null, null, "시공관리국 소속의 집무관 크로노 하라오운이다!!"],
[0, "하무엘", null, 0, null, 93, 43, 87, 0, 160, 333, null, null, "푸크푹이 유적안으로..윽…들어가고…있습니다…"],
[0, "이누보자키 타마", null, 0, null, 76, 88, 10, 0, 160, 300, null, null, "어디서 부터 잘못된걸까…."],
[0, "파르", null, 0, null, 55, 10, 82, 0, 160, 300, null, null, "여기서 벌어진 일들을 모두 마법나라에 고발했퐁!"],
[0, "타카마치 나노하", null, 0, null, 95, 105, 85, 0, 160, 300, "패권", "위압", "잠깐 머리좀 식힐까?"],
[0, "페이트 하라오운", null, 0, null, 89, 95, 73, 0, 160, 300, null, null, "우리들의 어떤 것도 아직 시작하지도 않았어. 그러니까 진정한 자신을 시작하기 위해서, 지금까지의 자신을 끝내러 가자"],
[0, "타카마치 비비오", null, 0, null, 75, 88, 79, 0, 160, 300, null, null, "엄마를 돌려줘!!"],
[0, "린디 하라오운", null, 0, null, 94, 63, 89, 0, 160, 300, null, null, "결계 마도사의 포진. 완료했어. 지시대로 관동 전역을 커버할수 있을 정도로 전개해 놨어."],
[0, "크로노 하라오운", null, 0, null, 73, 84, 58, 0, 160, 300, null, null, "시공관리국 소속의 집무관 크로노 하라오운이다!!"],
[0, "스바루 나카지마", null, 0, null, 67, 72, 64, 0, 160, 300, null, null],
[0, "긴카 나카지마", null, 0, null, 73, 80, 52, 0, 160, 300, null, null],
[0, "캐로", null, 0, null, 71, 63, 74, 0, 160, 300, null, null],
[0, "에리오 몬디알", null, 0, null, 65, 68, 72, 0, 160, 300, null, null],
[0, "티아나 란스터", null, 0, null, 72, 82, 78, 0, 160, 300, null, null],
[0, "야가미 하야테", null, 0, null, 74, 55, 85, 0, 160, 300, null, "환술"],
[0, "시그넘", null, 0, null, 75, 89, 79, 0, 160, 300, null, null],
[0, "비타", null, 0, null, 70, 85, 50, 0, 160, 300, null, null],
[0, "야가미 하야테", null, 0, null, 74, 55, 85, 0, 160, 300, null, "환술", "야천의 주인의 이름으로, 그대에게 새로운 이름을 내리리라. 굳게 받쳐주는 자. 행운의 순풍, 축복의 바람, 리인포스."],
[0, "시그넘", null, 0, null, 75, 89, 79, 0, 160, 300, null, null, "멈출 수 없어. 우리 수호기사는 주인의 미소를 위해서라면, 기사의 긍지까지도 버리겠다고 맹세했다. 이젠, 멈출 수 없어!"],
[0, "비타", null, 0, null, 70, 85, 50, 0, 160, 300, null, null, "바보냐! 이런 수의 탄환을 전부 컨트롤 할 수 있을 리 없잖아!"],
[0, "샤멀", null, 0, null, 55, 75, 80, 0, 160, 300, null, null],
[0, "노베", null, 0, null, 67, 80, 70, 0, 160, 300, null, null],
[0, "노베", null, 0, null, 77, 80, 70, 0, 160, 300, null, null],
[0, "칭크", null, 0, null, 63, 84, 75, 0, 160, 300, null, null],
[0, "콰트르", null, 0, null, 64, 39, 92, 0, 160, 300, null, null],
[0, "스칼리에티", null, 0, null, 41, 12, 94, 0, 160, 300, null, "발명", "나는 너의 아빠쯤 되려나? 페이트."],
[0, "스칼리에티", null, 0, null, 66, 12, 94, 0, 160, 300, null, "발명", "나는 너의 아빠쯤 되려나? 페이트."],
[0, "유노", null, 0, null, 64, 45, 90, 0, 160, 300, null, null],
[0, "쟈피라", null, 0, null, 56, 83, 65, 0, 160, 300, null, null],
[0, "셰인", null, 0, null, 43, 67, 30, 0, 160, 300, null, null],
@@ -76,7 +77,7 @@
[0, "클로에", null, 0, null, 60, 73, 70, 0, 160, 300, null, null],
[0, "토오사카 린", null, 0, null, 72, 62, 83, 0, 160, 300, null, null],
[0, "루비아", null, 0, null, 56, 40, 50, 0, 160, 300, null, "상재"],
[0, "에미야 시로", null, 0, null, 78, 40, 70, 0, 160, 300, null, null, "Trace on!"],
[0, "에미야 시로", null, 0, null, 78, 40, 70, 0, 160, 300, null, null, "영웅왕, 저장된 무기는 충분한가."],
[0, "타이가", null, 0, null, 55, 50, 40, 0, 160, 300, null, null],
[0, "루비", null, 0, null, 49, 20, 70, 0, 160, 300, null, null],
[0, "사파이어", null, 0, null, 49, 55, 86, 0, 160, 300, null, null],
@@ -87,18 +88,18 @@
[0, "젤레치", null, 0, null, 84, 52, 99, 0, 100, 300, null, null],
[0, "웨이버 벨벳", null, 0, null, 75, 43, 90, 0, 160, 300, null, null, "아니,나는 그의 신하다"],
[0, "후루카와 나기사", null, 0, null, 85, 30, 85, 0, 160, 300, null, null, "네, 마을의 마음이에요!"],
[0, "오카자키 토모야", null, 0, null, 85, 84, 75, 0, 160, 300, null, null],
[0, "오카자키 토모야", null, 0, null, 85, 84, 75, 0, 160, 300, null, null, "우린 오르기 시작한다. 길고, 긴, 언덕길을..."],
[0, "후지바야시 쿄", null, 0, null, 80, 85, 82, 0, 160, 300, null, "무쌍"],
[0, "후지바야시 료", null, 0, null, 74, 30, 77, 0, 160, 300, null, null],
[0, "사카가미 토모요", null, 0, null, 80, 92, 87, 0, 160, 300, null, "무쌍"],
[0, "히이라기 캇페이", null, 0, null, 75, 55, 18, 0, 160, 300, null, null],
[0, "스노하라 요헤이", null, 0, null, 69, 60, 30, 0, 160, 300, null, "견고", "그리고 변기커버!"],
[0, "스노하라 요헤이", null, 0, null, 69, 88, 30, 0, 160, 300, null, "견고", "그리고 변기커버!"],
[0, "스노하라 메이", null, 0, null, 70, 70, 70, 0, 160, 300, null, null],
[0, "후루카와 사나에", null, 0, null, 80, 40, 75, 0, 160, 300, null, null, "가족인걸요"],
[0, "후루카와 아키오", null, 0, null, 76, 86, 70, 0, 160, 300, null, "무쌍", "부모는 자식에게 꿈을 맡기는 거야!!! 네 꿈을 이루는게 나와 사나에의 꿈이야!!! 그걸 여기서 포기하면 안돼!!"],
[0, "오카자키나오유키", null, 0, null, 70, 20, 55, 0, 160, 300, null, null, "이제 괜찮은걸까… 내가 해야할 일을 끝낸걸까… 그래… 고맙다 토모야"],
[0, "이치노세 코토미", null, 0, null, 74, 52, 100, 0, 160, 300, null, null, "아빠,엄마...다녀오셨어요.."],
[0, "오카자키 우시오", null, 0, null, 82, 55, 83, 0, 160, 300, null, null, "안녕.. 아빠…"],
[0, "오카자키 우시오", null, 0, null, 82, 55, 87, 0, 160, 300, null, null, "안녕.. 아빠…"],
[0, "니시나 리에", null, 0, null, 77, 37, 82, 0, 160, 300, null, null],
[0, "미야자와 유키네", null, 0, null, 78, 30, 85, 0, 160, 300, null, null],
[0, "시마 카츠키", null, 0, null, 72, 49, 63, 0, 160, 300, null, null, "주인님 주인님 지금의 주인님은 그날의 주인님과는 다른 사람이지만 너무나 상냥한 사람이에요"],
@@ -114,14 +115,14 @@
[0, "루", null, 0, null, 81, 50, 73, 0, 160, 300, null, null, "난 미우를 좋아해."],
[0, "완냐", null, 0, null, 69, 48, 75, 0, 160, 300, null, null, "미유씨와 카나타씨의…설마 아니겠죠?…"],
[0, "야마무라 미캉", null, 0, null, 75, 64, 30, 0, 160, 300, null, null],
[0, "야보시 세이야", null, 0, null, 70, 50, 70, 0, 160, 300, null, null],
[0, "야보시 루이", null, 0, null, 75, 73, 71, 0, 160, 300, null, null, "샤라크성 출신의 가난한 우주여행자 야보시 세이야야!"],
[0, "야보시 세이야", null, 0, null, 70, 50, 70, 0, 160, 300, null, null, "샤라크성 출신의 가난한 우주여행자 야보시 세이야야!"],
[0, "야보시 루이", null, 0, null, 75, 73, 71, 0, 160, 300, null, null],
[0, "도리안", null, 0, null, 65, 49, 86, 0, 160, 300, null, "발명"],
[0, "키위", null, 0, null, 66, 33, 70, 0, 160, 300, null, "상재"],
[0, "구아바", null, 0, null, 45, 49, 51, 0, 160, 300, null, null, "와…왕자님…."],
[0, "히카리가오카", null, 0, null, 70, 49, 77, 0, 160, 300, null, null, "날 그렇게 봐준 레이디는 당신이 처음이야."],
[0, "코즈키유우", null, 0, null, 85, 33, 75, 0, 160, 300, null, "발명"],
[0, "코즈키미키", null, 0, null, 88, 33, 80, 0, 160, 300, null, null, "그래도 이래뵈도 걱정되서 온거라고..?"],
[0, "코즈키 유우", null, 0, null, 85, 33, 75, 0, 160, 300, null, "발명"],
[0, "코즈키 미키", null, 0, null, 88, 33, 80, 0, 160, 300, null, null, "그래도 이래뵈도 걱정되서 온거라고..?"],
[0, "란", null, 0, null, 80, 72, 40, 0, 160, 300, null, null, "어이 미우! 이게 무슨 소리야? 왜 너희 부모님이랑 루가 아는 사이인건데?"],
[0, "하루네 아이라", null, 0, null, 84, 45, 74, 0, 160, 300, null, null, "오로라~~~~~~~~~라이징~ 드리임!"],
[0, "아마미야 리즈무", null, 0, null, 80, 25, 82, 0, 160, 300, null, null],
@@ -145,14 +146,14 @@
[0, "펭귄선생", null, 0, null, 55, 44, 49, 0, 160, 300, null, null],
[0, "카미죠 토우마", null, 0, null, 70, 80, 50, 0, 160, 300, null, null, "이 악물어라 최강!.... 내 최약은 조금 아플거거든!"],
[0, "인덱스", null, 0, null, 30, 10, 89, 0, 160, 300, null, null],
[0, "미사카 미코토", null, 0, null, 78, 93, 88, 0, 160, 300, null, null, "당신이 살아있는 것만으로도, 구원받은 생명도 있어.\n당신이 그렇게 앞만 보고 달리는 것만으로, 용기를 얻은 사람도 있어.\n그러니까 망설이지 마!! 당신이 걸어온 길이 옳다는 건,"],
[0, "엑셀레이터", null, 0, null, 71, 110, 90, 0, 160, 300, null, "위압"],
[0, "리네트 비숍", null, 0, null, 80, 81, 82, 0, 160, 300, null, "저격"],
[0, "라스트오더", null, 0, null, 75, 10, 90, 0, 160, 300, null, null],
[0, "미사카 미코토", null, 0, null, 78, 93, 88, 0, 160, 300, null, null, "당신이 살아있는 것만으로도, 구원받은 생명도 있어.\n당신이 그렇게 앞만 보고 달리는 것만으로, 용기를 얻은 사람도 있어."],
[0, "엑셀레이터", null, 0, null, 71, 110, 90, 0, 160, 300, null, "위압", "압축, 압축, 공기를 압축..."],
[0, "리네트 비숍", null, 0, null, 80, 81, 82, 0, 160, 300, null, null],
[0, "라스트오더", null, 0, null, 75, 10, 90, 0, 160, 300, null, null, "미사카는 미사카는~"],
[0, "카키네 테이토쿠", null, 0, null, 55, 98, 22, 0, 160, 222, null, null],
[0, "무기노 시즈리", null, 0, null, 78, 90, 80, 0, 160, 300, null, null],
[0, "쇼쿠호 미사키", null, 0, null, 77, 30, 60, 0, 160, 300, null, null],
[0, "에리카 하르트만", null, 0, null, 81, 88, 68, 0, 160, 300, null, null],
[0, "프렌다 세이베른", null, 0, null, 64, 78, 55, 0, 160, 300, null, null],
[0, "아레이스타", null, 0, null, 67, 39, 90, 0, 160, 300, null, null],
[0, "무스지메 이와키", null, 0, null, 57, 49, 75, 0, 160, 300, null, null],
[0, "시라이 쿠로코", null, 0, null, 56, 29, 60, 0, 160, 300, null, "견고"],
@@ -162,12 +163,12 @@
[0, "칸자키", null, 0, null, 70, 92, 75, 0, 160, 300, null, null],
[0, "스테일 마그누스", null, 0, null, 47, 88, 55, 0, 160, 300, null, null],
[0, "츠쿠요미 코모에", null, 0, null, 78, 10, 85, 0, 160, 300, null, null],
[0, "에도가와 코난", null, 0, null, 75, 72, 99, 0, 160, 300, null, null, "미궁을 모르는 명탐정! 진실은 언제나 하나!"],
[0, "모리 란", null, 0, null, 79, 100, 82, 0, 160, 300, null, "돌격"],
[0, "에도가와 코난", null, 0, null, 85, 72, 99, 0, 160, 300, null, null, "미궁을 모르는 명탐정! 진실은 언제나 하나!"],
[0, "모리 란", null, 0, null, 79, 105, 82, 0, 160, 300, null, "돌격"],
[0, "스즈키 소노코", null, 0, null, 74, 55, 86, 0, 160, 300, null, "상재"],
[0, "쿄고쿠 마코토", null, 0, null, 79, 108, 52, 0, 160, 300, null, "무쌍"],
[0, "쿄고쿠 마코토", null, 0, null, 82, 108, 52, 0, 160, 300, null, "무쌍"],
[0, "진", null, 0, null, 79, 87, 90, 0, 160, 300, null, "돌격", "시체가 없다는건 죽지 않았다는 거지"],
[0, "베르무트", null, 0, null, 86, 67, 96, 0, 160, 300, null, null, "A secret makes a woman woman…"],
[0, "베르무트", null, 0, null, 92, 53, 96, 0, 160, 300, null, null, "A secret makes a woman woman…"],
[0, "키얀티", null, 0, null, 56, 80, 49, 0, 160, 300, null, "저격"],
[0, "코른", null, 0, null, 57, 84, 55, 0, 160, 300, null, "저격"],
[0, "키르", null, 0, null, 64, 77, 63, 0, 150, 300, null, null],
@@ -175,7 +176,7 @@
[0, "Rum", null, 0, null, 80, 30, 80, 0, 160, 300, null, null],
[0, "헤이지", null, 0, null, 85, 83, 95, 0, 160, 300, null, null],
[0, "카즈하", null, 0, null, 87, 79, 74, 0, 160, 300, null, null],
[0, "메구레 경부", null, 0, null, 94, 79, 88, 0, 160, 300, null, null, "지금 당신이 하고 있는 행동은 정의를 위한 것도, 죽은 아들을 위한 것도 아니야. 그저 홧김에 저지른 범죄일 뿐. 자기 고집만 믿고 사람의 생명을 위협하는 행동은 아무 의미도 없"],
[0, "메구레 경부", null, 0, null, 94, 79, 88, 0, 160, 300, null, null, "지금 당신이 하고 있는 행동은 정의를 위한 것도, 죽은 아들을 위한 것도 아니야. 그저 홧김에 저지른 범죄일 뿐. 자기 고집만 믿고 사람의 생명을 위협하는 행동은 아무 의미도 없고 어리석은 행동에 지나지 않아! 그걸 아직도 모르겠나!!!"],
[0, "타카기 형사", null, 0, null, 67, 80, 79, 0, 160, 300, null, null],
[0, "미와코", null, 0, null, 70, 83, 82, 0, 160, 300, null, null],
[0, "시라토리 형사", null, 0, null, 71, 84, 81, 0, 160, 300, null, null],
@@ -184,7 +185,7 @@
[0, "아카이 슈이치", null, 0, null, 86, 90, 100, 0, 160, 300, null, "저격"],
[0, "제임스 블랙", null, 0, null, 88, 55, 85, 0, 160, 300, null, null],
[0, "케멜수사관", null, 0, null, 67, 78, 79, 0, 160, 300, null, null],
[0, "하이바라 아이", null, 0, null, 78, 49, 100, 0, 160, 300, null, "발명"],
[0, "하이바라 아이", null, 0, null, 78, 49, 100, 0, 160, 300, null, "발명", "자기는 별 고생도 않고 남을 의지하는건 나쁜 버릇이에요"],
[0, "소년탐정단", null, 0, null, 67, 85, 74, 0, 160, 300, null, null],
[0, "워커", null, 0, null, 73, 80, 66, 0, 160, 300, null, null],
[0, "보스", null, 0, null, 75, 78, 78, 0, 160, 300, null, null],
@@ -211,7 +212,7 @@
[0, "시아", null, 0, null, 81, 88, 78, 0, 160, 300, null, "돌격"],
[0, "로안", null, 0, null, 85, 83, 79, 0, 160, 300, null, null],
[0, "후우란", null, 0, null, 82, 30, 90, 0, 160, 300, null, null],
[0, "파이렌", null, 0, null, 84, 85, 64, 0, 160, 300, null, "기병"],
[0, "파이렌", null, 0, null, 84, 85, 64, 0, 160, 300, null, "기병", "파이땅이라 부르지 마!"],
[999, "파이탄", null, 0, null, 19, 10, 67, 0, 160, 300, null, null],
[999, "쿠우탄", null, 0, null, 10, 9, 33, 0, 160, 300, null, null],
[0, "엔야", null, 0, null, 79, 91, 38, 0, 160, 300, null, null],
@@ -221,12 +222,12 @@
[0, "토시", null, 0, null, 86, 92, 36, 0, 160, 300, null, "돌격"],
[0, "이이셰", null, 0, null, 87, 94, 41, 0, 160, 300, null, null, "지력 36이 지력100을 이겼다!"],
[0, "마치", null, 0, null, 77, 23, 94, 0, 160, 300, null, null],
[0, "카린", null, 0, null, 96, 75, 92, 0, 160, 300, null, "반계"],
[0, "카린", null, 0, null, 96, 75, 92, 0, 160, 300, null, "반계", "사실 넌 황제와 조정을 진정한 주인으로 생각한 적이 없잖아?"],
[0, "슌란", null, 0, null, 85, 89, 10, 0, 160, 300, null, "돌격"],
[0, "슈란", null, 0, null, 87, 92, 75, 0, 160, 300, null, "저격"],
[0, "케이파", null, 0, null, 51, 14, 95, 0, 160, 300, null, "집중", "아아…카린님…!"],
[0, "곽가린", null, 0, null, 76, 19, 96, 0, 160, 300, null, "신산"],
[0, "정욱후우", null, 0, null, 80, 35, 92, 0, 160, 300, null, null],
[0, "연희무쌍린", null, 0, null, 76, 19, 96, 0, 160, 300, null, "신산"],
[0, "후우", null, 0, null, 80, 35, 92, 0, 160, 300, null, null],
[0, "나기", null, 0, null, 80, 84, 53, 0, 160, 300, null, null],
[0, "사와", null, 0, null, 83, 78, 63, 0, 160, 300, null, null],
[0, "마오", null, 0, null, 77, 78, 76, 0, 160, 300, null, null],
@@ -238,7 +239,7 @@
[0, "카론", null, 0, null, 89, 86, 51, 0, 160, 300, null, null],
[0, "에이카", null, 0, null, 79, 81, 52, 0, 160, 300, null, null, "좌절감이 숙녀를 키우는 것이에요!!!"],
[0, "초선", null, 0, null, 75, 100, 55, 0, 160, 300, null, "위압"],
[0, "루린", null, 0, null, 74, 76, 67, 0, 160, 300, null, null, "걱정돼…걱정된다고!"],
[0, "루린", null, 0, null, 74, 76, 67, 0, 160, 300, null, null],
[0, "샨푸", null, 0, null, 89, 94, 73, 0, 160, 300, null, null],
[0, "토우", null, 0, null, 22, 8, 77, 0, 160, 300, null, null],
[0, "스우", null, 0, null, 81, 50, 83, 0, 160, 300, null, null],
@@ -270,7 +271,7 @@
[0, "미코", null, 0, null, 49, 21, 84, 0, 160, 300, null, null, "남자인지 여자인지는 몰라도 아무래도 상관없이 영웅이니까 좋아"],
[0, "냐온", null, 0, null, 72, 30, 90, 0, 160, 300, "패권", "발명", "니얀다의 정체가 알고 싶은 건 사실이지만, 이런 식으로는 알고 싶지 않아!"],
[0, "콩이", null, 0, null, 62, 60, 75, 0, 160, 300, null, null, "불쌍하신 냐온사마…"],
[0, "네코선인", null, 0, null, 88, 52, 80, 0, 160, 300, "은둔", "신산"],
[999, "네코선인", null, 0, null, 88, 52, 80, 0, 160, 300, "은둔", "신산", "지상에 내려올 수 없는 내 대신에 세계를 구하기 위해,"],
[0, "긴코", null, 0, null, 67, 79, 90, 0, 160, 300, null, null],
[0, "사라카치", null, 0, null, 70, 83, 60, 0, 160, 300, null, null],
[0, "미이라맨", null, 0, null, 80, 80, 60, 0, 160, 300, null, "돌격", "미이라~맨"],
@@ -307,18 +308,18 @@
[0, "리디", null, 0, null, 77, 76, 80, 0, 160, 300, null, null],
[0, "수르", null, 0, null, 79, 70, 84, 0, 160, 300, null, null],
[0, "리아나", null, 0, null, 72, 78, 67, 0, 160, 300, null, null],
[0, "이츠카시도", null, 0, null, 87, 82, 80, 0, 160, 300, null, "환술", "이 세상 모든 사람이 널 정해도 난 그들보다 몇 배는 더 널 정해주겠어"],
[0, "이츠카 시도", null, 0, null, 87, 82, 80, 0, 160, 300, null, "환술", "이 세상 모든 사람이 널 정해도 난 그들보다 몇 배는 더 널 정해주겠어"],
[0, "이츠카 코토리", null, 0, null, 87, 91, 78, 0, 160, 300, null, null],
[0, "야토가미 토카", null, 0, null, 84, 94, 10, 0, 160, 300, null, null],
[0, "요시노", null, 0, null, 88, 85, 64, 0, 160, 300, null, null],
[0, "쿠루미", null, 0, null, 86, 92, 83, 0, 160, 300, null, null],
[0, "타카미야 마나", null, 0, null, 81, 82, 74, 0, 160, 300, null, null],
[0, "소노가미 린네", null, 0, null, 88, 95, 75, 0, 160, 300, null, null, "나는 시도의 소꿉친구라도, 연인이라도, 아내라도, 동생이라도, 누나라도, 어머니라도, 딸이라도, 상사라도, 부하라도, 적이라도, 원수라도, 타인이라도, 상관 없어―시도가 행복하다"],
[0, "소노가미 린네", null, 0, null, 88, 95, 75, 0, 160, 300, null, null, "나는 시도의 소꿉친구라도, 연인이라도, 아내라도, 동생이라도, 누나라도, 어머니라도, 딸이라도, 상사라도, 부하라도, 적이라도, 원수라도, 타인이라도, 상관 없어―시도가 행복하다면 그것만으로"],
[0, "카구야", null, 0, null, 72, 90, 20, 0, 160, 300, null, null, "훗 이몸의 종이 되거라 시도."],
[0, "유즈루", null, 0, null, 75, 80, 80, 0, 160, 300, null, null, "승낙. 그 승부, 받아주겠어요.」"],
[0, "이자요이 미쿠", null, 0, null, 87, 82, 70, 0, 160, 300, null, null],
[0, "마유리", null, 0, null, 81, 81, 81, 0, 160, 300, null, null],
[0, "아루스 마리아", null, 0, null, 75, 59, 92, 0, 160, 300, null, null],
[0, "아루스 마리아", null, 0, null, 75, 59, 92, 0, 160, 300, null, null, "······당신에게, 묻겠습니다. ───『사랑』이라는 것은, 무엇입니까?"],
[0, "아루스 마리나", null, 0, null, 75, 59, 94, 0, 160, 300, null, null],
[0, "소노가미 리오", null, 0, null, 80, 87, 77, 0, 160, 300, null, null, "리오 나는 소노가미 리오야."],
[0, "나츠미", null, 0, null, 74, 83, 75, 0, 160, 300, null, "환술"],
@@ -357,16 +358,16 @@
[0, "큐어 이그렛", null, 0, null, 82, 88, 94, 0, 160, 300, null, null],
[0, "미치루", null, 0, null, 84, 86, 80, 0, 160, 300, null, null],
[0, "카오루", null, 0, null, 84, 80, 86, 0, 160, 300, null, null],
[0, "큐어 드림", null, 0, null, 92, 96, 60, 0, 160, 300, null, "통찰", "희망이가진 위대한 힘! 큐어드림!"],
[0, "큐어 루즈", null, 0, null, 74, 93, 78, 0, 160, 300, null, null],
[0, "큐어 드림", null, 0, null, 97, 96, 60, 0, 160, 300, null, "통찰", "희망이가진 위대한 힘! 큐어드림!"],
[0, "큐어 루즈", null, 0, null, 74, 93, 78, 0, 160, 300, null, null, "정열은 붉은 불꽃! 큐어 루즈!"],
[0, "키리야", null, 0, null, 77, 89, 85, 0, 160, 300, null, null, "고마워요..선배들…이세계에 살아가는 의미를 알게해줘서…"],
[0, "샤이 루미너스", null, 0, null, 73, 89, 75, 0, 160, 300, null, null],
[0, "샤이 루미너스", null, 0, null, 73, 89, 75, 0, 160, 300, null, null, "빛의 마음과 빛의 의지, 모든 걸 하나로 모으기 위해!"],
[0, "레모네이드", null, 0, null, 84, 87, 66, 0, 160, 300, null, null],
[0, "큐어 민트", null, 0, null, 80, 75, 77, 0, 160, 300, null, null],
[0, "큐어 아쿠아", null, 0, null, 83, 80, 76, 0, 160, 300, null, null],
[0, "밀키로즈", null, 0, null, 84, 97, 72, 0, 160, 300, null, null],
[0, "분비", null, 0, null, 81, 88, 75, 0, 160, 300, null, "견고", "빨리 일 끝내고 퇴근해야지. 난 중간관리직이라 야근을 해도 추가수당이 안 나오거든"],
[0, "가마오", null, 0, null, 42, 67, 36, 0, 160, 300, null, null],
[0, "가마오", null, 0, null, 42, 67, 36, 0, 160, 300, null, null, "아 배고파…"],
[0, "큐어 블로섬", null, 0, null, 86, 75, 84, 0, 160, 300, null, null],
[0, "큐어 마린", null, 0, null, 71, 82, 41, 0, 160, 300, null, null],
[0, "큐어 션샤인", null, 0, null, 77, 83, 80, 0, 160, 300, null, null],
@@ -381,7 +382,7 @@
[0, "큐어 로제타", null, 0, null, 80, 80, 85, 0, 160, 300, null, "상재"],
[0, "큐어소드", null, 0, null, 79, 87, 82, 0, 160, 300, null, "척사"],
[0, "큐어에이스", null, 0, null, 75, 83, 77, 0, 160, 300, null, null],
[0, "하세가와 스바루", null, 0, null, 86, 77, 80, 0, 160, 300, null, null, "응 솔직히 아직 승산이 있있다는건 아니야.....하지만 그렇게 해서 너의 농구를 지킬수 있다면, 할 수 있는 데까진 해보고 싶어. 그래. 하게 해줘. 게다가 전력을 다한 네가 그"],
[0, "하세가와 스바루", null, 0, null, 86, 77, 80, 0, 160, 300, null, null, "응 솔직히 아직 승산이 있있다는건 아니야.....하지만 그렇게 해서 너의 농구를 지킬수 있다면, 할 수 있는 데까진 해보고 싶어. 그래. 하게 해줘. 게다가 전력을 다한 네가 그 정도의 선수라면, 아직 희망은 있어. 앞에 했던 말은 취소. 전부 다시 생각해 봐야겠어. 이건 빈말이 아니야. 응, 넌 정말로 굉장해. 지구우승? 하, 그게 어쨌다는 거야. 그런 간판보다도 네가 훨씬 굉장해."],
[0, "미나토 토모카", null, 0, null, 70, 64, 82, 0, 160, 300, null, null],
[0, "카시이 아이리", null, 0, null, 71, 76, 75, 0, 160, 300, null, null, "키얘기는 하지마세요!!"],
[0, "미사와 마호", null, 0, null, 65, 62, 77, 0, 160, 300, null, "상재"],
@@ -429,7 +430,7 @@
[0, "우에사토 히나타", null, 0, null, 80, 19, 85, 0, 160, 300, null, "귀모"],
[0, "이요지마 안즈", null, 0, null, 76, 88, 86, 0, 160, 295, null, "궁병", "만일 다시 태어난다면… 타마코와 자매로 태어나기를…."],
[0, "도이 타마코", null, 0, null, 72, 89, 37, 0, 160, 300, null, "견고"],
[0, "코오리치카게", null, 0, null, 67, 90, 83, 0, 160, 300, null, null, "나는 용자, 코오리 치카게 성의 한자는 '群'이 아니라 '郡' 자주 착각당하곤 해혈액형은 A 좋아하는 음식은 우동. 가다랑어도 꽤 좋아해 게임은 특기, 건슈팅이라든지. 누구라도 "],
[0, "코오리치카게", null, 0, null, 67, 90, 83, 0, 160, 300, null, null, "나는 용자, 코오리 치카게 성의 한자는 '群'이 아니라 '郡' 자주 착각당하곤 해혈액형은 A 좋아하는 음식은 우동. 가다랑어도 꽤 좋아해 게임은 특기, 건슈팅이라든지. 누구라도 좋으니까 기억해줬으면 좋겠어 나는 용사, 코오리 치카게"],
[0, "타카시마 유우나", null, 0, null, 72, 88, 75, 0, 160, 300, null, null],
[0, "시라토리 우타노", null, 0, null, 71, 83, 67, 0, 160, 300, null, "경작"],
[0, "후지모리 미토", null, 0, null, 70, 15, 81, 0, 160, 300, null, null],
@@ -478,7 +479,7 @@
[0, "로니에", null, 0, null, 80, 76, 52, 0, 160, 300, null, null],
[0, "벡터", null, 0, null, 81, 55, 77, 0, 160, 300, null, null],
[0, "최고사제", null, 0, null, 57, 19, 95, 0, 160, 300, null, null],
[0, "코우사카 호노카", null, 0, null, 88, 55, 64, 0, 160, 300, null, null, "비야 멈춰라~~~~~~!"],
[0, "코우사카 호노카", null, 0, null, 99, 55, 70, 0, 160, 300, null, null, "해보면 되잖아? 하고싶으니까, 해본다 정말로 하고싶은 것이란 그런느낌으로 시작하는거 아니야?"],
[0, "소노다 우미", null, 0, null, 75, 55, 88, 0, 160, 300, null, null],
[0, "미나미 코토리", null, 0, null, 76, 54, 80, 0, 160, 300, null, null, "코토리의 간식으로 삼아버릴 거야~!)"],
[0, "야자와 니코", null, 0, null, 78, 52, 66, 0, 160, 300, null, "인덕", "にっこにっこにー♪ あなたのハートににっこにっこにー♪ 笑顔届ける矢沢にこにこ♪ にっこにいって覚えてラブニコ♪"],
@@ -500,7 +501,7 @@
[0, "프리실라", null, 0, null, 76, 83, 56, 0, 160, 300, null, null],
[0, "멜티나", null, 0, null, 71, 82, 65, 0, 160, 300, null, null],
[0, "미즈시마 하나에", null, 0, null, 74, 80, 65, 0, 160, 300, null, null],
[0, "하루카제 도레미", null, 0, null, 86, 75, 42, 0, 160, 300, null, null, "엄마가 예전에 말했지. 엄마는 아기를 낳아서가 엄마가 아니라 아기를 키워가면서 엄마가 되어간다고..... 나 1년간 하나를 키우면서 그 의미를 알 것같은 기분이 들어. 하나를 지"],
[0, "하루카제 도레미", null, 0, null, 86, 75, 42, 0, 160, 300, null, null, "엄마가 예전에 말했지. 엄마는 아기를 낳아서가 엄마가 아니라 아기를 키워가면서 엄마가 되어간다고..... 나 1년간 하나를 키우면서 그 의미를 알 것같은 기분이 들어. 하나를 지켜줄 수 있는 사람은 엄마인 나 밖에 없어"],
[0, "하즈키", null, 0, null, 78, 61, 80, 0, 160, 300, null, null, "프리티 윗치 하즈키치!"],
[0, "아이코", null, 0, null, 78, 61, 75, 0, 160, 300, null, null, "프리티 윗치 아이코치!"],
[0, "하루카제 폿프", null, 0, null, 70, 70, 79, 0, 160, 300, null, null, "프리티 윗치 폿프치!"],
@@ -559,13 +560,13 @@
[0, "무사시", null, 0, null, 75, 85, 60, 0, 160, 300, null, "무쌍"],
[0, "에비스자와쿠루미", null, 0, null, 72, 88, 59, 0, 160, 300, null, "무쌍"],
[0, "타케야유키", null, 0, null, 73, 39, 29, 0, 160, 300, null, null, "학교생활부는 언제나 함께야!"],
[0, "와카사 유리", null, 0, null, 77, 33, 90, 0, 160, 300, null, null],
[0, "나오키 미키", null, 0, null, 74, 66, 75, 0, 160, 300, null, null],
[0, "와카사 유리", null, 0, null, 77, 33, 90, 0, 160, 300, null, null, "그래… 대학이란 건 다 같이 공부하는 곳이야."],
[0, "나오키 미키", null, 0, null, 74, 66, 75, 0, 160, 300, null, null, "그럴지도 몰라요. 하지만.. 살기만 하면, 그걸로 충분한가요?"],
[0, "사쿠라 메구미", null, 0, null, 77, 32, 80, 0, 160, 300, null, null, "언니가 아니라 사쿠라 선생님!"],
[0, "다이몬 마사루", null, 0, null, 56, 99, 42, 0, 160, 300, null, null, "어떤 때라도 최후의 최후까지 포기하지 않고 싸운다. 그게 남자가 사는 길이 아니겠나?."],
[0, "히나모리 아무", null, 0, null, 83, 70, 67, 0, 160, 300, null, null, "림없이 이어질 거야. 미래에도 이 맞잡은 손을…"],
[0, "다이몬 마사루", null, 0, null, 73, 104, 42, 0, 160, 300, null, null, "어떤 때라도 최후의 최후까지 포기하지 않고 싸운다. 그게 남자가 사는 길이 아니겠나?."],
[0, "히나모리 아무", null, 0, null, 83, 70, 67, 0, 160, 300, null, null, "림없이 이어질 거야. 미래에도 이 맞잡은 손을…"],
[0, "발푸르기스의 밤", null, 0, null, 67, 99, 56, 0, 160, 300, null, null, "하하하하하하하하!!"],
[0, "멘마", null, 0, null, 75, 56, 77, 0, 160, 300, null, null, "멘마 있지..! 더 함께 있고 싶어 놀고 싶어…. 그러니까…!다시 태어날꺼야…! 너희랑 같이 있을꺼야… 그러니까 진땅 울었으니까 이젠 안녕이야,,,,! 그러니까!!..../들켜 "],
[0, "멘마", null, 0, null, 75, 56, 77, 0, 160, 300, null, null, "멘마 있지..! 더 함께 있고 싶어 놀고 싶어…. 그러니까…!다시 태어날꺼야…! 너희랑 같이 있을꺼야… 그러니까 진땅 울었으니까 이젠 안녕이야,,,,! 그러니까!!...."],
[0, "진땅", null, 0, null, 73, 55, 75, 0, 160, 300, null, null, "얘들아…! /멘마!!! 찾았다..!"],
[0, "유키아츠", null, 0, null, 75, 69, 72, 0, 160, 300, null, null],
[0, "폿포", null, 0, null, 62, 69, 50, 0, 160, 300, null, null, "울렸다~ 울렸다~"],
@@ -579,8 +580,8 @@
[0, "이즈냥", null, 0, null, 76, 74, 67, 0, 160, 300, null, null],
[0, "기타노 유키코", null, 0, null, 88, 31, 95, 0, 160, 300, null, null, "모두들 잘부탁해!"],
[0, "둘리", null, 0, null, 34, 88, 52, 0, 160, 300, null, null, "아저씨가 죽으면 이집은 내가 가져야지."],
[0, "고길동", null, 0, null, 85, 96, 70, 0, 160, 300, null, null],
[0, "이그드라실", null, 0, null, 76, 82, 97, 0, 160, 300, null, null, "마! 내가 홍콩영화 한두편 본줄 알아?"],
[0, "고길동", null, 0, null, 85, 96, 70, 0, 160, 300, null, null, "마! 내가 홍콩영화 한두편 본줄 알아?"],
[0, "이그드라실", null, 0, null, 76, 82, 97, 0, 160, 300, null, null],
[0, "마츠다타카토", null, 0, null, 72, 56, 74, 0, 160, 300, null, null],
[0, "마키노 루키", null, 0, null, 77, 63, 77, 0, 160, 300, null, null],
[0, "리젠랴", null, 0, null, 72, 70, 77, 0, 160, 300, null, null],
@@ -600,17 +601,17 @@
[0, "나오키 아야토", null, 0, null, 80, 53, 80, 0, 160, 300, null, null],
[0, "나카무라 유리", null, 0, null, 81, 62, 76, 0, 160, 300, null, null],
[0, "Yui", null, 0, null, 82, 77, 76, 0, 160, 300, null, null, "결혼! 여자들의 궁극의 행복."],
[0, "히나타 히데키", null, 0, null, 78, 70, 60, 0, 160, 300, null, null, "어떤 장애라도 상관없다고 했잖아! 걸을 수 없어도, 일어설 수 없어도, 만약 아이를 못 낳아도.. 그렇더라도 난 너랑 결혼할거야! 계속, 계속 곁에 있어줄거야. 여기서 만난 넌 "],
[0, "히나타 히데키", null, 0, null, 78, 70, 60, 0, 160, 300, null, null, "어떤 장애라도 상관없다고 했잖아! 걸을 수 없어도, 일어설 수 없어도, 만약 아이를 못 낳아도.. 그렇더라도 난 너랑 결혼할거야! 계속, 계속 곁에 있어줄거야. 여기서 만난 넌 가짜 유이가 아냐. 유이다. 어디서 만났다고 한들 난 널 좋아하게 되었을 거야. 또다시 1/60억의 확률로 만나게 된다면 그때도 또, 네가 움직이지 못하는 몸이라도, 너와 결혼해줄게."],
[0, "오토나시 하츠네", null, 0, null, 74, 27, 80, 0, 160, 300, null, null],
[0, "이와사와 마사미", null, 0, null, 76, 29, 88, 0, 160, 300, null, null],
[0, "아마노 네네", null, 0, null, 82, 44, 81, 0, 160, 300, null, null],
[0, "아마노 유우", null, 0, null, 77, 49, 92, 0, 160, 300, null, null, "어차피 게임인걸?"],
[0, "아오누마 키리하", null, 0, null, 76, 50, 84, 0, 160, 300, null, null],
[0, "우즈마키 나루토", null, 0, null, 88, 94, 72, 0, 160, 300, null, "돌격", "난···닌자학교의 졸업시험에 3번을 떨어졌다···. ···재수없게 졸업시험에 나온 테스트가 항상··· ···항상 꼭 내가 제일 못하는 술법이었기 때문이었지. 분신술은··· 내가 "],
[0, "휴우가 히나타", null, 0, null, 80, 60, 78, 0, 160, 300, null, null],
[0, "우치하 사스케", null, 0, null, 78, 90, 70, 0, 160, 300, null, null],
[0, "우즈마키 나루토", null, 0, null, 95, 94, 72, 0, 160, 300, null, "돌격", "난···닌자학교의 졸업시험에 3번을 떨어졌다···. ···재수없게 졸업시험에 나온 테스트가 항상··· ···항상 꼭 내가 제일 못하는 술법이었기 때문이었지. 분신술은··· 내가 제일 못하는 술법이었어. 운명이 어떻다느니··· 바꿀 수 없다느니 그딴 따분한 소리는 그만 칭얼대라! 넌 나랑 달리··· 쓰레기 따위가 아니니까."],
[0, "휴우가 히나타", null, 0, null, 87, 60, 78, 0, 160, 300, null, null],
[0, "우치하 사스케", null, 0, null, 80, 93, 70, 0, 160, 300, null, null],
[0, "하루노 사쿠라", null, 0, null, 77, 85, 77, 0, 160, 300, null, "의술"],
[0, "휴우가네지", null, 0, null, 75, 80, 62, 0, 160, 300, null, null],
[0, "휴우가 네지", null, 0, null, 75, 80, 62, 0, 160, 300, null, null],
[0, "츠나데", null, 0, null, 75, 70, 84, 0, 160, 300, null, "의술"],
[0, "우미노 이루카", null, 0, null, 74, 68, 88, 0, 160, 300, null, null, "3대 호카게님... 4대 호카게님... 지금 제 눈 앞에 영웅이 있습니다!["],
[0, "록리", null, 0, null, 72, 80, 40, 0, 160, 300, null, null],
@@ -637,7 +638,7 @@
[0, "코우사카쿄우스케", null, 0, null, 70, 19, 30, 0, 160, 300, null, null],
[0, "코우사카 키리노", null, 0, null, 60, 60, 19, 0, 160, 300, null, null, "나한테 우리오빠 뺏긴 기분이 어떄?"],
[0, "쿠로네코", null, 0, null, 81, 20, 72, 0, 160, 300, null, null],
[0, "사오리 버지나", null, 0, null, 93, 37, 70, 0, 160, 300, null, null],
[0, "사오리 버지나", null, 0, null, 75, 37, 70, 0, 160, 300, null, null],
[0, "타무라 마나미", null, 0, null, 82, 50, 80, 0, 160, 300, null, null, "남매끼리 연애? 말이된다고 생각해?"],
[0, "아라가키 아야셰", null, 0, null, 75, 59, 77, 0, 160, 300, null, null],
[0, "보보보", null, 0, null, 67, 99, 50, 0, 160, 300, null, null, "코털진권 옥의!"],
@@ -646,7 +647,7 @@
[0, "뀌리", null, 0, null, 72, 78, 68, 0, 160, 300, null, null],
[0, "뷰티", null, 0, null, 77, 33, 90, 0, 160, 300, null, null],
[0, "카츠", null, 0, null, 62, 82, 50, 0, 160, 300, null, null],
[0, "노야마아즈사", null, 0, null, 82, 30, 72, 0, 160, 300, null, null],
[0, "노야마 아즈사", null, 0, null, 82, 30, 72, 0, 160, 300, null, null],
[0, "오가사와라유노스케", null, 0, null, 86, 39, 90, 0, 160, 300, null, null],
[0, "마츠오카 유이치", null, 0, null, 84, 36, 72, 0, 160, 300, null, null],
[0, "타카야나기 켄", null, 0, null, 82, 28, 80, 0, 160, 300, null, null],
@@ -672,11 +673,11 @@
[0, "큐어 파인", null, 0, null, 76, 82, 75, 0, 160, 300, null, null, "나 믿고 있어!"],
[0, "큐어 패션", null, 0, null, 79, 84, 83, 0, 160, 300, null, null, "붉은 하트는 행복의 증거, 갓 익은 프레시! 큐어 패션"],
[0, "큐어 해피", null, 0, null, 82, 82, 40, 0, 160, 300, null, null, "포기하지않을거야! 난 열심히 하겠다고 한번 마음먹은 일은 끝까지 해내고야마니까! 왜냐면 그게... 내 행복이니까!!"],
[0, "큐어 피스", null, 0, null, 84, 78, 14, 0, 160, 300, null, null, "그래, 난 겁쟁이 맞아. 하지만 물러날 수 없어! 왜냐하면 이 두 사람은 내가 여기 설 수있게 용기를 준 내 소중한 친구들이니까!! 이 두 사람을 괴롭히는것만은 절대 용서할수없어"],
[0, "큐어 피스", null, 0, null, 84, 78, 14, 0, 160, 300, null, null, "그래, 난 겁쟁이 맞아. 하지만 물러날 수 없어! 왜냐하면 이 두 사람은 내가 여기 설 수있게 용기를 준 내 소중한 친구들이니까!! 이 두 사람을 괴롭히는것만은 절대 용서할수없어!!!"],
[0, "큐어 써니", null, 0, null, 80, 80, 72, 0, 160, 300, null, null, "네 지금뭐라캤노? 내 친구를 욕하는 녀석은 내가 절대 가만안둔다!!"],
[0, "큐어 마치", null, 0, null, 83, 82, 75, 0, 160, 300, null, null, "우리 가족은 뿔뿔히 흩어지지않아! 우리 가족의 끈끈한 정은 영원히 사라지지않아! 너희들이 누군지 모르겠지만 만약 우리 가족 중 어느 한 사람이라도 다치게한다면 내가 용서못해! 너"],
[0, "큐어 뷰티", null, 0, null, 80, 76, 82, 0, 160, 300, null, null, "거기 당신들, 어디서 오신 누구신지 모르겠지만 지금 당장 돌아가주십시오. 저는 나나이로가오카(무지개) 학교 학생회 부회장을 맡고 있는 아오키 레이카(한설희) 입니다. 당신들이 교"],
[0, "유메노 코토", null, 0, null, 81, 39, 80, 0, 160, 300, null, null, "혼노~ 스코시~ 이지와루나~ 뎅와노 키리카타오~ 시타노~ 다앗테 큐우니 신켄~나오에데하나시 다스노~ 즈루이~ 스키타요토~ 이와세테아 게나이 다앗테 히카세테아게나이 후 와 리 시아"],
[0, "큐어 마치", null, 0, null, 83, 82, 75, 0, 160, 300, null, null, "우리 가족은 뿔뿔히 흩어지지않아! 우리 가족의 끈끈한 정은 영원히 사라지지않아! 너희들이 누군지 모르겠지만 만약 우리 가족 중 어느 한 사람이라도 다치게한다면 내가 용서못해! 너희들과 싸우겠어!"],
[0, "큐어 뷰티", null, 0, null, 80, 76, 82, 0, 160, 300, null, null, "거기 당신들, 어디서 오신 누구신지 모르겠지만 지금 당장 돌아가주십시오. 저는 나나이로가오카(무지개) 학교 학생회 부회장을 맡고 있는 아오키 레이카(한설희) 입니다. 당신들이 교내에서 저지른 난폭한 행동은 학생회 부회장으로서 봐줄수없습니다. 아니 이 아오키 레이카가 절대 용서하지않겠습니다!"],
[0, "유메노 코토", null, 0, null, 81, 39, 80, 0, 160, 300, null, null, "혼노~ 스코시~ 이지와루나~ 뎅와노 키리카타오~ 시타노~ 다앗테 큐우니 신켄~나오에데하나시 다스노~ 즈루이~ 스키타요토~ 이와세테아 게나이 다앗테 히카세테아게나이 후 와 리 시아와세노 하네~ 코코로노 우에니~ 마이오리테 키타"],
[0, "쿠로미", null, 0, null, 80, 49, 80, 0, 160, 300, null, null, "쿠로미 사건일지 XXX호!!"],
[0, "바쿠", null, 0, null, 55, 55, 50, 0, 160, 300, null, null],
[0, "요시이 아키히사", null, 0, null, 61, 81, 33, 0, 160, 300, null, null],
@@ -684,27 +685,27 @@
[0, "아이리스", null, 0, null, 83, 50, 86, 0, 160, 300, null, null, "어서오세요 도전자여. 기다리고 있엇습니다."],
[0, "세레나", null, 0, null, 85, 50, 82, 0, 160, 300, null, null],
[0, "시트론", null, 0, null, 79, 41, 92, 0, 160, 300, null, "발명", "과학이 세계를 지배한다!"],
[0, "난천", null, 0, null, 84, 53, 87, 0, 160, 300, null, null],
[0, "루자미네", null, 0, null, 80, 40, 92, 0, 160, 300, null, null],
[0, "난천", null, 0, null, 98, 53, 87, 0, 160, 300, null, null],
[0, "루자미네", null, 0, null, 85, 40, 92, 0, 160, 300, null, null],
[0, "릴리에", null, 0, null, 82, 64, 69, 0, 160, 300, null, null],
[0, "로로나", null, 0, null, 92, 89, 94, 0, 160, 300, null, null],
[0, "푸크푹", null, 0, null, 92, 79, 80, 0, 160, 300, null, null, "어서와 섀도게일 언니."],
[0, "로로나", null, 0, null, 92, 89, 94, 0, 160, 300, null, null, "와 토토리 짱이다!"],
[0, "푸크푹", null, 0, null, 104, 79, 80, 0, 160, 300, null, "환술", "어서와 섀도게일 언니."],
[0, "그림하트", null, 0, null, 77, 51, 49, 0, 160, 300, null, null, "목을 쳐라.."],
[0, "마왕팜", null, 0, null, 72, 94, 50, 0, 160, 300, null, null, "무지개…? 어째서…."],
[0, "피티 프레데리카", null, 0, null, 84, 33, 86, 0, 160, 300, null, null, "저같은건 믿을만한 상대가 아니랍니다 분명 불쾌한 기분이 들거에요."],
[0, "웨딘", null, 0, null, 86, 40, 85, 0, 160, 300, null, null, "마법나라잖아요! 꿈과 판타지잖아요! 당신들, 바보 아녜요? 어떻게 일이 잘 안 풀린다고 남까지 말려들게 해서 몽땅 죽여 버리겠다는 결론을 낼 수 있죠? 그런 걸 납득할 사람이 어"],
[0, "웨딘", null, 0, null, 86, 40, 85, 0, 160, 300, null, null, "마법나라잖아요! 꿈과 판타지잖아요! 당신들, 바보 아녜요? 어떻게 일이 잘 안 풀린다고 남까지 말려들게 해서 몽땅 죽여 버리겠다는 결론을 낼 수 있죠? 그런 걸 납득할 사람이 어디 있어요? 마법소녀잖아요. 누구보다도 상냥하고 누구보다 귀여워야 하는 마법소녀잖아요. 그런데 개차반들 밖에 없지 않나요? 프킨도 프레데리카도 '마법나라'도. 마법소녀의 팬 따위 되지 말 걸 그랬어요! 만화영화도 만화도 보지 말 걸 그랬어요! 전부 거짓말투성이야! 사실은 쓰레기뿐잖아요!"],
[0, "벨 크라넬", null, 0, null, 84, 85, 76, 0, 160, 300, null, null],
[0, "아이즈발렌슈타인", null, 0, null, 81, 90, 77, 0, 160, 300, null, null],
[0, "프레이 마이어", null, 0, null, 92, 101, 67, 0, 160, 300, null, "위압", "내기도….들어줬어… 내 친구야 내가 태어난 의미가 여기있어"],
[0, "앤 마이어", null, 0, null, 88, 90, 86, 0, 160, 300, null, null, "그야 당연하잖아… 난 여전히 프레이를 좋아해."],
[0, "프레이 마이어", null, 0, null, 100, 118, 70, 0, 160, 300, null, "위압", "내기도….들어줬어… 내 친구야 내가 태어난 의미가 여기있어"],
[0, "앤 마이어", null, 0, null, 96, 93, 86, 0, 160, 300, null, null, "그야 당연하잖아… 난 여전히 프레이를 좋아해."],
[0, "릴리루카 아데", null, 0, null, 77, 65, 81, 0, 160, 300, null, null],
[0, "에테몽키", null, 0, null, 78, 51, 91, 0, 160, 300, null, null, "인간이란 정말 대단해 타이치도 그렇지만 항상 계산이상의 무언가를 만들어내지. 녀석들의 배틀은 솔직히 즐거웠어."],
[0, "에테몬", null, 0, null, 65, 81, 55, 0, 160, 300, null, null, "사라지고 싶지 않아!! 난 이세상의 스타라고! 스타는 영원한 거란 말이야~~~~~~~~~~~~~~~~~~~~~~~~~~~~"],
[0, "후타바 안즈", null, 0, null, 72, 69, 71, 0, 160, 300, "유지", null],
[0, "에테몽키", null, 0, null, 83, 51, 91, 0, 160, 300, null, null, "인간이란 정말 대단해 타이치도 그렇지만 항상 계산이상의 무언가를 만들어내지. 녀석들의 배틀은 솔직히 즐거웠어."],
[0, "에테몬", null, 0, null, 79, 81, 55, 0, 160, 300, null, null, "사라지고 싶지 않아!! 난 이세상의 스타라고! 스타는 영원한 거란 말이야~~~~~~~~~~~~~~~~~~~~~~~~~~~~"],
[0, "마이멜로디", null, 0, null, 64, 33, 77, 0, 160, 300, null, null, "멜로디~마크 빙글빙글 셔플! 도와줘 카드야~"],
[0, "핀 다무네", null, 0, null, 89, 88, 84, 0, 160, 300, null, "통찰"],
[0, "레피야 비리다스", null, 0, null, 79, 84, 80, 0, 160, 300, null, null],
[0, "벨프 크로조", null, 0, null, 64, 67, 77, 0, 160, 300, null, null],
[0, "나이트런 레오", null, 0, null, 71, 89, 80, 0, 160, 300, null, null],
[0, "레오", null, 0, null, 71, 93, 80, 0, 160, 300, null, null],
[0, "사가 베르이만", null, 0, null, 84, 75, 81, 0, 160, 300, null, null],
[0, "슈가", null, 0, null, 65, 51, 71, 0, 160, 300, null, null],
[0, "솔트", null, 0, null, 75, 66, 79, 0, 160, 300, null, null],
@@ -723,28 +724,28 @@
[0, "키리시마 쇼우코", null, 0, null, 87, 73, 98, 0, 160, 300, null, null],
[0, "키노시타히데요시", null, 0, null, 79, 33, 57, 0, 160, 300, null, null],
[0, "키노시타 유우코", null, 0, null, 85, 67, 90, 0, 160, 300, null, null],
[0, "노하라 신노스케", null, 0, null, 69, 55, 69, 0, 160, 300, null, null, "날 흉내 낸다고 해서 훈이 네가 내가 될 수는 없는 거라고. 왜냐하면 나는 나고 너는 그냥 너이기 때문이야."],
[0, "노하라 신노스케", null, 0, null, 69, 55, 69, 0, 160, 300, null, null, "날 흉내 낸다고 해서 마사오 네가 내가 될 수는 없는 거라고. 왜냐하면 나는 나고 너는 그냥 너이기 때문이야."],
[0, "노하라 히로시", null, 0, null, 86, 71, 84, 0, 160, 300, null, null, "일은 대신할 수 있어도, 아버지는 대신할 수 없으니까/물론 이 세상은 평화롭지 않고 불합리하고 썩었어요, 하지만 그게 인간이죠!"],
[0, "노하라 마사에", null, 0, null, 85, 94, 80, 0, 160, 300, null, null, "부모란, 자식을 위해선 뭐든지 할 수 있는 사람들이에요."],
[0, "마미야 치아키", null, 0, null, 87, 79, 86, 0, 160, 300, null, null, "미래에서 기다릴게."],
[0, "콘노 마코토", null, 0, null, 84, 77, 89, 0, 160, 300, null, null],
[0, "콘노 마코토", null, 0, null, 84, 77, 89, 0, 160, 300, null, null, "응… 금방 뛰어갈게."],
[0, "요시야마 가즈코", null, 0, null, 85, 76, 89, 0, 160, 300, null, null, "현대인이든 미래인이든 소중한건 하나뿐.. 너는 내가 좋아하는 사람이라는 것이야. 진실은 그것뿐이야./기다릴께 이쪽에서 …"],
[0, "후카마치 가즈오", null, 0, null, 84, 81, 79, 0, 160, 300, null, null, "널 좋아해 라고 말한건 진실이야… 켄과 가즈오 둘사이에서 혼란이 오겟지만 기억속의 내가 말한건 사실이야… 그러니까 요시야마….!"],
[0, "이스칸다르", null, 0, null, 94, 97, 68, 0, 160, 300, null, null, "아아... 이번 원정도 가슴뛰는 것이었군..."],
[0, "길가메쉬", null, 0, null, 92, 115, 84, 0, 160, 300, null, null, "시공의 끝까지, 이 세계는 남김없이 나의 정원이다. 그렇기에 내가 보증하지. 세계는 결코 그대(そなた)를 질리게 하지 않을 것이다."],
[0, "길가메쉬", null, 0, null, 92, 100, 84, 0, 160, 300, null, null, "시공의 끝까지, 이 세계는 남김없이 나의 정원이다. 그렇기에 내가 보증하지. 세계는 결코 그대(そなた)를 질리게 하지 않을 것이다."],
[0, "아서 팬드리건", null, 0, null, 90, 96, 80, 0, 160, 300, null, null, "묻겠다, 그대가 나의 마스터인가?"],
[0, "니시무라 소이치", null, 0, null, 90, 115, 82, 0, 160, 300, null, null, "우리 교사는 모범을 보여야 하는 존재다. 그런데 도전하는 학생을 정면에서 받아 주지도 못하면서 무엇을 가르칠 수 있다는 거냐"],
[0, "타카이시 요코", null, 0, null, 85, 71, 110, 0, 160, 300, null, null],
[0, "니시무라 소이치", null, 0, null, 86, 97, 82, 0, 160, 300, null, null, "우리 교사는 모범을 보여야 하는 존재다. 그런데 도전하는 학생을 정면에서 받아 주지도 못하면서 무엇을 가르칠 수 있다는 거냐"],
[0, "타카이시 요코", null, 0, null, 85, 71, 100, 0, 160, 300, null, null],
[0, "하나무라 노조무", null, 0, null, 78, 59, 86, 0, 160, 300, null, "신중"],
[0, "하나무라 카렌", null, 0, null, 81, 41, 83, 0, 160, 300, null, null, "앞으로도 어쩌면 이번처럼 소중한 것을 잃어버리는 날이 올지도 몰라 아프고 아주 괴로운 일이 생길지도 몰라. 하지만 항상 린이 날 소중하게 여겨준다고 생각하면… 그 마음 만으로도 "],
[0, "하나무라 카렌", null, 0, null, 81, 41, 83, 0, 160, 300, null, null, "앞으로도 어쩌면 이번처럼 소중한 것을 잃어버리는 날이 올지도 몰라 아프고 아주 괴로운 일이 생길지도 몰라. 하지만 항상 린이 날 소중하게 여겨준다고 생각하면… 그 마음 만으로도 어떤 어려움이든 이겨낼수 있을거 같아"],
[0, "하나조노 아리스", null, 0, null, 84, 43, 71, 0, 160, 300, null, null],
[0, "카타리나클라에스", null, 0, null, 55, 49, 72, 0, 160, 300, null, null, ".에? ...뭐야, 그런 거였구나. 마력을 제어하지 못한다면 앞으로 할 수 있도록 노력하면 되잖아. 곧 있으면 마력의 가정교사도 온다고 하니까, 나랑 같이 마력의 훈련을 하면 되"],
[0, "카타리나클라에스", null, 0, null, 77, 49, 80, 0, 160, 300, null, null, ".에? ...뭐야, 그런 거였구나. 마력을 제어하지 못한다면 앞으로 할 수 있도록 노력하면 되잖아. 곧 있으면 마력의 가정교사도 온다고 하니까, 나랑 같이 마력의 훈련을 하면 되잖아"],
[0, "키스 클라에스", null, 0, null, 83, 78, 75, 0, 160, 300, null, null],
[0, "디올트 스티아트", null, 0, null, 88, 87, 83, 0, 160, 300, null, null],
[0, "앨런 스티아트", null, 0, null, 81, 80, 76, 0, 160, 300, null, null],
[0, "디올트 스티아트", null, 0, null, 96, 87, 83, 0, 160, 300, null, null],
[0, "앨런 스티아트", null, 0, null, 90, 80, 76, 0, 160, 300, null, null],
[0, "소피아이스카르트", null, 0, null, 73, 22, 91, 0, 160, 300, null, null, "나도 고마워… 앞으로는 소피아로써 네 곁에 있을태니까… 안녕…잘가 내 소중한 친구…"],
[0, "후지무라 리츠카", null, 0, null, 87, 67, 90, 0, 160, 300, null, null],
[0, "블러디", null, 0, null, 72, 89, 90, 0, 160, 300, null, null],
[0, "블러디", null, 0, null, 72, 89, 90, 0, 160, 300, null, null, "소중한 것들은 그걸 잃어버리고 난 뒤에야 그 가치를 깨닫게 되지…"],
[0, "유키무라 아오이", null, 0, null, 67, 31, 57, 0, 160, 300, null, null],
[0, "쿠라우에 히나타", null, 0, null, 75, 55, 94, 0, 160, 300, null, null],
[0, "사에토 카에데", null, 0, null, 86, 44, 71, 0, 160, 300, null, null],
@@ -753,21 +754,29 @@
[0, "사이렌지 하루나", null, 0, null, 84, 51, 88, 0, 160, 300, null, null],
[0, "라라 데빌룩", null, 0, null, 80, 55, 72, 0, 160, 300, null, null],
[0, "유우키 미캉", null, 0, null, 79, 67, 79, 0, 160, 300, null, null],
[0, "타마키 이로하", null, 0, null, 86, 73, 70, 0, 160, 300, null, null],
[0, "타마키 우이", null, 0, null, 75, 73, 80, 0, 160, 300, null, null],
[0, "타마키 이로하", null, 0, null, 86, 73, 70, 0, 160, 300, null, null, "\"내가 힘이 될 일이 있다면 언제든 말해줘! 근데... 허울좋은 소리로 들릴지도 모르겠지만…"],
[0, "타마키 우이", null, 0, null, 75, 73, 80, 0, 160, 300, null, null, "마법소녀의 운명을 바꾸고 싶으면 카미하마시로 와, 카미하마시에서 마법소녀는 구원받을수 있어."],
[0, "사토미 토우카", null, 0, null, 72, 71, 95, 0, 160, 300, null, null],
[0, "히아라기 네무", null, 0, null, 80, 77, 96, 0, 160, 300, null, null],
[0, "유나", null, 0, null, 91, 98, 78, 0, 160, 300, null, null],
[0, "피나", null, 0, null, 18, 44, 79, 0, 160, 300, null, null],
[0, "피나", null, 0, null, 18, 2, 79, 0, 160, 300, null, null],
[0, "호토리 타다세", null, 0, null, 73, 88, 80, 0, 160, 300, null, null, "꽃은 굉장하네.. 이런 조그마한 꽃이지만.. 열심히 피여 아주 조금이라도.. 누군가를 웃게 할 수있어.. 나도 그렇게 되고싶어."],
[0, "후지사키나가히코", null, 0, null, 70, 84, 81, 0, 160, 300, null, null, "비밀은.. 좋지 않을듯해.. 하지만.. 비밀로 하는게.. 좋을때도 있어.. 그게 상대방을 상처 입히지않고.. 끝난다면.. 마지막까지 하는 거짓말을 하는 괴로움과 마주 해야돼.."],
[0, "스폰지밥", null, 0, null, 46, 54, 17, 0, 160, 300, null, null, "그런 짓은 하지 말아야 했는데 난 그 사실을 몰랐어"],
[0, "뚱이", null, 0, null, 38, 60, 12, 0, 160, 300, null, null, "지식보다 더 소중한 건 우리의 우정이야. 난 바보가 좋아"],
[0, "징징이", null, 0, null, 57, 73, 50, 0, 160, 300, null, null, "다들 참 잘났네요. 당신들이 내 마지막 행복의 기회를 산산히 부숴버렸어요... 너무 잘게 부숴서 눈 씻고 찾아봐도, 흔적조차 찾을 수가 없다구요. 다들 훨씬 잘할 수 있을 거라고"],
[0, "스폰지밥", null, 0, null, 46, 54, 17, 0, 160, 300, null, null, "월요일 좋아~ 최고로 좋아~"],
[0, "뚱이", null, 0, null, 38, 60, 12, 0, 160, 300, null, null, "사랑해요~"],
[0, "징징이", null, 0, null, 57, 73, 50, 0, 160, 300, null, null, "다들 참 잘났네요. 당신들이 내 마지막 행복의 기회를 산산히 부숴버렸어요... 너무 잘게 부숴서 눈 씻고 찾아봐도, 흔적조차 찾을 수가 없다구요. 다들 훨씬 잘할 수 있을 거라고 생각했는데! 근데 내 생각이 잘못됐단 걸 깨닫게 되었죠... 내일 굳이 오지 않아도 돼요. 그냥 행진하다 사고로 다 죽었다고 얘기할 거니까요... 고마워요, 일을 이 지경으로 만들어 줘서!"],
[0, "집게사장", null, 0, null, 61, 67, 71, 0, 160, 300, null, "상재", "가를 배울 때 가장 중요한 건 학벌이 아니라 실력과 열정이야 일반 상식과 튼튼한 두 손만 있으면 돼."],
[0, "플랑크톤", null, 0, null, 60, 33, 81, 0, 160, 300, null, "발명"],
[0, "퐁퐁부인", null, 0, null, 71, 47, 84, 0, 160, 300, null, null],
[0, "사이타마", null, 0, null, 83, 105, 74, 0, 160, 300, null, "무쌍", "압도적인 힘이라는 건 참 시시한 거야."]
[0, "사이타마", null, 0, null, 83, 105, 74, 0, 160, 300, null, "무쌍", "압도적인 힘이라는 건 참 시시한 거야."],
[0, "쿠레하 유나", null, 0, null, 78, 89, 80, 0, 160, 300, null, null, "그래...내 마음 속에 숨구멍을 만들어 줬던 건 사쿠야... 그녀가 죽어서라도 내 행동을 막으려고 한다면, 그 유지를 따라서 다시 되돌릴 수 밖에 없는 걸까아..."],
[0, "Dr.히루루크", null, 0, null, 64, 30, 90, 0, 160, 300, null, "의술", "사람이 언제 죽는다고 생각하나···?\n심장이 총알에 뚫렸을 때···? ···아니.\n불치의 병에 걸렸을 때? ···아니.\n맹독 버섯 스프를 마셨을 때···? 아니야!!!\n···사람들에게서 잊혀졌을 때다···!!!"],
[0, "아냐 포저", null, 0, null, 89, 40, 89, 0, 160, 300, null, null, "와쿠 와쿠!"],
[0, "로이드 포저", null, 0, null, 93, 80, 100, 0, 160, 300, null, null, "예기치 않은 스텔라 획득이란 것도 큰 성과지만⋯. 왜 그럴까. 그보다도, 테러를 미연에 방지했을 때처럼⋯. 자랑스러운 기분이다"],
[0, "요르 포저", null, 0, null, 86, 102, 63, 0, 160, 300, null, "무쌍"],
[0, "프랭키 프랭클랜", null, 0, null, 78, 80, 80, 0, 160, 300, null, null],
[0, "실비아 셔우드", null, 0, null, 94, 77, 90, 0, 160, 300, null, null, "네 빈약한 경험치로 모든 일을 판단하지 마. 너도 첩보원 나부랭이라면 객관적인 사고로 움직여. 자잘한 정보 하나조차 사람의 목숨이 걸려 있다는 걸 잊지 마라"],
[0, "헨리 헨더슨", null, 0, null, 83, 55, 92, 0, 160, 300, null, null, "에⋯엘레강트⋯! 베리 엘레강트!(E⋯Elegant⋯! Very Elegant!) 저 가족은 대체 뭐지?!"]
],
"initialEvents":[
+1
View File
@@ -365,6 +365,7 @@
[
"month", 1000,
true,
["UpdateNationLevel"],
["ProvideNPCTroopLeader"]
],
[
+8 -8
View File
@@ -13,7 +13,7 @@ class GameUnitConst extends GameUnitConstBase
[],//성벽은 공격할 수 없다.
[self::T_FOOTMAN=>1.2],
['성벽입니다.','생성할 수 없습니다.'],
null, null
null, null, null
],
[
@@ -23,7 +23,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_ARCHER=>1.2, self::T_CAVALRY=>0.8, self::T_SIEGE=>1.2],
[self::T_ARCHER=>0.8, self::T_CAVALRY=>1.2, self::T_SIEGE=>0.8],
['표준적인 보병입니다.','보병은 방어특화이며,','상대가 회피하기 어렵습니다.'],
null, null
null, null, null
],
[
@@ -33,7 +33,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_CAVALRY=>1.2, self::T_FOOTMAN=>0.8, self::T_SIEGE=>1.2],
[self::T_CAVALRY=>0.8, self::T_FOOTMAN=>1.2, self::T_SIEGE=>0.8],
['표준적인 궁병입니다.','궁병은 회피특화입니다.'],
null, null
null, null, null
],
[
@@ -43,7 +43,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_FOOTMAN=>1.2, self::T_ARCHER=>0.8, self::T_SIEGE=>1.2],
[self::T_FOOTMAN=>0.8, self::T_ARCHER=>1.2, self::T_SIEGE=>0.8],
['표준적인 기병입니다.','기병은 공격특화입니다.'],
null, null
null, null, null
],
[
@@ -53,7 +53,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_SIEGE=>1.2],
[self::T_SIEGE=>0.8],
['계략을 사용하는 병종입니다.'],
null, null
null, null, null
],
[
1405, self::T_WIZARD, '남귀병',
@@ -62,7 +62,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_SIEGE=>1.2],
[self::T_SIEGE=>0.8],
['전투를 포기하고 계략에 몰두합니다.'],
null, null
null, null, null
],
[
@@ -72,7 +72,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_FOOTMAN=>0.8, self::T_ARCHER=>0.8, self::T_CAVALRY=>0.8, self::T_WIZARD=>0.8, self::T_CASTLE=>1.8],
[self::T_FOOTMAN=>1.2, self::T_ARCHER=>1.2, self::T_CAVALRY=>1.2, self::T_WIZARD=>1.2],
['높은 구조물 위에서 공격합니다.'],
['che_성벽부상무효'], null
['che_성벽부상무효'], null, ['che_성벽선제']
],
[
1501, self::T_SIEGE, '충차',
@@ -81,7 +81,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_FOOTMAN=>0.8, self::T_ARCHER=>0.8, self::T_CAVALRY=>0.8, self::T_WIZARD=>0.8, self::T_CASTLE=>2.4],
[self::T_FOOTMAN=>1.2, self::T_ARCHER=>1.2, self::T_CAVALRY=>1.2, self::T_WIZARD=>1.2],
['엄청난 위력으로 성벽을 부수어버립니다.'],
['che_성벽부상무효'], null
['che_성벽부상무효'], null, null
]
];
}
+22 -22
View File
@@ -14,7 +14,7 @@ class GameUnitConst extends GameUnitConstBase
[], //성벽은 공격할 수 없다.
[self::T_FOOTMAN => 1.2],
['성벽입니다.', '생성할 수 없습니다.'],
null, null
null, null, null
],
[
@@ -24,7 +24,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_ARCHER => 1.2, self::T_CAVALRY => 0.8, self::T_SIEGE => 1.2],
[self::T_ARCHER => 0.8, self::T_CAVALRY => 1.2, self::T_SIEGE => 0.8],
['표준적인 보병입니다.', '보병은 방어특화이며,', '상대가 회피하기 어렵습니다.'],
null, null
null, null, null
],
[
1200, self::T_ARCHER, '궁병',
@@ -33,7 +33,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_CAVALRY => 1.2, self::T_FOOTMAN => 0.8, self::T_SIEGE => 1.2],
[self::T_CAVALRY => 0.8, self::T_FOOTMAN => 1.2, self::T_SIEGE => 0.8],
['표준적인 궁병입니다.', '궁병은 선제사격을 하는 병종입니다.'],
null, ['che_선제사격시도', 'che_선제사격발동']
null, ['che_선제사격시도', 'che_선제사격발동'], null
],
[
1300, self::T_CAVALRY, '기병',
@@ -42,7 +42,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_FOOTMAN => 1.2, self::T_ARCHER => 0.8, self::T_SIEGE => 1.2],
[self::T_FOOTMAN => 0.8, self::T_ARCHER => 1.2, self::T_SIEGE => 0.8],
['표준적인 기병입니다.', '기병은 공격특화입니다.'],
null, null
null, null, null
],
[
1104, self::T_FOOTMAN, '근위병',
@@ -51,7 +51,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_ARCHER => 1.2, self::T_CAVALRY => 0.8, self::T_SIEGE => 1.2],
[self::T_ARCHER => 0.8, self::T_CAVALRY => 1.2, self::T_SIEGE => 0.8],
['최강의 보병입니다.'],
null, ['che_방어력증가5p']
null, ['che_방어력증가5p'], null
],
[
1106, self::T_FOOTMAN, '백이병',
@@ -60,7 +60,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_ARCHER => 1.1, self::T_CAVALRY => 0.9, self::T_SIEGE => 1.1],
[self::T_ARCHER => 0.9, self::T_CAVALRY => 1.1, self::T_SIEGE => 0.9],
['정예 보병입니다. 불리한 싸움도 버텨냅니다.'],
null, ['che_방어력증가5p']
null, ['che_방어력증가5p'], null
],
[
97201, self::T_ARCHER, '화랑',
@@ -69,7 +69,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_CAVALRY => 1.2, self::T_FOOTMAN => 0.9, self::T_SIEGE => 1.2],
[self::T_CAVALRY => 0.8, self::T_FOOTMAN => 1.1, self::T_SIEGE => 0.8],
['특수한 궁병입니다.'],
null, ['che_선제사격시도', 'che_선제사격발동']
null, ['che_선제사격시도', 'che_선제사격발동'], null
],
[
1204, self::T_ARCHER, '석궁병',
@@ -78,7 +78,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_CAVALRY => 1.2, self::T_FOOTMAN => 0.8, self::T_SIEGE => 1.2],
[self::T_CAVALRY => 0.8, self::T_FOOTMAN => 1.2, self::T_SIEGE => 0.8],
['강력한 화살을 쏩니다.'],
null, ['che_선제사격시도', 'che_선제사격발동']
null, ['che_선제사격시도', 'che_선제사격발동'], null
],
[
1303, self::T_CAVALRY, '돌격기병',
@@ -87,7 +87,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_FOOTMAN => 1.2, self::T_ARCHER => 0.8, self::T_SIEGE => 1.2],
[self::T_FOOTMAN => 0.8, self::T_ARCHER => 1.2, self::T_SIEGE => 0.8],
['저돌적으로 공격합니다.'],
null, ['che_기병병종전투']
null, ['che_기병병종전투'], null
],
[
1307, self::T_CAVALRY, '호표기병',
@@ -96,7 +96,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_FOOTMAN => 1.2, self::T_ARCHER => 0.8, self::T_SIEGE => 1.2],
[self::T_FOOTMAN => 0.8, self::T_ARCHER => 1.2, self::T_SIEGE => 0.8],
['정예 기병입니다.'],
null, ['che_기병병종전투']
null, ['che_기병병종전투'], null
],
[
1306, self::T_CAVALRY, '맹수병',
@@ -105,7 +105,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_FOOTMAN => 1.2, self::T_ARCHER => 0.8, self::T_SIEGE => 1.2],
[self::T_FOOTMAN => 0.8, self::T_ARCHER => 1.2, self::T_SIEGE => 0.8],
['어느 누구보다 강력합니다.'],
null, ['che_기병병종전투']
null, ['che_기병병종전투'], null
],
[
1400, self::T_WIZARD, '귀병',
@@ -114,7 +114,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_SIEGE => 1.2],
[self::T_SIEGE => 0.8],
['계략을 사용하는 병종입니다.'],
null, null
null, null, null
],
[
1405, self::T_WIZARD, '남귀병',
@@ -123,7 +123,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_SIEGE => 1.2],
[self::T_SIEGE => 0.8],
['전투를 포기하고 계략에 몰두합니다.'],
null, null
null, null, null
],
[
1404, self::T_WIZARD, '악귀병',
@@ -132,7 +132,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_SIEGE => 1.2],
[self::T_SIEGE => 0.8],
['백병전에도 능숙합니다.'],
null, null
null, null, null
],
[
1407, self::T_WIZARD, '천귀병',
@@ -141,7 +141,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_SIEGE => 1.2],
[self::T_SIEGE => 0.8],
['갑주를 두른 귀병입니다.'],
null, null
null, null, null
],
[
1500, self::T_SIEGE, '정란',
@@ -150,7 +150,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_FOOTMAN => 1.25, self::T_ARCHER => 1.25, self::T_CAVALRY => 1.25, self::T_WIZARD => 1.25, self::T_CASTLE => 1.8, 1106 => 1.112],
[self::T_FOOTMAN => 1.2, self::T_ARCHER => 1.2, self::T_CAVALRY => 1.2, self::T_WIZARD => 1.2],
['높은 구조물 위에서 공격합니다.'],
['che_성벽부상무효'], ['che_선제사격시도', 'che_선제사격발동']
['che_성벽부상무효'], ['che_선제사격시도', 'che_선제사격발동'], null
],
[
1501, self::T_SIEGE, '충차',
@@ -159,7 +159,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_FOOTMAN => 0.8, self::T_ARCHER => 0.8, self::T_CAVALRY => 0.8, self::T_WIZARD => 0.8, self::T_CASTLE => 2.4],
[self::T_FOOTMAN => 1.2, self::T_ARCHER => 1.2, self::T_CAVALRY => 1.2, self::T_WIZARD => 1.2],
['엄청난 위력으로 성벽을 부수어버립니다.'],
['che_성벽부상무효'], null
['che_성벽부상무효'], null, null
],
[
1502, self::T_SIEGE, '벽력거',
@@ -168,7 +168,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_FOOTMAN => 1.25, self::T_ARCHER => 1.25, self::T_CAVALRY => 1.25, self::T_WIZARD => 1.25, self::T_CASTLE => 1.8, 1106 => 1.112],
[self::T_FOOTMAN => 0.833, self::T_ARCHER => 0.833, self::T_CAVALRY => 0.833, self::T_WIZARD => 0.833, 1106 => 0.909],
['상대에게 돌덩이를 날립니다.'],
['che_성벽부상무효'], ['che_선제사격시도', 'che_선제사격발동']
['che_성벽부상무효'], ['che_선제사격시도', 'che_선제사격발동'], null
],
[
97101, self::T_FOOTMAN, '중장보병',
@@ -177,7 +177,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_ARCHER => 1.2, self::T_CAVALRY => 0.8, self::T_SIEGE => 1.2],
[self::T_ARCHER => 0.8, self::T_CAVALRY => 1.2, self::T_SIEGE => 0.8],
['전천후 보병입니다.'],
null, ['che_방어력증가5p']
null, ['che_방어력증가5p'], null
],
[
1202, self::T_ARCHER, '연노병',
@@ -186,7 +186,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_CAVALRY => 1.2, self::T_FOOTMAN => 0.8, self::T_SIEGE => 1.2],
[self::T_CAVALRY => 0.8, self::T_FOOTMAN => 1.2, self::T_SIEGE => 0.8],
['화살을 연사합니다.'],
null, ['che_선제사격시도', 'che_선제사격발동']
null, ['che_선제사격시도', 'che_선제사격발동'], null
],
[
1304, self::T_CAVALRY, '철기병',
@@ -195,7 +195,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_FOOTMAN => 1.2, self::T_ARCHER => 0.8, self::T_SIEGE => 1.2],
[self::T_FOOTMAN => 0.8, self::T_ARCHER => 1.2, self::T_SIEGE => 0.8],
['철갑을 두른 기병입니다.'],
null, ['che_기병병종전투']
null, ['che_기병병종전투'], null
],
[
1401, self::T_WIZARD, '신귀병',
@@ -204,7 +204,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_SIEGE => 1.2],
[self::T_SIEGE => 0.8],
['신출귀몰한 귀병입니다.'],
null, null
null, null, null
],
];
}
+46 -46
View File
@@ -21,7 +21,7 @@ class GameUnitConst extends GameUnitConstBase
[],//성벽은 공격할 수 없다.
[self::T_FOOTMAN=>1.2],
['성벽입니다.','생성할 수 없습니다.'],
null, null
null, null, null
],
[
@@ -31,7 +31,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_ARCHER=>1.2, self::T_CAVALRY=>0.8, self::T_SIEGE=>1.2, 217502=>1],
[self::T_ARCHER=>0.8, self::T_CAVALRY=>1.2, self::T_SIEGE=>0.8, 217502=>1],
["표준적인 보병입니다. 보병은 방어특화입니다."],
null, ['che_방어력증가5p']
null, ['che_방어력증가5p'], null
],
[
217101, self::T_FOOTMAN, '마물병',
@@ -40,7 +40,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_ARCHER=>1.2, self::T_CAVALRY=>0.8, self::T_SIEGE=>1.2, 217502=>1],
[self::T_ARCHER=>0.8, self::T_CAVALRY=>1.2, self::T_SIEGE=>0.8, 217502=>1],
["마군 지역 기본병종입니다. 조금더 강하지만 쌀을 많이 소비합니다."],
null, ['che_방어력증가5p']
null, ['che_방어력증가5p'], null
],
[
217102, self::T_FOOTMAN, '중장보병',
@@ -49,7 +49,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_ARCHER=>1.2, self::T_CAVALRY=>0.8, self::T_SIEGE=>1.2, 217502=>1],
[self::T_ARCHER=>0.8, self::T_CAVALRY=>1.2, self::T_SIEGE=>0.8, 217502=>1],
["헬만 특유의 견고한 보병입니다."],
null, ['che_방어력증가5p']
null, ['che_방어력증가5p'], null
],
[
217103, self::T_FOOTMAN, '흑의 군 보병',
@@ -58,7 +58,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_ARCHER=>1.2, self::T_CAVALRY=>0.8, self::T_SIEGE=>1.2, 217502=>1],
[self::T_ARCHER=>0.8, self::T_CAVALRY=>1.2, self::T_SIEGE=>0.8, 217502=>1],
["흑의 군 전통의 체계적인 훈련으로 공격력을 보완했습니다."],
null, ['che_방어력증가5p']
null, ['che_방어력증가5p'], null
],
[
217104, self::T_FOOTMAN, '용병',
@@ -67,7 +67,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_ARCHER=>1.2, self::T_CAVALRY=>0.8, self::T_SIEGE=>1.2, 217502=>1],
[self::T_ARCHER=>0.8, self::T_CAVALRY=>1.2, self::T_SIEGE=>0.8, 217502=>1],
["일반 보병보다 비싸지만 받은 만큼은 일해줍니다."],
null, ['che_방어력증가5p']
null, ['che_방어력증가5p'], null
],
[
217105, self::T_FOOTMAN, '사메라이',
@@ -76,7 +76,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_ARCHER=>1.2, self::T_CAVALRY=>0.8, self::T_SIEGE=>1.2, 217502=>1],
[self::T_ARCHER=>0.8, self::T_CAVALRY=>1.2, self::T_SIEGE=>0.8, 217502=>1],
["검으로 난무를 가하는 몬스터 보병입니다."],
null, ['che_방어력증가5p']
null, ['che_방어력증가5p'], null
],
[
217106, self::T_FOOTMAN, '템플나이트병',
@@ -85,7 +85,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_ARCHER=>1.2, self::T_CAVALRY=>0.8, self::T_SIEGE=>1.2, 217502=>1],
[self::T_ARCHER=>0.8, self::T_CAVALRY=>1.2, self::T_SIEGE=>0.8, 217502=>1],
["공격을 포기하고 오직 방어에만 집중합니다."],
null, ['che_방어력증가5p']
null, ['che_방어력증가5p'], null
],
[
217107, self::T_FOOTMAN, '메이드병',
@@ -94,7 +94,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_ARCHER=>1.2, self::T_CAVALRY=>0.8, self::T_SIEGE=>1.2, 217502=>1],
[self::T_ARCHER=>0.8, self::T_CAVALRY=>1.2, self::T_SIEGE=>0.8, 217502=>1],
["켓셀링크 휘하의 날렵한 전투메이드입니다."],
null, ['che_방어력증가5p']
null, ['che_방어력증가5p'], null
],
[
217108, self::T_FOOTMAN, '요괴병',
@@ -103,7 +103,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_ARCHER=>1.2, self::T_CAVALRY=>0.8, self::T_SIEGE=>1.2, 217502=>1],
[self::T_ARCHER=>0.8, self::T_CAVALRY=>1.2, self::T_SIEGE=>0.8, 217502=>1],
["단단한 육체를 믿고 적진을 돌파합니다."],
null, ['che_방어력증가5p']
null, ['che_방어력증가5p'], null
],
[
217109, self::T_FOOTMAN, '리자스 친위병',
@@ -112,7 +112,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_ARCHER=>1.2, self::T_CAVALRY=>0.8, self::T_SIEGE=>1.2, 217502=>1],
[self::T_ARCHER=>0.8, self::T_CAVALRY=>1.2, self::T_SIEGE=>0.8, 217502=>1],
["여왕을 수호하는 리자스 최강의 보병 정예보병입니다."],
null, ['che_방어력증가5p']
null, ['che_방어력증가5p'], null
],
[
217110, self::T_FOOTMAN, '케이브리스 마물병',
@@ -121,7 +121,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_ARCHER=>1.2, self::T_CAVALRY=>0.8, self::T_SIEGE=>1.2, 217502=>1],
[self::T_ARCHER=>0.8, self::T_CAVALRY=>1.2, self::T_SIEGE=>0.8, 217502=>1],
["케이브리스 직속의 마물 정예 전투보병입니다."],
null, ['che_방어력증가5p']
null, ['che_방어력증가5p'], null
],
[
217111, self::T_FOOTMAN, '투신',
@@ -130,7 +130,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_ARCHER=>1.2, self::T_CAVALRY=>0.8, self::T_SIEGE=>1.2, 217502=>1],
[self::T_ARCHER=>0.8, self::T_CAVALRY=>1.2, self::T_SIEGE=>0.8, 217502=>1],
["성마교단 유적에서 발굴된 사상 최강의 보병입니다."],
null, ['che_방어력증가5p']
null, ['che_방어력증가5p'], null
],
[
@@ -140,7 +140,7 @@ class GameUnitConst extends GameUnitConstBase
[ self::T_FOOTMAN=>0.8, self::T_CAVALRY=>1.2, self::T_SIEGE=>1.2, 217502=>1],
[ self::T_FOOTMAN=>1.2, self::T_CAVALRY=>0.8, self::T_SIEGE=>0.8, 217502=>1],
["표준적인 궁병입니다. 궁병은 회피특화입니다."],
null, null
null, null, null
],
[
217201, self::T_ARCHER, '투척마물병',
@@ -149,7 +149,7 @@ class GameUnitConst extends GameUnitConstBase
[ self::T_FOOTMAN=>0.8, self::T_CAVALRY=>1.2, self::T_SIEGE=>1.2, 217502=>1],
[ self::T_FOOTMAN=>1.2, self::T_CAVALRY=>0.8, self::T_SIEGE=>0.8, 217502=>1],
["날렵함을 포기하고 도끼를 투척합니다."],
null, null
null, null, null
],
[
217202, self::T_ARCHER, '백의 군 궁병',
@@ -158,7 +158,7 @@ class GameUnitConst extends GameUnitConstBase
[ self::T_FOOTMAN=>0.8, self::T_CAVALRY=>1.2, self::T_SIEGE=>1.2, 217502=>1],
[ self::T_FOOTMAN=>1.2, self::T_CAVALRY=>0.8, self::T_SIEGE=>0.8, 217502=>1],
["전략을 활용하여 치고 빠지기에 능합니다."],
null, null
null, null, null
],
[
217203, self::T_ARCHER, '벌레술사병',
@@ -167,7 +167,7 @@ class GameUnitConst extends GameUnitConstBase
[ self::T_FOOTMAN=>0.8, self::T_CAVALRY=>1.2, self::T_SIEGE=>1.2, 217502=>1],
[ self::T_FOOTMAN=>1.2, self::T_CAVALRY=>0.8, self::T_SIEGE=>0.8, 217502=>1],
["벌레들을 부려 몸을 보호하는 궁병입니다."],
null, null
null, null, null
],
[
217204, self::T_ARCHER, '저격암살병',
@@ -176,7 +176,7 @@ class GameUnitConst extends GameUnitConstBase
[ self::T_FOOTMAN=>0.8, self::T_CAVALRY=>1.2, self::T_SIEGE=>1.2, 217502=>1],
[ self::T_FOOTMAN=>1.2, self::T_CAVALRY=>0.8, self::T_SIEGE=>0.8, 217502=>1],
["독을 바른 탄환으로 적의 목숨을 앗아갑니다."],
null, null
null, null, null
],
[
217205, self::T_ARCHER, '호루스병',
@@ -185,7 +185,7 @@ class GameUnitConst extends GameUnitConstBase
[ self::T_FOOTMAN=>0.8, self::T_CAVALRY=>1.2, self::T_SIEGE=>1.2, 217502=>1],
[ self::T_FOOTMAN=>1.2, self::T_CAVALRY=>0.8, self::T_SIEGE=>0.8, 217502=>1],
["민첩하게 파고드는 호루스족 궁병입니다."],
null, null
null, null, null
],
[
217206, self::T_ARCHER, '튤립병',
@@ -194,7 +194,7 @@ class GameUnitConst extends GameUnitConstBase
[ self::T_FOOTMAN=>0.8, self::T_CAVALRY=>1.2, self::T_SIEGE=>1.2, 217502=>1],
[ self::T_FOOTMAN=>1.2, self::T_CAVALRY=>0.8, self::T_SIEGE=>0.8, 217502=>1],
["최첨단병기 튤립으로 적을 폭격합니다."],
null, null
null, null, null
],
[
217207, self::T_ARCHER, '아이스플레임 궁병',
@@ -203,7 +203,7 @@ class GameUnitConst extends GameUnitConstBase
[ self::T_FOOTMAN=>0.8, self::T_CAVALRY=>1.2, self::T_SIEGE=>1.2, 217502=>1],
[ self::T_FOOTMAN=>1.2, self::T_CAVALRY=>0.8, self::T_SIEGE=>0.8, 217502=>1],
["혁명을 성공으로 이끈 정예 게릴라 궁병입니다."],
null, null
null, null, null
],
[
217208, self::T_ARCHER, '카라 궁병',
@@ -212,7 +212,7 @@ class GameUnitConst extends GameUnitConstBase
[ self::T_FOOTMAN=>0.8, self::T_CAVALRY=>1.2, self::T_SIEGE=>1.2, 217502=>1],
[ self::T_FOOTMAN=>1.2, self::T_CAVALRY=>0.8, self::T_SIEGE=>0.8, 217502=>1],
["선조의 힘을 이어받은 최강의 카라 정예 궁병입니다."],
null, null
null, null, null
],
[
@@ -222,7 +222,7 @@ class GameUnitConst extends GameUnitConstBase
[ self::T_FOOTMAN=>1.2, self::T_ARCHER=>0.8, self::T_SIEGE=>1.2, 217502=>1],
[ self::T_FOOTMAN=>0.8, self::T_ARCHER=>1.2, self::T_SIEGE=>0.8, 217502=>1],
["표준적인 기동병입니다. 기동병은 공격특화입니다."],
null, ['che_기병병종전투']
null, ['che_기병병종전투'], null
],
[
217301, self::T_CAVALRY, '하치온나',
@@ -231,7 +231,7 @@ class GameUnitConst extends GameUnitConstBase
[ self::T_FOOTMAN=>1.2, self::T_ARCHER=>0.8, self::T_SIEGE=>1.2, 217502=>1],
[ self::T_FOOTMAN=>0.8, self::T_ARCHER=>1.2, self::T_SIEGE=>0.8, 217502=>1],
["작은 몸으로 회피하며 따끔한 일격을 먹이는 몬스터 기동병입니다."],
null, ['che_기병병종전투']
null, ['che_기병병종전투'], null
],
[
217302, self::T_CAVALRY, '적의 군 기동병',
@@ -240,7 +240,7 @@ class GameUnitConst extends GameUnitConstBase
[ self::T_FOOTMAN=>1.2, self::T_ARCHER=>0.8, self::T_SIEGE=>1.2, 217502=>1],
[ self::T_FOOTMAN=>0.8, self::T_ARCHER=>1.2, self::T_SIEGE=>0.8, 217502=>1],
["리자스군의 자랑인 기동부대입니다."],
null, ['che_기병병종전투']
null, ['che_기병병종전투'], null
],
[
217303, self::T_CAVALRY, '안드로이드 기동병',
@@ -249,7 +249,7 @@ class GameUnitConst extends GameUnitConstBase
[ self::T_FOOTMAN=>1.2, self::T_ARCHER=>0.8, self::T_SIEGE=>1.2, 217502=>1],
[ self::T_FOOTMAN=>0.8, self::T_ARCHER=>1.2, self::T_SIEGE=>0.8, 217502=>1],
["파이아르가 개발한 최첨단 안드로이드 기동병입니다."],
null, ['che_기병병종전투']
null, ['che_기병병종전투'], null
],
[
217304, self::T_CAVALRY, '파란쵸 기동병',
@@ -258,7 +258,7 @@ class GameUnitConst extends GameUnitConstBase
[ self::T_FOOTMAN=>1.2, self::T_ARCHER=>0.8, self::T_SIEGE=>1.2, 217502=>1],
[ self::T_FOOTMAN=>0.8, self::T_ARCHER=>1.2, self::T_SIEGE=>0.8, 217502=>1],
["적진을 일점돌파하는 파란쵸왕국의 돌격 기동병입니다. "],
null, ['che_기병병종전투']
null, ['che_기병병종전투'], null
],
[
217305, self::T_CAVALRY, '비행마물병',
@@ -267,7 +267,7 @@ class GameUnitConst extends GameUnitConstBase
[ self::T_FOOTMAN=>1.2, self::T_ARCHER=>0.8, self::T_SIEGE=>1.2, 217502=>1],
[ self::T_FOOTMAN=>0.8, self::T_ARCHER=>1.2, self::T_SIEGE=>0.8, 217502=>1],
["비행마물에 올라탄 기동마물병입니다."],
null, ['che_기병병종전투']
null, ['che_기병병종전투'], null
],
[
217306, self::T_CAVALRY, '마물조련사병',
@@ -276,7 +276,7 @@ class GameUnitConst extends GameUnitConstBase
[ self::T_FOOTMAN=>1.2, self::T_ARCHER=>0.8, self::T_SIEGE=>1.2, 217502=>1],
[ self::T_FOOTMAN=>0.8, self::T_ARCHER=>1.2, self::T_SIEGE=>0.8, 217502=>1],
["무시무시한 몬스터에 올라타서 싸우는 극강의 기동병입니다."],
null, ['che_기병병종전투']
null, ['che_기병병종전투'], null
],
[
217307, self::T_CAVALRY, '기마병',
@@ -285,7 +285,7 @@ class GameUnitConst extends GameUnitConstBase
[ self::T_FOOTMAN=>1.2, self::T_ARCHER=>0.8, self::T_SIEGE=>1.2, 217502=>1],
[ self::T_FOOTMAN=>0.8, self::T_ARCHER=>1.2, self::T_SIEGE=>0.8, 217502=>1],
["JAPAN 특유의 기마에 올라타 적을 짓밟는 기동병입니다."],
null, ['che_기병병종전투']
null, ['che_기병병종전투'], null
],
[
217308, self::T_CAVALRY, '엔젤나이트',
@@ -294,7 +294,7 @@ class GameUnitConst extends GameUnitConstBase
[ self::T_FOOTMAN=>1.2, self::T_ARCHER=>0.8, self::T_SIEGE=>1.2, 217502=>1],
[ self::T_FOOTMAN=>0.8, self::T_ARCHER=>1.2, self::T_SIEGE=>0.8, 217502=>1],
["신의 명령으로 파멸을 내리기 위해 강림했습니다."],
null, ['che_기병병종전투']
null, ['che_기병병종전투'], null
],
[
@@ -304,7 +304,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_SIEGE=>1.2],
[self::T_SIEGE=>0.8],
["마법을 사용하는 병종입니다."],
null, null
null, null, null
],
[
217401, self::T_WIZARD, '마물 마법병',
@@ -313,7 +313,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_SIEGE=>1.2],
[self::T_SIEGE=>0.8],
["마군 지역 기본병종입니다. 조금더 강하지만 금을 많이 소비합니다."],
null, null
null, null, null
],
[
217402, self::T_WIZARD, '카라 마법병',
@@ -322,7 +322,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_SIEGE=>1.2],
[self::T_SIEGE=>0.8],
["신속히 움직이며 카라의 저주로 적을 공격합니다."],
null, null
null, null, null
],
[
217403, self::T_WIZARD, '제스 마법병',
@@ -331,7 +331,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_SIEGE=>1.2],
[self::T_SIEGE=>0.8],
["마법국가 제스의 전통있는 마법병입니다."],
null, null
null, null, null
],
[
217404, self::T_WIZARD, '무녀',
@@ -340,7 +340,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_SIEGE=>1.2],
[self::T_SIEGE=>0.8],
["신마법과 함께 신통한 춤으로 활력을 불어넣는 마법병입니다."],
null, null
null, null, null
],
[
217405, self::T_WIZARD, '프로즌',
@@ -349,7 +349,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_SIEGE=>1.2],
[self::T_SIEGE=>0.8],
["백병전에 불리한 연약한 몸으로 마법 사용에 집중하는 몬스터입니다."],
null, null
null, null, null
],
[
217406, self::T_WIZARD, '마소한 마법병',
@@ -358,7 +358,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_SIEGE=>1.2],
[self::T_SIEGE=>0.8],
["로브를 여러겹 둘러입은 마물마법병입니다. 직접 전투보단 마법에 집중합니다."],
null, null
null, null, null
],
[
217407, self::T_WIZARD, '신관병',
@@ -367,7 +367,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_SIEGE=>1.2],
[self::T_SIEGE=>0.8],
["AL교의 전투신관입니다. 몸을 보호하는 성스러운 마법을 사용합니다."],
null, null
null, null, null
],
[
217408, self::T_WIZARD, '중장마법병',
@@ -376,7 +376,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_SIEGE=>1.2],
[self::T_SIEGE=>0.8],
["두터운 로브를 입고 불길한 주문을 읊습니다."],
null, null
null, null, null
],
[
217409, self::T_WIZARD, '악마병',
@@ -385,7 +385,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_SIEGE=>1.2],
[self::T_SIEGE=>0.8],
["고대 제스 왕가의 계약에 따라 소환된 흑마법병입니다."],
null, null
null, null, null
],
[
217410, self::T_WIZARD, '호넷 마물병',
@@ -394,7 +394,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_SIEGE=>1.2],
[self::T_SIEGE=>0.8],
["호넷 직속의 최강 마물 마법병입니다."],
null, null
null, null, null
],
[
217411, self::T_WIZARD, 'Z가디언',
@@ -403,7 +403,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_SIEGE=>1.2],
[self::T_SIEGE=>0.8],
["제스의 기술력이 집약된 최고의 마법병기입니다."],
null, null
null, null, null
],
[
@@ -413,7 +413,7 @@ class GameUnitConst extends GameUnitConstBase
[ self::T_FOOTMAN=>0.8, self::T_ARCHER=>0.8, self::T_CAVALRY=>0.8, self::T_WIZARD=>0.8, self::T_CASTLE=>1.8],
[ self::T_FOOTMAN=>1.2, self::T_ARCHER=>1.2, self::T_CAVALRY=>1.2, self::T_WIZARD=>1.2],
["높은 구조물 위에서 공격합니다."],
['che_성벽부상무효'], null
['che_성벽부상무효'], null, null
],
[
217501, self::T_SIEGE, '충차',
@@ -422,7 +422,7 @@ class GameUnitConst extends GameUnitConstBase
[ self::T_FOOTMAN=>0.8, self::T_ARCHER=>0.8, self::T_CAVALRY=>0.8, self::T_WIZARD=>0.8, self::T_CASTLE=>2.4],
[ self::T_FOOTMAN=>1.2, self::T_ARCHER=>1.2, self::T_CAVALRY=>1.2, self::T_WIZARD=>1.2],
["엄청난 위력으로 성벽을 부수어버립니다."],
['che_성벽부상무효'], null
['che_성벽부상무효'], null, null
],
[
217502, self::T_SIEGE, '튤립3호',
@@ -431,7 +431,7 @@ class GameUnitConst extends GameUnitConstBase
[ ],
[ ],
["파괴적인 위력과 정말 파괴적인 비용을 자랑하는 전차입니다. "],
['che_성벽부상무효'], null
['che_성벽부상무효'], null, null
],
+10 -10
View File
@@ -13,7 +13,7 @@ class GameUnitConst extends GameUnitConstBase
[],//성벽은 공격할 수 없다.
[self::T_FOOTMAN=>1.2],
['성벽입니다.','생성할 수 없습니다.'],
null, null
null, null, null
],
[
1100, self::T_FOOTMAN, '보병',
@@ -22,7 +22,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_ARCHER=>1.2, self::T_CAVALRY=>0.8],
[self::T_ARCHER=>0.8, self::T_CAVALRY=>1.2],
['표준적인 보병입니다.','보병은 방어특화이며,','상대가 회피하기 어렵습니다.'],
null, null
null, null, null
],
[
@@ -32,7 +32,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_CAVALRY=>1.2, self::T_FOOTMAN=>0.8],
[self::T_CAVALRY=>0.8, self::T_FOOTMAN=>1.2],
['표준적인 궁병입니다.','궁병은 회피특화입니다.'],
null, null
null, null, null
],
[
@@ -42,7 +42,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_FOOTMAN=>1.2, self::T_ARCHER=>0.8],
[self::T_FOOTMAN=>0.8, self::T_ARCHER=>1.2],
['표준적인 기병입니다.','기병은 공격특화입니다.'],
null, null
null, null, null
],
[
@@ -52,7 +52,7 @@ class GameUnitConst extends GameUnitConstBase
[],
[],
['계략을 사용하는 병종입니다.'],
null, null
null, null, null
],
[
1405, self::T_WIZARD, '남귀병',
@@ -61,7 +61,7 @@ class GameUnitConst extends GameUnitConstBase
[],
[],
['전투를 포기하고 계략에 몰두합니다.'],
null, null
null, null, null
],
[
@@ -71,7 +71,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_CASTLE=>1.8],
[],
['높은 구조물 위에서 공격합니다.'],
['che_성벽부상무효'], null
['che_성벽부상무효'], null, null
],
[
1501, self::T_SIEGE, '충차',
@@ -80,7 +80,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_CASTLE=>2.4],
[],
['엄청난 위력으로 성벽을 부수어버립니다.'],
['che_성벽부상무효'], null
['che_성벽부상무효'], null, null
],
[
1502, self::T_SIEGE, '벽력거',
@@ -89,7 +89,7 @@ class GameUnitConst extends GameUnitConstBase
[self::T_CASTLE=>1.8],
[],
['상대에게 돌덩이를 날립니다.'],
['che_성벽부상무효'], null
['che_성벽부상무효'], null, null
],
[
1503, self::T_SIEGE, '목우',
@@ -98,7 +98,7 @@ class GameUnitConst extends GameUnitConstBase
[],
[],
['상대를 저지하는 특수병기입니다.'],
['che_성벽부상무효'], null
['che_성벽부상무효'], null, null
]
];
}
-6
View File
@@ -1,11 +1,5 @@
@charset 'utf-8';
.map_title_tooltiptext .tooltip-inner {
max-width: 220px;
width: 220px;
text-align: left;
}
.map_title_text {
margin: auto;
text-align: center;
+2 -2
View File
@@ -17,7 +17,7 @@
@include media-1000px{
#container {
#app {
width: 1000px;
margin: 0 auto;
}
@@ -28,7 +28,7 @@
overflow-x: hidden;
}
#container {
#app {
width: 500px;
margin: 0 auto;
}
+17 -11
View File
@@ -72,14 +72,9 @@
</BContainer>
</template>
<script lang="ts">
declare const queryValues: {
generalID: number | null;
};
</script>
<script lang="ts" setup>
import { BContainer, useToast, BFormSelect, BFormSelectOption, BButton } from "bootstrap-vue-next";
import { onMounted, provide, ref, watch } from "vue";
import { onMounted, provide, ref, toRef, watch } from "vue";
import { getGameConstStore, type GameConstStore } from "./GameConstStore";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
@@ -98,6 +93,10 @@ import { parseTime } from "./util/parseTime";
const toasts = unwrap(useToast());
const props = defineProps<{
generalID?: number;
}>();
const generalList = ref(new Map<number, GeneralListItemP1>());
const textMap = {
recent_war: [
@@ -118,7 +117,10 @@ const orderedInvGeneralKeyIndex = ref(new Map<number, number>());
const orderBy = ref<keyof typeof textMap>("turntime");
const targetGeneral = ref<GeneralListItemP1>();
const targetGeneralID = ref(queryValues.generalID ?? undefined);
const targetGeneralID = ref(props.generalID);
watch(()=>props.generalID, (generalID)=>{
targetGeneralID.value = generalID;
})
type GeneralLogs = {
[key in GeneralLogType]: Map<number, string>;
@@ -128,12 +130,12 @@ const targetGeneralLogs = ref<GeneralLogs>();
const nationInfo = ref<NationStaticItem>();
watch([generalList, targetGeneralID], async ([generalList, targetGeneralID]) => {
if (targetGeneralID === undefined) {
watch([generalList, targetGeneralID], async ([generalList, generalID]) => {
if (!generalID) {
targetGeneral.value = undefined;
return;
}
targetGeneral.value = generalList.get(targetGeneralID);
targetGeneral.value = generalList.get(generalID);
const logs: GeneralLogs = {
generalHistory: new Map(),
@@ -146,8 +148,12 @@ watch([generalList, targetGeneralID], async ([generalList, targetGeneralID]) =>
for (const reqType of ["generalHistory", "battleResult", "battleDetail", "generalAction"] as const) {
waiter.push(
SammoAPI.Nation.GetGeneralLog({ generalID: targetGeneralID, reqType }).then(
SammoAPI.Nation.GetGeneralLog({ generalID: generalID, reqType }).then(
(res) => {
if(res.generalID !== targetGeneralID.value){
return;
}
const rawLogs: [number, string][] = Object.entries(res.log).map(([key, value]) => [
Number(key),
formatLog(value),
+3 -3
View File
@@ -447,19 +447,19 @@ watch(refreshCounter, async () => {
title: "갱신 완료",
body: `동향 변경이 있습니다.`,
},
{ delay: 1000 * 3 }
{ delay: 100 * 15 }
);
} else {
toasts.success(
{
title: "갱신 완료",
},
{ delay: 1000 * 3 }
{ delay: 100 * 5 }
);
}
const lastVoteID = response.global.lastVoteID;
if (lastVoteID > lastVoteState.value) {
if (lastVoteID > lastVoteState.value && lastVoteID > (response.aux.myLastVote ?? 0)) {
lastVoteState.value = lastVoteID;
toasts.warning(
{
+10 -6
View File
@@ -29,7 +29,7 @@ import type {
GetFrontInfoResponse,
GetHistoryResponse,
GetMenuResponse,
GetRecentRecordResponse,
GetRecentRecordResponse,
} from "./defs/API/Global";
import type { CachedMapResult, CommandTableResponse, GeneralListResponse, ItemTypeKey, MapResult } from "./defs";
import type { VoteDetailResult, VoteListResult } from "./defs/API/Vote";
@@ -71,12 +71,12 @@ const apiRealPath = {
amount: number;
}>,
GetUniqueItemAuctionDetail: GET as APICallT<{
auctionID: number;
auctionID: number;
}, UniqueItemAuctionDetail>,
GetUniqueItemAuctionList: GET as APICallT<undefined, UniqueItemAuctionList>,
OpenUniqueAuction: POST as APICallT<{
itemID: string,
amount: number,
itemID: string,
amount: number,
}, OpenAuctionResponse>,
},
Betting: {
@@ -199,7 +199,11 @@ const apiRealPath = {
SendMessage: POST as APICallT<{
mailbox: number;
text: string;
}, ValidResponse & {msgID: number, msgType: MsgType}>
}, ValidResponse & { msgID: number, msgType: MsgType }>,
ReadLatestMessage: PATCH as APICallT<{
type: 'diplomacy' | 'private';
msgID: number;
}>
},
Misc: {
UploadImage: POST as APICallT<
@@ -273,7 +277,7 @@ const apiRealPath = {
troopID: number;
troopName: string;
}>,
GetNationInfo: GET as APICallT<{full?: boolean}, NationInfoResponse>,
GetNationInfo: GET as APICallT<{ full?: boolean }, NationInfoResponse>,
},
Troop: {
NewTroop: POST as APICallT<{
+1 -1
View File
@@ -37,7 +37,7 @@
<span style="color: cyan">NPC {{ createdNPCCnt.toLocaleString() }} </span>
</div>
<div class="s-border-t py-2 col col-6 col-md-4 subTournamentState">
<span v-if="frontInfo.global.tournamentType">
<span v-if="frontInfo.global.isTournamentActive">
<a v-if="tournamentStep.availableJoin" href="b_tournament.php" target="_blank">
<span style="color: cyan"
>{{ formatTournamentType(frontInfo.global.tournamentType) }}
+4 -3
View File
@@ -80,7 +80,7 @@ import type { GeneralListItem, GeneralListItemP1, GeneralListItemP2, GeneralList
import { getIconPath } from "@/util/getIconPath";
import { inject, ref, watch, type PropType, type Ref, type StyleValue } from "vue";
import { AgGridVue } from "ag-grid-vue3";
import type {
import {
Column,
CellClassParams,
CellStyle,
@@ -92,6 +92,7 @@ import type {
GridReadyEvent,
CellClickedEvent,
IRowNode,
NumberFilter,
} from "ag-grid-community";
import { ProvidedColumnGroup } from "ag-grid-community";
import { getNPCColor } from "@/utilGame";
@@ -483,7 +484,7 @@ const sortableNumber: Omit<GenColDef, "colId" | "headerName"> = {
sortable: true,
comparator: (a, b, _a, _b, _desc) => a - b,
sortingOrder: ["desc", "asc", null],
filter: "number",
filter: NumberFilter,
cellClass: rightAlignClass,
};
const defaultColDef = ref<ColDef>({
@@ -669,7 +670,7 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
return (_a.data?.dedlevel??0) - (_b.data?.dedlevel??0);
},
sortingOrder: ["desc", "asc", null],
filter: "text",
filter: true,
cellClass: centerCellClass,
columnGroupShow: "open",
},
+5 -8
View File
@@ -13,11 +13,9 @@
]"
>
<div
v-my-tooltip.hover.top="{
class: 'map_title_tooltiptext',
}"
v-b-tooltip.hover.top="titleTooltip"
:title="titleTooltip"
class="map_title"
:title="getTitleTooltip()"
>
<!-- eslint-disable-next-line vue/max-attributes-per-line -->
<span class="map_title_text" :style="{ color: getTitleColor() }"
@@ -165,12 +163,11 @@ export type CityPositionMap = {
</script>
<script lang="ts" setup>
import "@/../scss/map.scss";
import { type PropType, toRef, inject, type Ref, ref, watch, type ComponentPublicInstance } from "vue";
import { type PropType, toRef, inject, type Ref, ref, watch, type ComponentPublicInstance, computed } from "vue";
import { v4 as uuidv4 } from "uuid";
import { CURRENT_MAP_VERSION, type MapResult } from "@/defs";
import { joinYearMonth } from "@/util/joinYearMonth";
import { parseYearMonth } from "@/util/parseYearMonth";
import vMyTooltip from "@/directives/vMyTooltip";
import type { GameConstStore } from "@/GameConstStore";
import { unwrap_err } from "@/util/unwrap_err";
import { getMaxRelativeTechLevel, TECH_LEVEL_YEAR_GAP } from "@/utilGame/techLevel";
@@ -282,7 +279,7 @@ function getBeginGameLimitTooltip(): string | undefined {
return `초반제한 기간 : ${remainYear}${remainMonth > 0 ? ` ${remainMonth}개월` : ""} (${startYear + 3}년)`;
}
function getTitleTooltip(): string {
const titleTooltip = computed(()=>{
const result: string[] = [];
const beginLimit = getBeginGameLimitTooltip();
if (beginLimit) {
@@ -302,7 +299,7 @@ function getTitleTooltip(): string {
}
return result.join("<br>");
}
});
function getMapSeasonClassName(): string {
const { month } = mapData.value;
+30 -15
View File
@@ -255,21 +255,8 @@ const messageIndexedList: Record<MsgType, Ref<MsgItem[]>> = {
diplomacy: messageDiplomacy,
};
function generateLatestMsgState(msgType: MsgType) {
const storageKey = `state.${serverID}.latestReadMsg.${msgType}`;
const latestReadMsgID = parseInt(localStorage.getItem(storageKey) ?? "0");
const obj = ref<[string | undefined, number, number]>([undefined, latestReadMsgID, latestReadMsgID]);
watch(obj, ([, newMsgID], [, oldMsgID]) => {
if (newMsgID == oldMsgID) {
return;
}
localStorage.setItem(storageKey, newMsgID.toString());
});
return obj;
}
const latestPrivateMsgToastInfo = generateLatestMsgState("private");
const latestDiplomacyMsgToastInfo = generateLatestMsgState("diplomacy");
const latestPrivateMsgToastInfo = ref<[string | undefined, number, number]>([undefined, 0, 0]);
const latestDiplomacyMsgToastInfo = ref<[string | undefined, number, number]>([undefined, 0, 0]);
function readLatestMsg(msgType: MsgType) {
const targetMap = {
@@ -288,6 +275,13 @@ function readLatestMsg(msgType: MsgType) {
if (toastID) {
toasts.remove(toastID);
}
if(msgType == "private" || msgType == "diplomacy"){
void SammoAPI.Message.ReadLatestMessage({
type: msgType,
msgID: lastestReceivedID,
});
}
target.value = [undefined, lastestReceivedID, lastestReceivedID];
}
@@ -403,6 +397,27 @@ function updateMsgResponse(response: MsgResponse) {
return;
}
if(response.latestRead){
const newDiplomacyIdx = response.latestRead.diplomacy;
const [diplomacyToastID, diplomacyLatestMsgID] = latestDiplomacyMsgToastInfo.value;
if(diplomacyLatestMsgID < newDiplomacyIdx){
if(diplomacyToastID){
toasts.remove(diplomacyToastID);
}
latestDiplomacyMsgToastInfo.value = [undefined, newDiplomacyIdx, newDiplomacyIdx];
}
const newPrivateIdx = response.latestRead.private;
const [privateToastID, privateLatestMsgID] = latestPrivateMsgToastInfo.value;
if(privateLatestMsgID < newPrivateIdx){
if(privateToastID){
toasts.remove(privateToastID);
}
latestPrivateMsgToastInfo.value = [undefined, newPrivateIdx, newPrivateIdx];
}
}
lastSequence.value = Math.max(lastSequence.value, response.sequence);
for (const msgType of Object.keys(messageIndexedList) as (keyof typeof messageIndexedList)[]) {
+3
View File
@@ -221,6 +221,9 @@ export type GetFrontInfoResponse = {
} | null
>;
};
aux: {
myLastVote?: number;
};
};
+4
View File
@@ -58,6 +58,10 @@ export type MsgResponse = {
nationID: number;
generalName: string;
sequence: number;
latestRead?: {
private: number;
diplomacy: number;
}
};
export type MailboxItem = {
+1
View File
@@ -40,6 +40,7 @@ export type GeneralListItemP0 = {
autorun_limit: number;
nationID?: number;
city: number;
troop: number;
};
-97
View File
@@ -1,97 +0,0 @@
import type { Directive, DirectiveBinding } from 'vue'
import Tooltip from 'bootstrap/js/dist/tooltip'
function resolveTrigger(modifiers: DirectiveBinding['modifiers']): Tooltip.Options['trigger'] {
if (modifiers.manual) {
return 'manual'
}
const trigger: string[] = []
if (modifiers.click) {
trigger.push('click')
}
if (modifiers.hover) {
trigger.push('hover')
}
if (modifiers.focus) {
trigger.push('focus')
}
if (trigger.length > 0) {
return trigger.join(' ') as Tooltip.Options['trigger']
}
return 'hover focus'
}
function resolvePlacement(modifiers: DirectiveBinding['modifiers']): Tooltip.Options['placement'] {
if (modifiers.left) {
return 'left'
}
if (modifiers.right) {
return 'right'
}
if (modifiers.bottom) {
return 'bottom'
}
return 'top'
}
function resolveDelay(values: TooltipOptions): Tooltip.Options['delay'] {
if (values?.delay) {
return values.delay
}
return 0
}
type TooltipOptions = {
delay?: number,
class?: string
} | undefined;
const vMyTooltip: Directive<HTMLElement, TooltipOptions> = {
beforeMount(el, binding) {
el.setAttribute('data-bs-toogle', 'tooltip')
const isHtml = /<("[^"]*"|'[^']*'|[^'">])*>/.test(el.title)
const trigger = resolveTrigger(binding.modifiers)
const placement = resolvePlacement(binding.modifiers)
const delay = resolveDelay(binding.value)
const options: Partial<Tooltip.Options> = {
trigger,
placement,
delay,
html: isHtml,
}
const customClass = binding.value?.class;
if (customClass) {
options.customClass = customClass;
}
new Tooltip(el, options)
},
updated(el) {
const title = el.getAttribute('title')
if (title !== '') {
const instance = Tooltip.getInstance(el)
instance?.hide()
el.setAttribute('data-bs-original-title', title || '')
el.setAttribute('title', '')
}
},
unmounted(el) {
const instance = Tooltip.getInstance(el)
instance?.dispose()
},
}
export default vMyTooltip
+4 -2
View File
@@ -10,11 +10,13 @@ import { insertCustomCSS } from "./util/customCSS";
import { installVue3Components } from "./util/installVue3Components";
declare const query:{
generalID?: number;
};
auto500px();
htmlReady(() => {
insertCustomCSS();
});
installVue3Components(createApp(PageBattleCenter)).mount('#app');
installVue3Components(createApp(PageBattleCenter, query)).mount('#app');
-1
View File
@@ -1,7 +1,6 @@
import "@scss/board.scss";
import { createApp } from 'vue'
import PageBoard from '@/PageBoard.vue';
import BootstrapVueNext from 'bootstrap-vue-next'
import { setAxiosXMLHttpRequest } from '@util/setAxiosXMLHttpRequest';
import { auto500px } from "./util/auto500px";
import { insertCustomCSS } from "./util/customCSS";
+3 -4
View File
@@ -1,8 +1,6 @@
import '@scss/processing.scss';
import { unwrap } from "@util/unwrap";
import BootstrapVueNext from 'bootstrap-vue-next'
import Multiselect from 'vue-multiselect';
import { commandMap as GeneralActions } from "@/processing/General";
import { commandMap as NationActions } from '@/processing/Nation';
import { type App, createApp } from 'vue';
@@ -14,6 +12,7 @@ import { StoredActionsHelper } from './util/StoredActionsHelper';
import type { ReserveCommandResponse } from './defs/API/Command';
import { htmlReady } from './util/htmlReady';
import { insertCustomCSS } from './util/customCSS';
import { installVue3Components } from './util/installVue3Components';
declare const staticValues: {
serverNick: string,
@@ -100,13 +99,13 @@ if (app === undefined) {
console.error(`모듈이 지정되지 않음`, entryInfo);
}
else {
const div = unwrap(document.querySelector('#container'));
const div = unwrap(document.querySelector('#app'));
div.addEventListener('customSubmit', (e: Event) => {
const { detail } = e as unknown as CustomEvent<Args>;
void submitCommand(entryInfo[0] == 'Nation', turnList, entryInfo[1], detail);
}, true);
app.use(BootstrapVueNext).component('v-multiselect', Multiselect).mount('#container');
installVue3Components(app).mount('#app');
}
+1 -1
View File
@@ -27,7 +27,7 @@ $gen = Util::getReq('gen', 'int');
'mapName' => GameConst::$mapName,
'unitSet' => GameConst::$unitSet,
],
'queryValues' => [
'query' => [
'generalID' => $gen,
]
], false) ?>
+1 -1
View File
@@ -101,7 +101,7 @@ if (!$commandObj->hasPermissionToReserve()) {
</head>
<body class="img_back">
<div id="container"></div>
<div id="app"></div>
</body>
</html>
+2 -2
View File
@@ -137,8 +137,8 @@ foreach (ServConfig::getServerList() as $setting) {
</div>
<div id="bottom_box">
<div class="container"><a href="terms.2.html">개인정보처리방침</a> &amp; <a href="terms.1.html">이용약관</a><br>© 2022 • HideD
<br>크롬 파이어폭스에 최적화되어있습니다.
<div class="container"><a href="terms.2.html">개인정보처리방침</a> &amp; <a href="terms.1.html">이용약관</a><br>© 2023 • HideD
<br>크롬, 엣지, 파이어폭스에 최적화되어있습니다.
</div>
</div>
+1 -1
View File
@@ -193,7 +193,7 @@ class KakaoUtil
$refreshTokenValidUntil = TimeUtil::nowAddSeconds($refreshResult['refresh_token_expires_in']);
$oauthInfo['refreshToken'] = $refreshToken;
$oauthInfo['refresh_token_expires_in'] = $refreshTokenValidUntil;
$oauthInfo['refreshTokenValidUntil'] = $refreshTokenValidUntil;
}
RootDB::db()->update('member', [
+10 -10
View File
@@ -2,12 +2,6 @@
namespace sammo;
//NOTE: JavaScript 버전과 일치
const MAX_RNG_SUPPORT_BIT = 53;
if (PHP_INT_SIZE * 8 < MAX_RNG_SUPPORT_BIT) {
throw new \RangeException('PHP not support '.MAX_RNG_SUPPORT_BIT.' bit integer');
}
/**
* Reseed를 하지 않는 단순한 형태의 sha512 drbg
* float: bit를 강제로 채움
@@ -16,7 +10,9 @@ if (PHP_INT_SIZE * 8 < MAX_RNG_SUPPORT_BIT) {
class LiteHashDRBG implements RNG
{
const MAX_INT = (1 << MAX_RNG_SUPPORT_BIT) - 1;
//NOTE: JavaScript 버전과 일치
const MAX_RNG_SUPPORT_BIT = 53;
const MAX_INT = (1 << self::MAX_RNG_SUPPORT_BIT) - 1;
const BUFFER_BYTE_SIZE = 512 / 8; //SHA512
protected string $buffer;
@@ -182,7 +178,7 @@ class LiteHashDRBG implements RNG
public function nextInt(?int $max = null): int
{
if ($max === null || $max === self::MAX_INT) {
$buffer = $this->nextBits(MAX_RNG_SUPPORT_BIT) . "\x00";
$buffer = $this->nextBits(self::MAX_RNG_SUPPORT_BIT) . "\x00";
return self::parseU64($buffer);
}
@@ -207,9 +203,9 @@ class LiteHashDRBG implements RNG
public function nextFloat1(): float
{
$max = 1 << MAX_RNG_SUPPORT_BIT;
$max = 1 << self::MAX_RNG_SUPPORT_BIT;
while (true) {
$buffer = $this->nextBits(MAX_RNG_SUPPORT_BIT + 1) . "\x00";
$buffer = $this->nextBits(self::MAX_RNG_SUPPORT_BIT + 1) . "\x00";
$nInt = self::parseU64($buffer);
if ($nInt <= $max) {
break;
@@ -223,3 +219,7 @@ class LiteHashDRBG implements RNG
return new LiteHashDRBG($seed, $idx);
}
}
if (PHP_INT_SIZE * 8 < LiteHashDRBG::MAX_RNG_SUPPORT_BIT) {
throw new \RangeException('PHP not support '.LiteHashDRBG::MAX_RNG_SUPPORT_BIT.' bit integer');
}
+3 -2
View File
@@ -2,14 +2,15 @@
namespace sammo;
const MAX_RNG_SUPPORT_BIT = 53;
/**
* 내부에 랜덤 값을 호출하지 않을 것이 확실할 때 RNG 대용으로 사용
* 어떤 함수를 호출하든 에러 발생
*/
class NoRNG implements RNG
{
const MAX_INT = (1 << MAX_RNG_SUPPORT_BIT) - 1;
const MAX_RNG_SUPPORT_BIT = 53;
const MAX_INT = (1 << self::MAX_RNG_SUPPORT_BIT) - 1;
public function __construct()
{
}