Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
42310fa170 | ||
|
|
bfff1d9e2f | ||
|
|
2c98777b9f | ||
|
|
4a91260a31 | ||
|
|
076971bcae | ||
|
|
01fcc9bcab | ||
|
|
348877c7cd | ||
|
|
478f26e75a | ||
|
|
57cc558b9b | ||
|
|
20b1f8eff8 | ||
|
|
f0a7990404 | ||
|
|
27f53ff4a6 | ||
|
|
9ec5dfc8b1 | ||
|
|
e4c1e414c8 | ||
|
|
a391650472 | ||
|
|
3cfde2b40d | ||
|
|
8bf1ed33dc | ||
|
|
422b3be0c5 | ||
|
|
c34e44c4ec | ||
|
|
a2b20eca67 | ||
|
|
d41c316bfb | ||
|
|
ca1d8aab84 | ||
|
|
32e3dcff7c | ||
|
|
9a23470622 | ||
|
|
c37a186802 | ||
|
|
84c17e3b22 | ||
|
|
64e0fca124 | ||
|
|
5c6537fd7a | ||
|
|
764a82df00 | ||
|
|
e904911fd9 | ||
|
|
a7494f72f6 | ||
|
|
b6b53b028d | ||
|
|
1743ce714a | ||
|
|
656f0c5d11 | ||
|
|
d80aebe1f4 | ||
|
|
ad48bc1962 | ||
|
|
d72fbb7098 | ||
|
|
95d5eaa09b | ||
|
|
b997a6159e | ||
|
|
1cd8b7a450 | ||
|
|
c5a8eb4261 | ||
|
|
8891c29cbd | ||
|
|
2facdfa1dd | ||
|
|
65c9fa0c21 | ||
|
|
e53c85dbaf | ||
|
|
3a231e8e61 | ||
|
|
4dc7776387 | ||
|
|
5ca043e0f1 | ||
|
|
9ef052c814 | ||
|
|
6d1690beaa | ||
|
|
171656e5be | ||
|
|
385e7f4871 | ||
|
|
89c5485784 | ||
|
|
02d191ec28 | ||
|
|
47c3090f3a | ||
|
|
476dac1ac1 | ||
|
|
8ca0d0ced4 | ||
|
|
ab197489d7 | ||
|
|
99736d187b | ||
|
|
197c09bd0d | ||
|
|
6cc634ba1f | ||
|
|
4c89ad91d2 | ||
|
|
d66233bce0 | ||
|
|
b485e6927e | ||
|
|
74b9e8bd7b | ||
|
|
545742ecc0 | ||
|
|
d9f1f65352 | ||
|
|
0904d17a5f | ||
|
|
87171b4246 | ||
|
|
78f8b56203 | ||
|
|
c160c5a555 | ||
|
|
31c7bf5348 | ||
|
|
6eadf718fc | ||
|
|
effced24d1 | ||
|
|
09f399db9a | ||
|
|
2b73f6faea | ||
|
|
943662605e | ||
|
|
9a04962bd6 | ||
|
|
c371f6bbb5 | ||
|
|
8324e2e454 | ||
|
|
30307c1b56 | ||
|
|
f139d0d2a8 | ||
|
|
33843764a7 | ||
|
|
aca7d7c571 | ||
|
|
bff72f609c | ||
|
|
adb03b7420 | ||
|
|
2094e18b60 | ||
|
|
541d8ddd40 | ||
|
|
f996172218 | ||
|
|
f9ffc1c1f3 | ||
|
|
05085ad504 | ||
|
|
64aecaf28b | ||
|
|
7b3ef45300 | ||
|
|
bd25de930b | ||
|
|
15d43c9653 | ||
|
|
5b7eba61cc |
@@ -28,6 +28,7 @@ d_shared
|
||||
d_pic/*.jpg
|
||||
d_pic/*.gif
|
||||
d_pic/*.png
|
||||
d_pic/uploaded_image
|
||||
|
||||
d_setting/*.php
|
||||
*/d_setting/*.php
|
||||
|
||||
@@ -9,8 +9,10 @@
|
||||
* <code>mpm_event</code> 권장
|
||||
* PHP 7.2 이상
|
||||
* <code>php-fpm</code> 권장
|
||||
* php에서 curl을 실행가능해야 합니다.
|
||||
* MySQL 5.6 이상, 또는 MariaDB 10.0 이상
|
||||
* php에서 curl, pdo-sqlite을 실행가능해야 합니다. (기본값: 지원)
|
||||
* mysqlnd가 지원되어야합니다. (기본값: 지원)
|
||||
* MariaDB 10.2.1 이상
|
||||
* MySQL 5.6 이상도 가능하도록 할 예정입니다.
|
||||
* <code>git</code>
|
||||
* <code>curl</code>
|
||||
|
||||
@@ -47,7 +49,7 @@ Database 수는 로그인 관리 서버 1개, 내부 서버 7개로, 총 8개의
|
||||
|
||||
현재 카카오로그인 API KEY를 입력하는 작업이 설치과정에 추가되어있지 않습니다.
|
||||
|
||||
<code>f_install/templates/KakaoKey.orig.php</code> 를 <code>d_setting/KakaoKey.php</code>로 복사한 후 API 키를 입력하여야 합니다.
|
||||
설치 후 <code>d_setting/KakaoKey.php</code>에서 API키를 입력해야 합니다.
|
||||
|
||||
|
||||
## 라이선스
|
||||
|
||||
+6
-2
@@ -52,8 +52,8 @@ input::-webkit-input-placeholder {
|
||||
|
||||
}
|
||||
|
||||
.login-button{
|
||||
margin-top: 5px;
|
||||
.login_btn_group{
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.login-register{
|
||||
@@ -98,6 +98,10 @@ input::-webkit-input-placeholder {
|
||||
z-index:800;
|
||||
}
|
||||
|
||||
#login_card{
|
||||
z-index:801;
|
||||
}
|
||||
|
||||
#btn_kakao_login{
|
||||
cursor: pointer;
|
||||
height:46px;
|
||||
|
||||
Vendored
+2
-2
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Vendored
+3
-3
File diff suppressed because one or more lines are too long
@@ -0,0 +1,153 @@
|
||||
/* Forked from https://github.com/asiffermann/summernote-image-title */
|
||||
|
||||
(function (factory) {
|
||||
/* Global define */
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof module === 'object' && module.exports) {
|
||||
// Node/CommonJS
|
||||
module.exports = factory(require('jquery'));
|
||||
} else {
|
||||
// Browser globals
|
||||
factory(window.jQuery);
|
||||
}
|
||||
}(function ($) {
|
||||
$.extend(true, $.summernote.lang, {
|
||||
'en-US': {
|
||||
imageFlip: {
|
||||
edit: 'Flip image',
|
||||
flipLabel: 'Alternative Image'
|
||||
}
|
||||
},
|
||||
'ko-KR': {
|
||||
imageFlip: {
|
||||
edit: '이미지 전환',
|
||||
flipLabel: '대체 이미지( , 로 구분)'
|
||||
}
|
||||
},
|
||||
});
|
||||
$.extend($.summernote.options, {
|
||||
imageFlip: {
|
||||
icon: '<i class="note-icon-pencil"/>',
|
||||
tooltip: 'Image Flip'
|
||||
}
|
||||
});
|
||||
$.extend($.summernote.plugins, {
|
||||
'imageFlip': function (context) {
|
||||
var self = this;
|
||||
|
||||
var ui = $.summernote.ui;
|
||||
var $note = context.layoutInfo.note;
|
||||
var $editor = context.layoutInfo.editor;
|
||||
var $editable = context.layoutInfo.editable;
|
||||
var $toolbar = context.layoutInfo.toolbar;
|
||||
|
||||
if (typeof context.options.imageFlip === 'undefined') {
|
||||
context.options.imageFlip = {};
|
||||
}
|
||||
|
||||
var options = context.options;
|
||||
var lang = options.langInfo;
|
||||
|
||||
context.memo('button.imageFlip', function () {
|
||||
var button = ui.button({
|
||||
contents: ui.icon(options.icons.pencil),
|
||||
container: false,
|
||||
tooltip: lang.imageFlip.edit,
|
||||
click: function (e) {
|
||||
context.invoke('imageFlip.show');
|
||||
}
|
||||
});
|
||||
|
||||
return button.render();
|
||||
});
|
||||
|
||||
this.initialize = function () {
|
||||
var $container = options.dialogsInBody ? $(document.body) : $editor;
|
||||
|
||||
var body = '<div class="form-group">' +
|
||||
'<label>' + lang.imageFlip.flipLabel + '</label>' +
|
||||
'<input class="note-image-flip-text form-control" type="text" />' +
|
||||
'</div>';
|
||||
|
||||
var footer = '<button href="#" class="btn btn-primary note-image-flip-btn">' + lang.imageFlip.edit + '</button>';
|
||||
|
||||
this.$dialog = ui.dialog({
|
||||
title: lang.imageFlip.edit,
|
||||
body: body,
|
||||
footer: footer
|
||||
}).render().appendTo($container);
|
||||
};
|
||||
|
||||
this.destroy = function () {
|
||||
ui.hideDialog(this.$dialog);
|
||||
this.$dialog.remove();
|
||||
};
|
||||
|
||||
this.bindEnterKey = function ($input, $btn) {
|
||||
$input.on('keypress', function (event) {
|
||||
if (event.keyCode === 13) {
|
||||
$btn.trigger('click');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.show = function () {
|
||||
var $img = $($editable.data('target'));
|
||||
|
||||
var imgInfo = {
|
||||
imgDom: $img,
|
||||
flip: $img.data('flip'),
|
||||
};
|
||||
this.showLinkDialog(imgInfo).then(function (imgInfo) {
|
||||
ui.hideDialog(self.$dialog);
|
||||
var $img = imgInfo.imgDom;
|
||||
|
||||
if (imgInfo.flip) {
|
||||
$img.attr('data-flip', imgInfo.flip);
|
||||
}
|
||||
else {
|
||||
$img.removeData('flip');
|
||||
}
|
||||
|
||||
$note.val(context.invoke('code'));
|
||||
$note.change();
|
||||
});
|
||||
};
|
||||
|
||||
this.showLinkDialog = function (imgInfo) {
|
||||
return $.Deferred(function (deferred) {
|
||||
var $imageFlip = self.$dialog.find('.note-image-flip-text');
|
||||
var $editBtn = self.$dialog.find('.note-image-flip-btn');
|
||||
|
||||
ui.onDialogShown(self.$dialog, function () {
|
||||
context.triggerEvent('dialog.shown');
|
||||
|
||||
$editBtn.click(function (event) {
|
||||
event.preventDefault();
|
||||
deferred.resolve({
|
||||
imgDom: imgInfo.imgDom,
|
||||
flip: $imageFlip.val(),
|
||||
});
|
||||
});
|
||||
|
||||
$imageFlip.val(imgInfo.flip).trigger('focus');
|
||||
self.bindEnterKey($imageFlip, $editBtn);
|
||||
|
||||
});
|
||||
|
||||
ui.onDialogHidden(self.$dialog, function () {
|
||||
$editBtn.off('click');
|
||||
|
||||
if (deferred.state() === 'pending') {
|
||||
deferred.reject();
|
||||
}
|
||||
});
|
||||
|
||||
ui.showDialog(self.$dialog);
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
}));
|
||||
@@ -52,6 +52,7 @@ $rootDB->insert('member',[
|
||||
'oauth_type' => 'NONE',
|
||||
'id' => $username,
|
||||
'email' => null,
|
||||
'token_valid_until'=>'2999-01-01 00:00:00',
|
||||
'pw' => $finalPassword,
|
||||
'salt' => $userSalt,
|
||||
'grade'=> 6,
|
||||
|
||||
@@ -217,8 +217,8 @@ $result = Util::generateFileUsingSimpleTemplate(
|
||||
);
|
||||
|
||||
$result = Util::generateFileUsingSimpleTemplate(
|
||||
__dir__.'/templates/menu.orig.js',
|
||||
ROOT.'/d_shared/menu.js',
|
||||
__dir__.'/templates/menu.orig.json',
|
||||
ROOT.'/d_shared/menu.json',
|
||||
[],
|
||||
true
|
||||
);
|
||||
@@ -263,6 +263,14 @@ $result = Util::generateFileUsingSimpleTemplate(
|
||||
]
|
||||
);
|
||||
|
||||
Util::generateFileUsingSimpleTemplate(
|
||||
__dir__.'/templates/KakaoKey.orig.php',
|
||||
ROOT.'/d_setting/KakaoKey.php',
|
||||
[
|
||||
],
|
||||
true
|
||||
);
|
||||
|
||||
if ($result !== true) {
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
|
||||
@@ -20,16 +20,18 @@ ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
CREATE TABLE `member` (
|
||||
`NO` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
`oauth_id` BIGINT(20) NULL DEFAULT NULL,
|
||||
`oauth_type` ENUM('NONE','KAKAO') NOT NULL,
|
||||
`ID` VARCHAR(64) NOT NULL,
|
||||
`EMAIL` VARCHAR(64) NULL DEFAULT NULL,
|
||||
`oauth_type` ENUM('NONE','KAKAO') NOT NULL,
|
||||
`oauth_info` TEXT NOT NULL DEFAULT '{}',
|
||||
`token_valid_until` DATETIME NULL DEFAULT NULL,
|
||||
`PW` CHAR(128) NOT NULL,
|
||||
`salt` CHAR(16) NOT NULL,
|
||||
`third_use` INT(1) NOT NULL DEFAULT '0',
|
||||
`NAME` VARCHAR(64) NOT NULL,
|
||||
`PICTURE` VARCHAR(64) NULL DEFAULT 'default.jpg',
|
||||
`IMGSVR` INT(1) NULL DEFAULT '0',
|
||||
`acl` text NOT NULL DEFAULT '{}' COMMENT 'json',
|
||||
`acl` TEXT NOT NULL DEFAULT '{}' COMMENT 'json',
|
||||
`GRADE` INT(1) NULL DEFAULT '1',
|
||||
`REG_NUM` INT(3) NULL DEFAULT '0',
|
||||
`REG_DATE` DATETIME NOT NULL,
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
window.sammo_menu = [
|
||||
['/bbs/board', '삼모게시판', '_blank'],
|
||||
['/bbs/tip', '팁/강좌', '_blank'],
|
||||
['/bbs/news', '삼국 일보', '_blank'],
|
||||
['/bbs/history2', '개인 열전', '_blank'],
|
||||
['/bbs/history3', '국가 열전', '_blank'],
|
||||
['/bbs/patch', '패치 내역', '_blank']
|
||||
];
|
||||
@@ -0,0 +1,8 @@
|
||||
[
|
||||
["/bbs/board", "삼모게시판", "_blank"],
|
||||
["/bbs/tip", "팁/강좌", "_blank"],
|
||||
["/bbs/news", "삼국 일보", "_blank"],
|
||||
["/bbs/history2", "개인 열전", "_blank"],
|
||||
["/bbs/history3", "국가 열전", "_blank"],
|
||||
["/bbs/patch", "패치 내역", "_blank"]
|
||||
]
|
||||
@@ -179,7 +179,7 @@ if($showServers){
|
||||
if($nation['generals']){
|
||||
$generals = $db->query('SELECT `general_no`, `name`, `last_yearmonth` FROM ng_old_generals WHERE server_id=%s AND general_no IN %li', $serverID, $nation['generals']);
|
||||
|
||||
if(count($generals) != $nation['generals'] && $serverID == UniqueConst::$serverID){
|
||||
if(count($generals) != count($nation['generals']) && $serverID == UniqueConst::$serverID){
|
||||
$liveGenerals = $nation['generals'];
|
||||
foreach($generals as $general){
|
||||
if(in_array($general['general_no'], $nation['generals'])){
|
||||
|
||||
+1
-1
@@ -170,7 +170,7 @@ $max_refresh['name'] = '접속자 총합';
|
||||
$refresh_result = array_merge([$max_refresh], $db->query('SELECT `name`,refresh,`connect` FROM general ORDER BY refresh DESC LIMIT 5'));
|
||||
|
||||
foreach ($refresh_result as $i=>$user) {
|
||||
$w = round($user['refresh'] / $max_refresh['refresh'] * 100, 1);
|
||||
$w = round($user['refresh'] / max(1, $max_refresh['refresh']) * 100, 1);
|
||||
$w2 = round(100 - $w, 1);
|
||||
$color = getTrafficColor($w);
|
||||
?>
|
||||
|
||||
@@ -55,6 +55,7 @@ var editable = <?=($me['level']>=5?'true':'false')?>;
|
||||
<?=WebUtil::printJS('../e_lib/bootstrap.bundle.min.js')?>
|
||||
<?=WebUtil::printJS('../e_lib/summernote/summernote-bs4.min.js')?>
|
||||
<?=WebUtil::printJS('../e_lib/summernote/lang/summernote-ko-KR.js')?>
|
||||
<?=WebUtil::printJS('../e_lib/summernote/plugin/image-sammo/summernote-image-flip.js')?>
|
||||
<?=WebUtil::printJS('../d_shared/common_path.js')?>
|
||||
<?=WebUtil::printJS('js/common.js')?>
|
||||
<?=WebUtil::printJS('js/dipcenter.js')?>
|
||||
|
||||
+1
-1
@@ -177,7 +177,7 @@ for($i=0; $i < $nationcount; $i++) {
|
||||
";
|
||||
}
|
||||
?>
|
||||
<tr><td colspan=<?=$nationcount+1?> align=center>불가침 : <font color=limegreen>@</font>, 통합 : <font color=cyan>○</font>, 합병 : <font color=skyblue>◎</font>, 통상 : ㆍ, 선포 : <font color=magenta>▲</font>, 교전 : <font color=red>★</font></td></tr>
|
||||
<tr><td colspan=<?=$nationcount+1?> align=center>불가침 : <font color=limegreen>@</font>, <!--통합 : <font color=cyan>○</font>, -->합병 : <font color=skyblue>◎</font>, 통상 : ㆍ, 선포 : <font color=magenta>▲</font>, 교전 : <font color=red>★</font></td></tr>
|
||||
</table>
|
||||
|
||||
<?php if ($realConflict) : ?>
|
||||
|
||||
@@ -150,12 +150,15 @@ if($meLevel >= 5 && $nation["l{$meLevel}set"] == 0) {
|
||||
echo "
|
||||
<select name=outlist size=1 style=color:white;background-color:black;>";
|
||||
|
||||
$query = "select no,name,level,leader,power,intel,killturn from general where nation='{$me['nation']}' and level!='12' and no!='{$me['no']}' order by npc,binary(name)";
|
||||
$query = "select no,npc,name,level,leader,power,intel,killturn from general where nation='{$me['nation']}' and level!='12' and no!='{$me['no']}' order by npc,binary(name)";
|
||||
$result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),"");
|
||||
$gencount = MYDB_num_rows($result);
|
||||
|
||||
for($i=0; $i < $gencount; $i++) {
|
||||
$general = MYDB_fetch_array($result);
|
||||
if($general['npc'] == 5){
|
||||
continue;
|
||||
}
|
||||
echo "
|
||||
<option value={$general['no']}>{$general['name']} <small>({$general['leader']}/{$general['power']}/{$general['intel']}, {$general['killturn']}턴)</small></option>";
|
||||
}
|
||||
|
||||
@@ -75,6 +75,12 @@ if($btn == "임명") {
|
||||
header('location:b_myBossInfo.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
//NPC 부대장 추방 불가
|
||||
if($general['npc'] == 5){
|
||||
header('location:b_myBossInfo.php');
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
//나와 대상 장수는 국가가 같아야 함
|
||||
|
||||
@@ -11,6 +11,12 @@
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
.map_title_tooltiptext .tooltip-inner{
|
||||
max-width:220px;
|
||||
width:220px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.map_title_text{
|
||||
margin:auto;
|
||||
text-align:center;
|
||||
|
||||
+8
-4
@@ -437,7 +437,7 @@ function commandTable() {
|
||||
$connect=$db->get();
|
||||
$userID = Session::getUserID();
|
||||
|
||||
$admin = $gameStor->getValues(['startyear', 'year', 'month', 'develcost', 'scenario']);
|
||||
$admin = $gameStor->getValues(['startyear', 'year', 'month', 'develcost', 'scenario', 'join_mode']);
|
||||
|
||||
$query = "select no,npc,troop,city,nation,level,crew,makelimit,special from general where owner='{$userID}'";
|
||||
$result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),"");
|
||||
@@ -554,7 +554,10 @@ function commandTable() {
|
||||
addCommand("인재탐색(랜덤경험, 자금$develcost)", 29, 0);
|
||||
}
|
||||
|
||||
if($me['level'] >= 1 && $city['supply'] != 0) {
|
||||
if($admin['join_mode'] == 'onlyRandom'){
|
||||
//do Nothing
|
||||
}
|
||||
else if($me['level'] >= 1 && $city['supply'] != 0) {
|
||||
addCommand("등용(자금{$develcost5}+장수가치)", 22);
|
||||
} else {
|
||||
addCommand("등용(자금{$develcost5}+장수가치)", 22, 0);
|
||||
@@ -709,7 +712,7 @@ function CoreCommandTable() {
|
||||
addCommand("몰수", 24, $valid);
|
||||
commandGroup("", 1);
|
||||
commandGroup("====== 외 교 ======");
|
||||
addCommand("통합 제의", 53, $valid);
|
||||
//addCommand("통합 제의", 53, $valid);
|
||||
|
||||
addCommand("항복 권고", 51, $valid);
|
||||
if($nation['level'] >= 2) {
|
||||
@@ -1252,8 +1255,9 @@ function increaseRefresh($type="", $cnt=1) {
|
||||
$db = DB::db();
|
||||
$gameStor = KVStorage::getStorage($db, 'game_env');
|
||||
$gameStor->refresh = $gameStor->refresh+$cnt; //TODO: +로 증가하는 값은 별도로 분리
|
||||
$isunited = $gameStor->isunited;
|
||||
|
||||
if($generalID && $userGrade < 6) {
|
||||
if($isunited != 2 && $generalID && $userGrade < 6) {
|
||||
$db->update('general', [
|
||||
'lastrefresh'=>$date,
|
||||
'con'=>$db->sqleval('con + %i', $cnt),
|
||||
|
||||
+13
-6
@@ -105,12 +105,19 @@ function getTurn(array $general, $type, $font=1) {
|
||||
case 25: //임관
|
||||
$double = $command[1];
|
||||
|
||||
$nation = getNationStaticInfo($double);
|
||||
if($double == 98){
|
||||
$nationName = '건국된 임의 국가';
|
||||
}
|
||||
else if($double == 99){
|
||||
$nationName = '임의의 국가';
|
||||
}
|
||||
else{
|
||||
$nationName = getNationStaticInfo($double)['name']??'?!?!';
|
||||
}
|
||||
|
||||
|
||||
if(!$nation['name']) { $nation['name'] = '????'; }
|
||||
|
||||
$josaRo = JosaUtil::pick($nation['name'], '로');
|
||||
$str[$i] = "【{$nation['name']}】{$josaRo} 임관";
|
||||
$josaRo = JosaUtil::pick($nationName, '로');
|
||||
$str[$i] = "【{$nationName}】{$josaRo} 임관";
|
||||
break;
|
||||
case 26: //집합
|
||||
$str[$i] = "집합";
|
||||
@@ -511,7 +518,7 @@ function processCommand($no) {
|
||||
case 27: process_27($general); break; //발령
|
||||
case 51: process_51($general); break; //항복권고
|
||||
case 52: process_52($general); break; //원조
|
||||
case 53: process_53($general); break; //통합제의
|
||||
//case 53: process_53($general); break; //통합제의
|
||||
case 61: process_61($general); break; //불가침제의
|
||||
case 62: process_62($general); break; //선전 포고
|
||||
case 63: process_63($general); break; //종전 제의
|
||||
|
||||
@@ -209,7 +209,7 @@ function getSpecialInfo(?int $type):?string{
|
||||
|
||||
60 => ['돌격', '[전투] 상대 회피 불가, 공격 시 전투 페이즈 +1, 공격 시 대미지 +10%'],
|
||||
61 => ['무쌍', '[전투] 대미지 +10%, 공격 시 필살 확률 +10%p'],
|
||||
62 => ['견고', '[전투] 상대 필살 불가, 상대 계략 시도시 성공 확률 -10%p'],
|
||||
62 => ['견고', '[전투] 상대 필살, 격노, 위압, 저격 불가, 상대 계략 시도시 성공 확률 -10%p, 부상 없음, 아군 피해 -5%'],
|
||||
63 => ['위압', '[전투] 훈련/사기≥90, 병력≥1,000 일 때 첫 페이즈 위압 발동(적 공격 불가)'],
|
||||
|
||||
70 => ['저격', '[전투] 전투 개시 시 1/3 확률로 저격 발동'],
|
||||
|
||||
+60
-14
@@ -229,7 +229,7 @@ function SetNationFront($nationNo) {
|
||||
|
||||
if($adj){
|
||||
$db->update('city', [
|
||||
'front'=>1
|
||||
'front'=>($enemyCities?1:2),
|
||||
], 'nation=%i and city in %li', $nationNo, array_keys($adj));
|
||||
}
|
||||
}
|
||||
@@ -562,11 +562,14 @@ function postUpdateMonthly() {
|
||||
$genCount = $genNum[$dip['me']];
|
||||
// 25% 참여율일때 두당 10턴에 4000명 소모한다고 계산
|
||||
// 4000 / 10 * 0.25 = 100
|
||||
$term = Util::round($dip['dead'] / 100 / $genCount) + 1;
|
||||
if($dip['term'] > $term) { $term = $dip['term']; }
|
||||
if($term > 13) { $term = 13; }
|
||||
$query = "update diplomacy set term='{$term}' where (me='{$dip['me']}' and you='{$dip['you']}')";
|
||||
MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),"");
|
||||
$term = floor($dip['dead'] / 100 / $genCount);
|
||||
$dip['dead'] -= $term * 100 * $genCount;
|
||||
$term = Util::valueFit($dip['term'] + $term, 0, 13);
|
||||
|
||||
$db->update('diplomacy', [
|
||||
'term' => $term,
|
||||
'dead'=> $dip['dead'],
|
||||
], 'me = %i AND you = %i', $dip['me'], $dip['you']);
|
||||
}
|
||||
|
||||
//개전국 로그
|
||||
@@ -609,7 +612,7 @@ function postUpdateMonthly() {
|
||||
}
|
||||
pushWorldHistory($history, $admin['year'], $admin['month']);
|
||||
//사상자 초기화
|
||||
$query = "update diplomacy set dead=0";
|
||||
$query = "update diplomacy set dead=0 WHERE state != 0";
|
||||
MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),"");
|
||||
//외교 기한-1
|
||||
$query = "update diplomacy set term=term-1 where term!=0";
|
||||
@@ -960,9 +963,9 @@ function updateNationState() {
|
||||
$connect=$db->get();
|
||||
|
||||
$history = array();
|
||||
$admin = $gameStor->getValues(['year', 'month']);
|
||||
$admin = $gameStor->getValues(['year', 'month', 'fiction', 'startyear', 'show_img_level', 'turnterm']);
|
||||
|
||||
$query = "select nation,name,level from nation";
|
||||
$query = "select nation,name,level,gennum,tech from nation";
|
||||
$nationresult = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),"");
|
||||
$nationcount = MYDB_num_rows($nationresult);
|
||||
|
||||
@@ -973,10 +976,6 @@ function updateNationState() {
|
||||
$cityresult = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),"");
|
||||
$citycount = MYDB_num_rows($cityresult);
|
||||
|
||||
$query = "select no from general where nation='{$nation['nation']}'";
|
||||
$genresult = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),"");
|
||||
$gencount = MYDB_num_rows($genresult);
|
||||
|
||||
if($citycount == 0) {
|
||||
$nationlevel = 0; // 방랑군
|
||||
} elseif($citycount == 1) {
|
||||
@@ -996,6 +995,7 @@ function updateNationState() {
|
||||
}
|
||||
|
||||
if($nationlevel > $nation['level']) {
|
||||
$oldLevel = $nation['level'];
|
||||
$nation['level'] = $nationlevel;
|
||||
|
||||
switch($nationlevel) {
|
||||
@@ -1020,16 +1020,62 @@ function updateNationState() {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
$lastAssemblerID = $gameStor->assembler_id??0;
|
||||
for($levelGen = max(1, $oldLevel) + 1; $levelGen <= $nationlevel; $levelGen+=1){
|
||||
if(in_array($levelGen, [3, 5, 7])){
|
||||
$genStep = 2;
|
||||
}
|
||||
else{
|
||||
$genStep = 1;
|
||||
}
|
||||
|
||||
while($genStep > 0){
|
||||
$lastAssemblerID += 1;
|
||||
$npcObj = new Scenario\NPC(
|
||||
999, '부대장'.$lastAssemblerID, null, $nation['nation'], null,
|
||||
10, 10, 10, 1, $admin['year'] - 15, $admin['year'] + 15, '은둔', '척사'
|
||||
);
|
||||
$npcObj->npc = 5;
|
||||
$npcObj->build($admin);
|
||||
$npcID = $npcObj->generalID;
|
||||
|
||||
$db->insert('troop', [
|
||||
'name'=>$npcObj->realName,
|
||||
'nation'=>$nation['nation'],
|
||||
'no'=>$npcID
|
||||
]);
|
||||
$troopID = $db->insertId();
|
||||
|
||||
$command = EncodeCommand(0, 0, 0, 26); //집합
|
||||
$db->update('general', [
|
||||
'troop'=>$troopID,
|
||||
'turn0'=>$command,
|
||||
'turn1'=>$command,
|
||||
'turn2'=>$command,
|
||||
'turn3'=>$command,
|
||||
'turn4'=>$command,
|
||||
'turn5'=>$command,
|
||||
'killturn'=>80
|
||||
], 'no=%i', $npcID);
|
||||
$genStep -= 1;
|
||||
}
|
||||
}
|
||||
$gameStor->assembler_id = $lastAssemblerID;
|
||||
|
||||
//작위 상승
|
||||
$query = "update nation set level='{$nation['level']}' where nation='{$nation['nation']}'";
|
||||
MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),"");
|
||||
|
||||
refreshNationStaticInfo();
|
||||
}
|
||||
|
||||
$gencount = $db->queryFirstField('SELECT count(*) FROM general WHERE nation=%i', $nation['nation']);
|
||||
|
||||
$gennum = $gencount;
|
||||
if($gencount < GameConst::$initialNationGenLimit) $gencount = GameConst::$initialNationGenLimit;
|
||||
//기술 및 변경횟수 업데이트
|
||||
$query = "update nation set tech=totaltech/'$gencount',gennum='$gennum' where nation='{$nation['nation']}'";
|
||||
$query = "update nation set totaltech=tech*'$gencount',gennum='$gennum' where nation='{$nation['nation']}'";
|
||||
MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),"");
|
||||
}
|
||||
pushWorldHistory($history, $admin['year'], $admin['month']);
|
||||
|
||||
+54
-10
@@ -192,10 +192,34 @@ function processAI($no) {
|
||||
$isStart = 0;
|
||||
}
|
||||
|
||||
$query = "select no,turn0,npcid,name,nation,nations,city,level,npcmsg,personal,leader,intel,power,gold,rice,crew,train,atmos,npc,affinity,mode,injury,picture,imgsvr,killturn,makelimit,dex0,dex10,dex20,dex30,dex40 from general where no='$no'";
|
||||
$query = "select no,turn0,npcid,name,nation,nations,city,level,npcmsg,personal,leader,intel,power,gold,rice,crew,train,atmos,troop,npc,affinity,mode,injury,picture,imgsvr,killturn,makelimit,dex0,dex10,dex20,dex30,dex40 from general where no='$no'";
|
||||
$result = MYDB_query($query, $connect) or Error("processAI01 ".MYDB_error($connect),"");
|
||||
$general = MYDB_fetch_array($result);
|
||||
|
||||
if($general['npc'] == 5){
|
||||
if($general['nation'] == 0 && $general['killturn'] > 1){
|
||||
$command = EncodeCommand(0, 0, 0, 0); //휴식
|
||||
$db->update('general', [
|
||||
'turn0'=>$command,
|
||||
'killturn'=>1
|
||||
], 'no=%i', $general['no']);
|
||||
}
|
||||
else{
|
||||
$command = EncodeCommand(0, 0, 0, 26); //집합
|
||||
$db->update('general', [
|
||||
'turn0'=>$command,
|
||||
'turn1'=>$command,
|
||||
'turn2'=>$command,
|
||||
'turn3'=>$command,
|
||||
'turn4'=>$command,
|
||||
'turn5'=>$command,
|
||||
'killturn'=>rand(70,75),
|
||||
], 'no=%i', $general['no']);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// 입력된 턴이 있으면 그것 실행
|
||||
if(DecodeCommand($general['turn0'])[0] != 0) {
|
||||
return;
|
||||
@@ -224,7 +248,7 @@ function processAI($no) {
|
||||
$coreCommand = MYDB_fetch_array($result);
|
||||
}
|
||||
|
||||
$cityCount = $db->queryFirstField('SELECT count(city) FROM city WHERE nation=%i AND supply=1 AND front=1', $general['nation']);
|
||||
$cityCount = $db->queryFirstField('SELECT count(city) FROM city WHERE nation=%i AND supply=1 AND front>0', $general['nation']);
|
||||
// 공격가능도시 있으면 1
|
||||
$attackable = $cityCount > 0;
|
||||
|
||||
@@ -467,7 +491,7 @@ function processAI($no) {
|
||||
|
||||
SetNationFront($nation['nation']);
|
||||
|
||||
$frontCount = $db->queryFirstField('SELECT count(city) FROM city WHERE nation=%i AND front=1', $general['nation']);
|
||||
$frontCount = $db->queryFirstField('SELECT count(city) FROM city WHERE nation=%i AND front>0', $general['nation']);
|
||||
if($frontCount > 0){
|
||||
break;
|
||||
}
|
||||
@@ -721,7 +745,7 @@ function processAI($no) {
|
||||
//전시일때
|
||||
if($general['gold'] + $general['rice'] < $resrc*2) { $command = EncodeCommand(0, 0, 0, 9); } //금쌀없으면 조달
|
||||
elseif($general['rice'] > $resrc && $city['rate'] < 95 && $city['front'] == 0) { $command = EncodeCommand(0, 0, 0, 4); } // 우선 선정
|
||||
elseif($general['rice'] > $resrc && $city['rate'] < 50 && $city['front'] == 1) { $command = EncodeCommand(0, 0, 0, 4); } // 우선 선정
|
||||
elseif($general['rice'] > $resrc && $city['rate'] < 50 && $city['front'] > 0) { $command = EncodeCommand(0, 0, 0, 4); } // 우선 선정
|
||||
elseif($general['gold'] < $resrc || ($general['gold'] < $resrc *2 && $general['rice'] > $resrc * 6)) { // 금없으면 쌀팜
|
||||
$amount = intdiv(($general['rice'] - $general['gold'])/2, 100); // 100단위
|
||||
if($amount > 0) { $command = EncodeCommand(0, 1, $amount, 49); }// 팜
|
||||
@@ -803,9 +827,9 @@ function processAI($no) {
|
||||
if(count($target) == 0) {
|
||||
//전방 도시 선택, 30% 확률로 태수 있는 전방으로 워프
|
||||
if(rand()%100 < 30) {
|
||||
$query = "select city from city where nation='{$general['nation']}' and supply='1' and front=1 order by gen1 desc,rand() limit 0,1";
|
||||
$query = "select city from city where nation='{$general['nation']}' and supply='1' and front>0 order by gen1 desc,rand() limit 0,1";
|
||||
} else {
|
||||
$query = "select city from city where nation='{$general['nation']}' and supply='1' and front=1 order by rand() limit 0,1";
|
||||
$query = "select city from city where nation='{$general['nation']}' and supply='1' and front>0 order by rand() limit 0,1";
|
||||
}
|
||||
$result = MYDB_query($query, $connect) or Error("processAI10 ".MYDB_error($connect),"");
|
||||
$cityCount = MYDB_num_rows($result);
|
||||
@@ -913,7 +937,7 @@ function NPCStaffWork($general, $nation, $dipState){
|
||||
|
||||
$commandList = [];
|
||||
|
||||
foreach($db->query('SELECT `no`, nation, city, npc, `gold`, `rice`, leader, `power`, intel, killturn, crew, train, atmos, `level` FROM general WHERE nation = %i', $general['nation']) as $nationGeneral) {
|
||||
foreach($db->query('SELECT `no`, nation, city, npc, `gold`, `rice`, leader, `power`, intel, killturn, crew, train, atmos, `level`, troop FROM general WHERE nation = %i', $general['nation']) as $nationGeneral) {
|
||||
$cityID = $nationGeneral['city'];
|
||||
$generalID = $nationGeneral['no'];
|
||||
|
||||
@@ -1082,7 +1106,15 @@ function NPCStaffWork($general, $nation, $dipState){
|
||||
}
|
||||
} else{ // 포상
|
||||
$compNpcWar = $npcWarGeneralsID?$nationGenerals[$npcWarGeneralsID[0]]:null;
|
||||
$compNpcCivil = $npcCivilGeneralsID?$nationGenerals[$npcCivilGeneralsID[0]]:null;
|
||||
$compNpcCivil = null;
|
||||
foreach($npcCivilGeneralsID??[] as $npcCivilID){
|
||||
$npcCivil = $nationGenerals[$npcCivilID];
|
||||
if($npcCivil['npc'] == 5){
|
||||
continue;
|
||||
}
|
||||
$compNpcCivil = $npcCivil;
|
||||
break;
|
||||
}
|
||||
|
||||
if($compNpcWar && $compNpcWar[$resName] < 21000){
|
||||
$amount = min(100, intdiv(($nation[$resName]-($resName=='rice'?(GameConst::$baserice):(GameConst::$basegold))), 5000)*10 + 10);
|
||||
@@ -1171,13 +1203,18 @@ function NPCStaffWork($general, $nation, $dipState){
|
||||
|
||||
if($maxDevCity['dev'] >= 95 && $targetCity['city'] != $maxDevCity['city'] && $targetCity['dev'] <= 70){
|
||||
$targetGeneral = $nationGenerals[Util::choiceRandom($maxDevCity['generals'])];
|
||||
$commandList[EncodeCommand(0, $targetGeneral['no'], $targetCity['city'], 27)] = 2;
|
||||
if($targetGeneral['troop'] != 0){
|
||||
$commandList[EncodeCommand(0, $targetGeneral['no'], $targetCity['city'], 27)] = 2;
|
||||
}
|
||||
}
|
||||
|
||||
if(count($targetCity['generals']) < count($maxCity['generals']) - 2){
|
||||
//세명 이상 차이나야 함
|
||||
$targetGeneral = $nationGenerals[Util::choiceRandom($maxCity['generals'])];
|
||||
if($targetGeneral['npc']>=2 || $maxCity['dev'] >= 95){
|
||||
if($targetGeneral['npc']==5){
|
||||
|
||||
}
|
||||
else if($targetGeneral['npc']>=2 || $maxCity['dev'] >= 95 && $targetGeneral['troop'] == 0){
|
||||
//유저장은 의도가 있을 것이므로 삽나지 않는 이상 발령 안함!
|
||||
$commandList[EncodeCommand(0, $targetGeneral['no'], $targetCity['city'], 27)] = 5;
|
||||
}
|
||||
@@ -1205,6 +1242,10 @@ function NPCStaffWork($general, $nation, $dipState){
|
||||
if($nationGeneral['train'] * $nationGeneral['atmos'] < 75 * 75){
|
||||
continue;
|
||||
}
|
||||
|
||||
if($nationGeneral['troop'] != 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
$score = 5;
|
||||
if($nationGeneral['npc']<2){
|
||||
@@ -1255,6 +1296,9 @@ function NPCStaffWork($general, $nation, $dipState){
|
||||
if($generalCity['pop'] - 33000 > $nationGeneral['leader']){
|
||||
continue;
|
||||
}
|
||||
if($nationGeneral['troop'] != 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
$score = 5;
|
||||
if($nationGeneral['npc']<2){
|
||||
|
||||
@@ -273,6 +273,10 @@ function process_1(&$general, $type) {
|
||||
$exp = CharExperience($exp, $general['personal']);
|
||||
$ded = CharDedication($ded, $general['personal']);
|
||||
|
||||
if($city['front'] == 1 && $city['city'] != $nation['capital']){
|
||||
$score *= 0.5;
|
||||
}
|
||||
|
||||
$score += $city["$stype"];
|
||||
if($score > $city["{$stype}2"]) { $score = $city["{$stype}2"]; }
|
||||
// 내정 상승
|
||||
@@ -558,6 +562,16 @@ function process_5(&$general, $type) {
|
||||
$exp = $score * 0.7;
|
||||
$ded = $score * 1.0;
|
||||
|
||||
if($city['front'] == 1 && $city['city'] != $nation['capital']){
|
||||
if($stype == 'def'){
|
||||
$score *= 0.5;
|
||||
}
|
||||
else{
|
||||
$score *= 0.25;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 성격 보정
|
||||
$exp = CharExperience($exp, $general['personal']);
|
||||
$ded = CharDedication($ded, $general['personal']);
|
||||
|
||||
@@ -11,7 +11,7 @@ function process_22(&$general) {
|
||||
$history = [];
|
||||
$date = substr($general['turntime'],11,5);
|
||||
|
||||
$admin = $gameStor->getValues(['startyear','year','month','develcost']);
|
||||
$admin = $gameStor->getValues(['startyear','year','month','develcost','join_mode']);
|
||||
|
||||
$query = "select nation,supply from city where city='{$general['city']}'";
|
||||
$result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),"");
|
||||
@@ -26,7 +26,10 @@ function process_22(&$general) {
|
||||
|
||||
$cost = Util::round($admin['develcost'] + ($you['experience'] + $you['dedication'])/1000) * 10;
|
||||
|
||||
if(!$you) {
|
||||
if($admin['join_mode'] == 'onlyRandom'){
|
||||
$log[] = "<C>●</>{$admin['month']}월:랜덤 임관만 가능합니다. 등용 실패. <1>$date</>";
|
||||
}
|
||||
else if(!$you) {
|
||||
$log[] = "<C>●</>{$admin['month']}월:없는 장수입니다. 등용 실패. <1>$date</>";
|
||||
} elseif($admin['year'] < $admin['startyear']+3) {
|
||||
$log[] = "<C>●</>{$admin['month']}월:초반 제한중입니다. 등용 실패. <1>$date</>";
|
||||
@@ -70,7 +73,7 @@ function process_25(&$general) {
|
||||
$history = [];
|
||||
$date = substr($general['turntime'],11,5);
|
||||
|
||||
$admin = $gameStor->getValues(['startyear', 'year', 'month', 'scenario', 'fiction']);
|
||||
$admin = $gameStor->getValues(['startyear', 'year', 'month', 'scenario', 'fiction', 'join_mode', 'init_year', 'init_month']);
|
||||
|
||||
$query = "select nation from city where city='{$general['city']}'";
|
||||
$result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),"");
|
||||
@@ -79,12 +82,16 @@ function process_25(&$general) {
|
||||
$command = DecodeCommand($general['turn0']);
|
||||
$where = $command[1];
|
||||
|
||||
if($admin['join_mode'] == 'onlyRandom' && $where < 98){
|
||||
$where = 99;
|
||||
}
|
||||
|
||||
$nation = null;
|
||||
|
||||
$joinedNations = Json::decode($general['nations']);
|
||||
|
||||
// 랜덤임관인 경우
|
||||
if($general['npc'] > 2 && $where >= 98 && ($admin['scenario'] < 100 || $admin['scenario'] >= 2000 || !$admin['fiction'])){
|
||||
if($general['npc'] >= 2 && $where >= 98 && !$admin['fiction'] && 1000 <= $admin['scenario'] && $admin['scenario'] < 2000){
|
||||
//'사실' 모드에서는 '성향'에 우선을 두되, 장수수, 랜덤에 비중을 둠
|
||||
$nations = $db->query(
|
||||
'SELECT nation.`name` as `name`,nation.nation as nation,scout,nation.`level` as `level`,gennum,`affinity` FROM nation join general on general.nation = nation.nation and general.level = 12 WHERE nation.nation not in %li and gennum < %i and scout = 0',
|
||||
@@ -126,10 +133,18 @@ function process_25(&$general) {
|
||||
|
||||
$allGen = array_sum($generals);
|
||||
|
||||
$genLimit = GameConst::$defaultMaxGeneral;
|
||||
if($admin['join_mode'] == 'onlyRandom' && TimeUtil::IsRangeMonth($admin['init_year'], $admin['init_month'], 1, $admin['year'], $admin['month'])){
|
||||
$genLimit = GameConst::$initialNationGenLimitForRandInit;
|
||||
}
|
||||
else if($admin['year'] < $admin['startyear'] + 3){
|
||||
$genLimit = GameConst::$initialNationGenLimit;
|
||||
}
|
||||
|
||||
$nations = $db->query(
|
||||
'SELECT nation.`name` as `name`,nation.nation as nation,scout,nation.`level` as `level`,gennum,`injury` FROM nation join general on general.nation = nation.nation and general.level = 12 WHERE nation.nation not in %li and gennum < %i and scout = 0',
|
||||
$joinedNations,
|
||||
($admin['year'] < $admin['startyear']+3)?GameConst::$initialNationGenLimit:GameConst::$defaultMaxGeneral
|
||||
$genLimit
|
||||
);
|
||||
shuffle($nations);
|
||||
|
||||
@@ -152,6 +167,9 @@ function process_25(&$general) {
|
||||
if($randVals){
|
||||
$nation = $nations[Util::choiceRandomUsingWeight($randVals)];
|
||||
}
|
||||
else{
|
||||
$nation = null;
|
||||
}
|
||||
|
||||
} else {
|
||||
$nation = $db->queryFirstRow('SELECT `name`,nation,scout,`level` FROM nation WHERE nation=%i', $where);
|
||||
@@ -164,6 +182,12 @@ function process_25(&$general) {
|
||||
|
||||
if(!$nation) {
|
||||
$log[] = "<C>●</>{$admin['month']}월:임관할 국가가 없습니다. 임관 실패. <1>$date</>";
|
||||
if($where >= 98 && $genLimit == GameConst::$initialNationGenLimitForRandInit){
|
||||
//랜덤 모드, 초기화시에는 랜덤 임관을 대신 한턴 더 넣어준다.
|
||||
$db->update('general', [
|
||||
'turn1'=>EncodeCommand(0, 0, $where, 25),
|
||||
], '`no` = %i', $general['no']);
|
||||
}
|
||||
} elseif($general['nation'] != 0) {
|
||||
$log[] = "<C>●</>{$admin['month']}월:재야가 아닙니다. 임관 실패. <1>$date</>";
|
||||
} elseif($nation['nation'] == 0) {
|
||||
|
||||
@@ -101,7 +101,7 @@ function processGoldIncome() {
|
||||
$query = "update nation set gold='{$nation['gold']}' where nation='{$nation['nation']}'";
|
||||
MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),"");
|
||||
|
||||
$query = "select no,name,nation,dedication,gold from general where nation='{$nation['nation']}'";
|
||||
$query = "select no,name,nation,dedication,gold from general where nation='{$nation['nation']}' AND npc != 5";
|
||||
$genresult = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),"");
|
||||
$gencount = MYDB_num_rows($genresult);
|
||||
|
||||
@@ -317,7 +317,7 @@ function getGoldOutcome($nation, $bill) {
|
||||
$db = DB::db();
|
||||
$connect=$db->get();
|
||||
|
||||
$query = "select dedication from general where nation='$nation'"; // 장수 목록
|
||||
$query = "select dedication from general where nation='$nation' AND npc != 5"; // 장수 목록
|
||||
$genresult = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),"");
|
||||
$gencount = MYDB_num_rows($genresult);
|
||||
|
||||
@@ -424,7 +424,7 @@ function processRiceIncome() {
|
||||
$query = "update nation set rice='{$nation['rice']}' where nation='{$nation['nation']}'";
|
||||
MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),"");
|
||||
|
||||
$query = "select no,name,nation,dedication,rice from general where nation='{$nation['nation']}'";
|
||||
$query = "select no,name,nation,dedication,rice from general where nation='{$nation['nation']}' AND npc != 5";
|
||||
$genresult = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),"");
|
||||
$gencount = MYDB_num_rows($genresult);
|
||||
|
||||
@@ -517,7 +517,7 @@ function getRiceOutcome($nation, $bill) {
|
||||
$db = DB::db();
|
||||
$connect=$db->get();
|
||||
|
||||
$query = "select dedication from general where nation='$nation'"; // 장수 목록
|
||||
$query = "select dedication from general where nation='$nation' AND npc != 5"; // 장수 목록
|
||||
$genresult = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),"");
|
||||
$gencount = MYDB_num_rows($genresult);
|
||||
|
||||
@@ -594,14 +594,14 @@ function disaster() {
|
||||
|
||||
for($i=0; $i < $citycount; $i++) {
|
||||
$city = MYDB_fetch_array($cityresult);
|
||||
//호황 발생 도시 선택 ( 기본 3% 이므로 약 3개 도시 )
|
||||
//재해 발생 도시 선택 ( 기본 6% 이므로 약 6개 도시 )
|
||||
if($isgood == 1) { $ratio = 3 + Util::round(1.0*$city['secu']/$city['secu2']*3); } // 3 ~ 6%
|
||||
else { $ratio = 6 - Util::round(1.0*$city['secu']/$city['secu2']*3); } // 3 ~ 6%
|
||||
//호황 발생 도시 선택 ( 기본 2% )
|
||||
//재해 발생 도시 선택 ( 기본 6% )
|
||||
if($isgood == 1) { $ratio = 2 + Util::round($city['secu']/$city['secu2']*5); } // 2 ~ 7%
|
||||
else { $ratio = 6 - Util::round($city['secu']/$city['secu2']*5); } // 1 ~ 6%
|
||||
|
||||
if(rand()%100+1 < $ratio) {
|
||||
$disastercity[] = $city['city'];
|
||||
$disasterratio[] = 1.0 * $city['secu'] / $city['secu2'];
|
||||
$disasterratio[] = Util::valueFit($city['secu'] / 0.8 / $city['secu2'], 0, 1);
|
||||
$disastername[] = $city['name'];
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -282,11 +282,11 @@ else if($session->userGrade == 4){
|
||||
<td width=498 class='bg1 center'><b>개인 기록</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width=498 style="text-align:left;"><?=getGeneralPublicRecordRecent(15)?></td>
|
||||
<td width=498 style="text-align:left;"><?=getGenLogRecent($me['no'], 15)?></td>
|
||||
<td width=498 id="general_public_record" style="text-align:left;"><?=getGeneralPublicRecordRecent(15)?></td>
|
||||
<td width=498 id="general_log" style="text-align:left;"><?=getGenLogRecent($me['no'], 15)?></td>
|
||||
</tr>
|
||||
<tr><td width=998 colspan=2 class='bg1 center'><b>중원 정세</b></td></tr>
|
||||
<tr><td width=998 colspan=2 style="text-align:left;"><?=getWorldHistoryRecent(15)?></td></tr>
|
||||
<tr><td width=998 id="world_history" colspan=2 style="text-align:left;"><?=getWorldHistoryRecent(15)?></td></tr>
|
||||
</table>
|
||||
<div class="message_input_form bg0">
|
||||
<select id="mailbox_list" size="1">
|
||||
|
||||
@@ -152,6 +152,20 @@ if($session->userGrade < 5 && !$allowReset){
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="join_mode" class="col-sm-3 col-form-label">임관 모드</label>
|
||||
<div class="col-sm-9">
|
||||
<div id="join_mode" class="btn-group btn-group-toggle" data-toggle="buttons">
|
||||
<label class="btn btn-secondary active">
|
||||
<input type="radio" name="join_mode" value="full" checked>일반
|
||||
</label>
|
||||
<label class="btn btn-secondary">
|
||||
<input type="radio" name="join_mode" value="onlyRandom">랜덤 임관
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="show_img_level" class="col-sm-3 col-form-label">이미지 표기</label>
|
||||
<div class="col-sm-9">
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
namespace sammo;
|
||||
|
||||
include 'lib.php';
|
||||
include 'func.php';
|
||||
|
||||
$session = Session::requireGameLogin([])->setReadOnly();
|
||||
$userID = $session::getUserID();
|
||||
$serverID = UniqueConst::$serverID;
|
||||
|
||||
$image = $_FILES['img'];
|
||||
switch ($image['error']) {
|
||||
case UPLOAD_ERR_OK:
|
||||
break;
|
||||
case UPLOAD_ERR_NO_FILE:
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reason'=>'파일이 없습니다.'
|
||||
]);
|
||||
case UPLOAD_ERR_INI_SIZE:
|
||||
case UPLOAD_ERR_FORM_SIZE:
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reason'=>'업로드된 파일이 지나치게 큽니다.'
|
||||
]);
|
||||
default:
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reason'=>'업로드되지 않았습니다.'
|
||||
]);
|
||||
}
|
||||
|
||||
if(!is_uploaded_file($image['tmp_name'])) {
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reason'=>'제대로 파일이 업로드되지 않았습니다.'
|
||||
]);
|
||||
}
|
||||
|
||||
if($image['size'] > 1048576) {
|
||||
//파일크기 검사
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reason'=>'1MB 이하로 올려주세요!'
|
||||
]);
|
||||
}
|
||||
|
||||
$size = getImageSize($image['tmp_name']);
|
||||
|
||||
$imageType = $size[2];
|
||||
$availableImageType = array('.jpg'=>IMAGETYPE_JPEG, '.png'=>IMAGETYPE_PNG, '.gif'=>IMAGETYPE_GIF);
|
||||
$newExt = array_search($imageType, $availableImageType, true);
|
||||
|
||||
if(!$newExt) {
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reason'=>'jpg, gif, png 파일이 아닙니다!'
|
||||
]);
|
||||
}
|
||||
|
||||
$db = RootDB::db();
|
||||
$imgStor = KVStorage::getStorage($db, 'img_storage');
|
||||
|
||||
$picName = hash_file('md5', $image['tmp_name']);
|
||||
$newPicName = "$picName$newExt";
|
||||
|
||||
$destDir = AppConf::getUserIconPathFS().'/uploaded_image';
|
||||
$dest = $destDir.'/'.$newPicName;
|
||||
|
||||
if(!file_exists($dest)){
|
||||
if (!file_exists($destDir)) {
|
||||
mkdir($destDir);
|
||||
}
|
||||
if(!is_dir($destDir)) {
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reason'=>'버그! 업로드 경로 확인!'
|
||||
]);
|
||||
}
|
||||
if(!is_writable($destDir)){
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reason'=>'버그! 업로드 권한 확인!'
|
||||
]);
|
||||
}
|
||||
|
||||
$dest = $destDir.'/'.$newPicName;
|
||||
|
||||
if(!move_uploaded_file($image['tmp_name'], $dest)) {
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reason'=>'업로드에 실패했습니다!'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$storedStatus = $imgStor->$newPicName??[];
|
||||
$imgKey = "$serverID:$userID";
|
||||
if(!key_exists($imgKey, $storedStatus)){
|
||||
$storedStatus[$imgKey] = TimeUtil::DatetimeNow();
|
||||
}
|
||||
|
||||
$imgStor->$newPicName = $storedStatus;
|
||||
|
||||
Json::die([
|
||||
'result'=>true,
|
||||
'reason'=>'성공',
|
||||
'path'=>AppConf::getUserIconPathWeb().'/uploaded_image/'.$newPicName
|
||||
]);
|
||||
+8
-4
@@ -41,6 +41,7 @@ $v->rule('required', [
|
||||
'scenario',
|
||||
'fiction',
|
||||
'extend',
|
||||
'join_mode',
|
||||
'npcmode',
|
||||
'show_img_level'
|
||||
])->rule('integer', [
|
||||
@@ -51,8 +52,8 @@ $v->rule('required', [
|
||||
'extend',
|
||||
'npcmode',
|
||||
'show_img_level',
|
||||
'tournament_trig'
|
||||
]);
|
||||
'tournament_trig',
|
||||
])->rule('in', 'join_mode', ['onlyRandom', 'full']);
|
||||
if(!$v->validate()){
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
@@ -92,6 +93,7 @@ $extend = (int)$_POST['extend'];
|
||||
$npcmode = (int)$_POST['npcmode'];
|
||||
$show_img_level = (int)$_POST['show_img_level'];
|
||||
$tournament_trig = (int)$_POST['tournament_trig'];
|
||||
$join_mode = $_POST['join_mode'];
|
||||
|
||||
if($reserve_open){
|
||||
$reserve_open = new \DateTime($reserve_open);
|
||||
@@ -124,7 +126,8 @@ if($reserve_open){
|
||||
'npcmode'=>$npcmode,
|
||||
'show_img_level'=>$show_img_level,
|
||||
'tournament_trig'=>$tournament_trig,
|
||||
'gameConf'=>$scenarioObj->getGameConf()
|
||||
'gameConf'=>$scenarioObj->getGameConf(),
|
||||
'join_mode'=>$join_mode,
|
||||
]),
|
||||
'date'=>$reserve_open->format('Y-m-d H:i:s')
|
||||
]);
|
||||
@@ -143,5 +146,6 @@ Json::die(ResetHelper::buildScenario(
|
||||
$extend,
|
||||
$npcmode,
|
||||
$show_img_level,
|
||||
$tournament_trig
|
||||
$tournament_trig,
|
||||
$join_mode
|
||||
));
|
||||
+19
-5
@@ -155,26 +155,40 @@ $leader = $leader + $pleader;
|
||||
$power = $power + $ppower;
|
||||
$intel = $intel + $pintel;
|
||||
|
||||
$admin = $gameStor->getValues(['scenario', 'turnterm', 'show_img_level', 'startyear', 'year']);
|
||||
$relYear = Util::valueFit($admin['year'] - $admin['startyear'], 0);
|
||||
|
||||
$age = 20 + ($pleader + $ppower + $pintel) * 2 - (mt_rand(0, 1));
|
||||
// 아직 남았고 천재등록상태이면 특기 부여
|
||||
if ($genius) {
|
||||
$specage2 = $age;
|
||||
$special2 = getSpecial2($leader, $power, $intel);
|
||||
} else {
|
||||
$specage2 = Util::round((80 - $age)/3) + $age;
|
||||
$specage2 = Util::valueFit(Util::round((80 - $age)/4 - $relYear / 2), 3) + $age;
|
||||
$special2 = 0;
|
||||
}
|
||||
//내특
|
||||
$specage = Util::round((80 - $age)/12) + $age;
|
||||
$specage = Util::valueFit(Util::round((80 - $age)/12 - $relYear / 2), 3) + $age;
|
||||
$special = 0;
|
||||
|
||||
$admin = $gameStor->getValues(['scenario', 'turnterm', 'show_img_level']);
|
||||
|
||||
if ($admin['scenario'] >= 1000) {
|
||||
$specage2 = $age + 3;
|
||||
$specage = $age + 3;
|
||||
}
|
||||
|
||||
if($relYear < 3){
|
||||
$experience = 0;
|
||||
}
|
||||
else{
|
||||
$expGenCount = $db->queryFirstField('SELECT count(*) FROM general WHERE nation != 0 AND npc < 5');
|
||||
$targetGenOrder = Util::round($expGenCount * 0.2);
|
||||
$experience = $db->queryFirstField(
|
||||
'SELECT experience FROM general WHERE nation != 0 AND npc < 5 ORDER BY experience ASC LIMIT %i, 1',
|
||||
$targetGenOrder - 1
|
||||
);
|
||||
$experience *= 0.8;
|
||||
}
|
||||
|
||||
$turntime = getRandTurn($admin['turnterm']);
|
||||
|
||||
$lastconnect = date('Y-m-d H:i:s');
|
||||
@@ -211,7 +225,7 @@ $db->insert('general', [
|
||||
'leader' => $leader,
|
||||
'power' => $power,
|
||||
'intel' => $intel,
|
||||
'experience' => 0,
|
||||
'experience' => $experience,
|
||||
'dedication' => 0,
|
||||
'gold' => 1000,
|
||||
'rice' => 1000,
|
||||
|
||||
+64
-6
@@ -143,18 +143,76 @@ function getIconPath(imgsvr, picture){
|
||||
}
|
||||
}
|
||||
|
||||
jQuery(function($){
|
||||
$('.obj_tooltip').tooltip({
|
||||
title:function(){
|
||||
return $.trim($(this).find('.tooltiptext').html());
|
||||
},
|
||||
html:true
|
||||
function activeFlip($obj){
|
||||
var $result;
|
||||
if($obj === undefined){
|
||||
$result = $('img[data-flip]');
|
||||
}
|
||||
else{
|
||||
$result = $obj.find('img[data-flip]');
|
||||
}
|
||||
|
||||
$result.each(function(){
|
||||
activeFlipItem($(this));
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function activeFlipItem($img){
|
||||
var imageList = [];
|
||||
imageList.push($img.attr('src'));
|
||||
$.each($img.data('flip').split(','), function(idx, value){
|
||||
var value = $.trim(value);
|
||||
if(!value){
|
||||
return true;
|
||||
}
|
||||
imageList.push(value);
|
||||
});
|
||||
if(imageList.length <= 1){
|
||||
return;
|
||||
}
|
||||
$img.data('computed_flip_array', imageList);
|
||||
$img.data('computed_flip_idx', 0);
|
||||
|
||||
$img.click(function(){
|
||||
var arr = $img.data('computed_flip_array');
|
||||
var idx = $img.data('computed_flip_idx');
|
||||
idx = (idx + 1)%(arr.length);
|
||||
$img.attr('src', arr[idx]);
|
||||
$img.data('computed_flip_idx', idx);
|
||||
});
|
||||
$img.css('cursor','pointer');
|
||||
}
|
||||
|
||||
jQuery(function($){
|
||||
$('.obj_tooltip').each(function(){
|
||||
var $objTooltip = $(this);
|
||||
var tooltipClassText = $objTooltip.data('tooltip-class');
|
||||
if(!tooltipClassText){
|
||||
tooltipClassText = '';
|
||||
}
|
||||
console.log($objTooltip.data('tooltip-class'));
|
||||
var template = '<div class="tooltip {0}" role="tooltip"><div class="arrow"></div><div class="tooltip-inner"></div></div>'
|
||||
.format(tooltipClassText);
|
||||
|
||||
$objTooltip.tooltip({
|
||||
title:function(){
|
||||
return $.trim($(this).find('.tooltiptext').html());
|
||||
},
|
||||
template:template,
|
||||
html:true
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
activeFlip();
|
||||
|
||||
var customCSS = localStorage.getItem('sam_customCSS');
|
||||
if(customCSS){
|
||||
var $style = $('<style type="text/css"></style>');
|
||||
$style.text(customCSS);
|
||||
$style.appendTo($('head'));
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
+58
-1
@@ -15,6 +15,12 @@ jQuery(function($){
|
||||
function enableEditor(){
|
||||
editMode = true;
|
||||
$cancelEdit.show();
|
||||
|
||||
var inputText = $noticeInput.val();
|
||||
if(!inputText || inputText == '<p></p>'){
|
||||
inputText = '<p><br></p>';
|
||||
}
|
||||
|
||||
$editForm.removeClass('viewer').summernote({
|
||||
minHeight:200,
|
||||
maxHeight:null,
|
||||
@@ -31,10 +37,59 @@ jQuery(function($){
|
||||
['para', ['ul', 'ol', 'paragraph']],
|
||||
['height', ['height', 'codeview']]
|
||||
],
|
||||
popover: {
|
||||
image: [
|
||||
['imagesize', ['imageSize100', 'imageSize50', 'imageSize25']],
|
||||
['float', ['floatLeft', 'floatRight', 'floatNone']],
|
||||
['remove', ['removeMedia']],
|
||||
['custom', ['imageFlip']],
|
||||
],
|
||||
},
|
||||
fontNames: ['맑은 고딕', 'Nanum Gothic', 'Nanum Myeongjo', 'Nanum Pen Script', '굴림', '굴림체', '바탕', '바탕체', '궁서', '궁서체'],
|
||||
fontSizes: ['8', '9', '10', '11', '12', '14', '16', '20', '24', '28', '32', '36', '40', '46', '52', '60'],
|
||||
callbacks: {
|
||||
onImageUpload: function(files) {
|
||||
$editForm.summernote('saveRange');
|
||||
if(files.length == 0){
|
||||
alert('업로드된 파일이 없습니다.');
|
||||
return false;
|
||||
}
|
||||
|
||||
}).summernote('code', $noticeInput.val());
|
||||
var formData = new FormData();
|
||||
formData.append('img', files[0]);
|
||||
$editForm.summernote("pasteHtml", '');
|
||||
|
||||
$.ajax({
|
||||
type:'post',
|
||||
url:'j_image_upload.php',
|
||||
dataType:'json',
|
||||
contentType: false,
|
||||
processData:false,
|
||||
data:formData
|
||||
}).then(function(result){
|
||||
if(!result.result){
|
||||
alert(result.reason);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log($editForm.summernote('code'));
|
||||
if($editForm.summernote('isEmpty')){
|
||||
|
||||
console.log('haha?');
|
||||
var $img = $('<img>');
|
||||
$img.attr('src', result.path);
|
||||
$editForm.summernote('code', $img);
|
||||
return;
|
||||
}
|
||||
|
||||
$editForm.summernote("insertImage", result.path, result.path);
|
||||
|
||||
},function(){
|
||||
alert('알 수 없는 이유로 아이콘 업로드를 실패했습니다.');
|
||||
});
|
||||
}
|
||||
}
|
||||
}).summernote('code', inputText);
|
||||
}
|
||||
|
||||
function disableEditor(){
|
||||
@@ -42,10 +97,12 @@ jQuery(function($){
|
||||
$editForm.summernote('destroy');
|
||||
$cancelEdit.hide();
|
||||
$editForm.html($noticeInput.val()).addClass('viewer');
|
||||
activeFlip($editForm);
|
||||
}
|
||||
|
||||
$cancelEdit.hide();
|
||||
$editForm.html($noticeInput.val());
|
||||
activeFlip($editForm);
|
||||
if(editable){
|
||||
$submitBtn.prop('disabled', false);
|
||||
}
|
||||
|
||||
+4
-2
@@ -114,7 +114,8 @@ function formSetup(){
|
||||
extend:"required",
|
||||
npcmode:"required",
|
||||
show_img_level:"required",
|
||||
tournament_trig:"required"
|
||||
tournament_trig:"required",
|
||||
join_mode:'required',
|
||||
},
|
||||
errorElement: "div",
|
||||
errorPlacement: function ( error, element ) {
|
||||
@@ -153,7 +154,8 @@ function formSetup(){
|
||||
npcmode:$('#npcmode input:radio:checked').val(),
|
||||
show_img_level:$('#show_img_level input:radio:checked').val(),
|
||||
tournament_trig:$('#tournament_trig input:radio:checked').val(),
|
||||
reserve_open:$('#reserve_open').val()
|
||||
reserve_open:$('#reserve_open').val(),
|
||||
join_mode:$('#join_mode input:radio:checked').val(),
|
||||
}
|
||||
}).then(function(result){
|
||||
var deferred = $.Deferred();
|
||||
|
||||
@@ -173,4 +173,16 @@ jQuery(function($){
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
var randomGenType = Math.floor(Math.random()*7);
|
||||
if(randomGenType < 3){
|
||||
abilityLeadpow();
|
||||
}
|
||||
else if(randomGenType < 6){
|
||||
abilityLeadint();
|
||||
}
|
||||
else{
|
||||
abilityPowint();
|
||||
}
|
||||
|
||||
});
|
||||
@@ -90,6 +90,32 @@ function reloadWorldMap(option){
|
||||
$map_title.css('color', 'yellow');
|
||||
}
|
||||
|
||||
$map_title_tooltip = $('.map_title .tooltiptext');
|
||||
$map_title_tooltip.empty();
|
||||
|
||||
var tooltipTexts = [];
|
||||
if(year < startYear + 3){
|
||||
var startYearText = [];
|
||||
var remainYear = startYear + 3 - year;
|
||||
var remainMonth = 12 - month + 1;
|
||||
if(remainMonth > 0){
|
||||
remainYear -= 1;
|
||||
}
|
||||
if(remainYear){
|
||||
startYearText.push('{0}년'.format(remainYear));
|
||||
}
|
||||
if(remainMonth){
|
||||
startYearText.push('{0}개월'.format(remainMonth));
|
||||
}
|
||||
|
||||
tooltipTexts.push('초반제한 기간 : {0} ({1}년)'.format(startYearText.join(' '), startYear + 3));
|
||||
}
|
||||
|
||||
var currentTechLimit = Math.floor(Math.max(0, year - startYear) / 5) + 1;
|
||||
var nextTechLimitYear = currentTechLimit * 5 + startYear;
|
||||
|
||||
tooltipTexts.push('기술등급 제한 : {0}등급 ({1}년 해제)'.format(currentTechLimit, nextTechLimitYear, currentTechLimit+1));
|
||||
$map_title_tooltip.html(tooltipTexts.join('<br>'));
|
||||
|
||||
$world_map.removeClass('map_string map_summer map_fall map_winter');
|
||||
if(month <= 3){
|
||||
|
||||
@@ -257,6 +257,16 @@ function refreshMailboxList(obj){
|
||||
$optgroup.css('color', 'white');
|
||||
}
|
||||
|
||||
nation.general.sort(function(lhs, rhs){
|
||||
if(lhs[1] < rhs[1]){
|
||||
return -1;
|
||||
}
|
||||
if(lhs[1] > rhs[1]){
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
$.each(nation.general, function(){
|
||||
var generalID = this[0];
|
||||
var generalName = this[1];
|
||||
|
||||
+47
-27
@@ -31,7 +31,7 @@ function processWar(array $rawAttacker, array $rawDefenderCity){
|
||||
}
|
||||
|
||||
$gameStor = KVStorage::getStorage($db, 'game_env');
|
||||
[$startYear, $year, $month, $cityRate] = $gameStor->getValuesAsArray(['startyear', 'year', 'month', 'city_rate']);
|
||||
[$startYear, $year, $month, $cityRate, $joinMode] = $gameStor->getValuesAsArray(['startyear', 'year', 'month', 'city_rate', 'join_mode']);
|
||||
|
||||
$attacker = new WarUnitGeneral($rawAttacker, $rawAttackerCity, $rawAttackerNation, true, $year, $month);
|
||||
|
||||
@@ -154,7 +154,8 @@ function processWar(array $rawAttacker, array $rawDefenderCity){
|
||||
'startyear'=>$startYear,
|
||||
'year'=>$year,
|
||||
'month'=>$month,
|
||||
'city_rate'=>$cityRate
|
||||
'city_rate'=>$cityRate,
|
||||
'join_mode'=>$joinMode,
|
||||
], $attacker->getRaw(), $city->getRaw(), $rawAttackerNation, $rawDefenderNation);
|
||||
}
|
||||
|
||||
@@ -183,9 +184,9 @@ function extractBattleOrder($general){
|
||||
$general['leader'] +
|
||||
$general['power'] +
|
||||
$general['intel'] +
|
||||
$general['weap'] +
|
||||
$general['horse'] +
|
||||
$general['book'] +
|
||||
getWeapEff($general['weap']) +
|
||||
getHorseEff($general['horse']) +
|
||||
getBookEff($general['book']) +
|
||||
$general['crew'] / 100
|
||||
);
|
||||
}
|
||||
@@ -270,8 +271,12 @@ function processWar_NG(
|
||||
$attacker->setOppose($defender);
|
||||
$defender->setOppose($attacker);
|
||||
|
||||
$attacker->checkBattleBeginSkill();
|
||||
$defender->checkBattleBeginSkill();
|
||||
foreach(Util::zip(
|
||||
$attacker->checkBattleBeginSkill(),
|
||||
$defender->checkBattleBeginSkill()
|
||||
) as $b){
|
||||
//doNothing
|
||||
}
|
||||
|
||||
$attacker->checkBattleBeginItem();
|
||||
$defender->checkBattleBeginItem();
|
||||
@@ -283,21 +288,33 @@ function processWar_NG(
|
||||
$attacker->beginPhase();
|
||||
$defender->beginPhase();
|
||||
|
||||
$attacker->checkPreActiveSkill();
|
||||
$defender->checkPreActiveSkill();
|
||||
foreach(Util::zip(
|
||||
$attacker->checkPreActiveSkill(),
|
||||
$defender->checkPreActiveSkill()
|
||||
) as $b){
|
||||
//doNothing
|
||||
}
|
||||
|
||||
$attacker->checkActiveSkill();
|
||||
$defender->checkActiveSkill();
|
||||
foreach(Util::zip(
|
||||
$attacker->checkActiveSkill(),
|
||||
$defender->checkActiveSkill()
|
||||
) as $b){
|
||||
//doNothing
|
||||
}
|
||||
//NOTE: 마법은 checkActiveSkill, checkPostActiveSkill 내에서 반영
|
||||
|
||||
$attacker->checkPostActiveSkill();
|
||||
$defender->checkPostActiveSkill();
|
||||
foreach(Util::zip(
|
||||
$attacker->checkPostActiveSkill(),
|
||||
$defender->checkPostActiveSkill()
|
||||
) as $b){
|
||||
//doNothing
|
||||
}
|
||||
//NOTE: 반계류 등의 스킬을 post에서 반영
|
||||
|
||||
$activeSkillIterator = new \AppendIterator();
|
||||
$activeSkillIterator->append($attacker->applyActiveSkill());
|
||||
$activeSkillIterator->append($defender->applyActiveSkill());
|
||||
foreach($activeSkillIterator as $b){
|
||||
foreach(Util::zip(
|
||||
$attacker->applyActiveSkill(),
|
||||
$defender->applyActiveSkill()
|
||||
) as $b){
|
||||
//doNothing
|
||||
}
|
||||
|
||||
@@ -498,6 +515,7 @@ function ConquerCity($admin, $general, $city, $nation, $destnation) {
|
||||
pushNationHistory($destnation, "<C>●</>{$year}년 {$month}월:<D><b>{$nation['name']}</b></>의 <Y>{$general['name']}</>에 의해 <G><b>{$city['name']}</b></>{$josaYiCity} <span class='ev_highlight'>함락</span>");
|
||||
|
||||
$citycount = $db->queryFirstField('SELECT count(city) FROM city WHERE nation = %i', $city['nation']);
|
||||
$renewFront = false;
|
||||
|
||||
// 국가 멸망시
|
||||
//TODO: 국가 멸망 코드를 별도로 작성
|
||||
@@ -509,9 +527,6 @@ function ConquerCity($admin, $general, $city, $nation, $destnation) {
|
||||
$history[] = "<C>●</>{$year}년 {$month}월:<R><b>【멸망】</b></><D><b>{$losenation['name']}</b></>{$josaYi} 멸망하였습니다.";
|
||||
pushNationHistory($nation, "<C>●</>{$year}년 {$month}월:<D><b>{$losenation['name']}</b></>{$josaUl} 정복");
|
||||
|
||||
//다굴치는 나라들 전방설정을 위해 미리 얻어옴
|
||||
$dipNations = $db->queryFirstColumn('SELECT you FROM diplomacy WHERE me = %i', $losenation['nation']);
|
||||
|
||||
$loseGeneralGold = 0;
|
||||
$loseGeneralRice = 0;
|
||||
//멸망국 장수들 역사 기록 및 로그 전달
|
||||
@@ -546,7 +561,7 @@ function ConquerCity($admin, $general, $city, $nation, $destnation) {
|
||||
$loseGeneralRice += $loseRice;
|
||||
|
||||
//모두 등용장 발부
|
||||
if(Util::randBool(0.5)) {
|
||||
if($admin['join_mode'] != 'onlyRandom' && Util::randBool(0.5)) {
|
||||
$msg = ScoutMessage::buildScoutMessage($general['no'], $gen['no']);
|
||||
if($msg){
|
||||
$msg->send(true);
|
||||
@@ -554,7 +569,7 @@ function ConquerCity($admin, $general, $city, $nation, $destnation) {
|
||||
}
|
||||
|
||||
//NPC인 경우 10% 확률로 임관(엔장, 인재, 의병)
|
||||
if($gen['npc'] >= 2 && $gen['npc'] <= 8 && rand() % 100 < 10) {
|
||||
if($admin['join_mode'] != 'onlyRandom' && $gen['npc'] >= 2 && $gen['npc'] <= 8 && rand() % 100 < 10) {
|
||||
$commissionCommand = EncodeCommand(0, 0, $nation['nation'], 25); //임관
|
||||
$query = "update general set turn0='$commissionCommand' where no={$gen['no']}";
|
||||
MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),"");
|
||||
@@ -615,11 +630,7 @@ function ConquerCity($admin, $general, $city, $nation, $destnation) {
|
||||
// 국가 삭제
|
||||
$query = "delete from nation where nation='{$city['nation']}'";
|
||||
MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),"");
|
||||
// 아까 얻어온 다굴국들 전방설정
|
||||
foreach($dipNations as $dip){
|
||||
//전방설정
|
||||
SetNationFront($dip);
|
||||
}
|
||||
$renewFront = true;
|
||||
// 멸망이 아니면
|
||||
} else {
|
||||
// 태수,군사,시중은 일반으로...
|
||||
@@ -717,6 +728,15 @@ function ConquerCity($admin, $general, $city, $nation, $destnation) {
|
||||
SetNationFront($conquerNation);
|
||||
}
|
||||
|
||||
if($renewFront){
|
||||
foreach(getAllNationStaticInfo() as $nation){
|
||||
if($nation['level'] <= 0){
|
||||
continue;
|
||||
}
|
||||
SetNationFront($nation['nation']);
|
||||
}
|
||||
}
|
||||
|
||||
pushGenLog($general, $log);
|
||||
pushGeneralPublicRecord($alllog, $year, $month);
|
||||
pushWorldHistory($history);
|
||||
|
||||
+38
-29
@@ -686,6 +686,9 @@ function command_25($turn, $command) {
|
||||
$connect=$db->get();
|
||||
$userID = Session::getUserID();
|
||||
|
||||
$gameStor->cacheValues(['year','startyear','month','join_mode']);
|
||||
|
||||
$onlyRandom = $gameStor->join_mode == 'onlyRandom';
|
||||
starter("임관");
|
||||
|
||||
$query = "select no,nations from general where owner='{$userID}'";
|
||||
@@ -700,11 +703,30 @@ function command_25($turn, $command) {
|
||||
|
||||
$nationList = $db->query('SELECT nation,`name`,color,scout,scoutmsg,gennum FROM nation ORDER BY rand()');
|
||||
|
||||
echo "
|
||||
foreach($nationList as $nation){
|
||||
if ($onlyRandom && TimeUtil::IsRangeMonth($gameStor->init_year, $gameStor->init_month, 1, $gameStor->year, $gameStor->month) && $nation['gennum'] >= GameConst::$initialNationGenLimitForRandInit) {
|
||||
$nation['availableJoin'] = false;
|
||||
}
|
||||
else if($gameStor->year < $gameStor->startyear+3 && $nation['gennum'] >= GameConst::$initialNationGenLimit){
|
||||
$nation['availableJoin'] = false;
|
||||
}
|
||||
else if($nation['scout'] == 1) {
|
||||
$nation['availableJoin'] = false;
|
||||
}
|
||||
else{
|
||||
$nation['availableJoin'] = true;
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
국가에 임관합니다.<br>
|
||||
이미 임관/등용되었던 국가는 다시 임관할 수 없습니다.<br>
|
||||
바로 군주의 위치로 이동합니다.<br>
|
||||
<?php if($onlyRandom): ?>
|
||||
랜덤 임관 대상 국가는 아래에서 확인할 수 있습니다.<br>
|
||||
<?php else: ?>
|
||||
임관할 국가를 목록에서 선택하세요.<br>
|
||||
<?php endif; ?>
|
||||
!!!는 방랑군을 포함한 랜덤임관입니다. 유니크를 기대하신다면!<br>
|
||||
???는 방랑군을 제외한 랜덤임관입니다. 유니크 혜택은 없습니다.<br>
|
||||
임관 금지이거나 초기 제한중인 국가는 붉은색 배경으로 표시됩니다.<br>
|
||||
@@ -712,37 +734,24 @@ function command_25($turn, $command) {
|
||||
<form name=form1 action=c_double.php method=post>
|
||||
<select name=double size=1 style=color:white;background-color:black>
|
||||
<option value=99 style=color:white;background-color:black;>!!!</option>
|
||||
<option value=98 style=color:white;background-color:black;>???</option>";
|
||||
<option value=98 style=color:white;background-color:black;>???</option>
|
||||
|
||||
$scoutStr = "";
|
||||
foreach($nationList as $nation){
|
||||
if($gameStor->year < $gameStor->startyear+3 && $nation['gennum'] >= GameConst::$initialNationGenLimit) {
|
||||
echo "
|
||||
<option value={$nation['nation']} style=color:{$nation['color']};background-color:red;>【 {$nation['name']} 】</option>";
|
||||
} elseif($nation['scout'] == 1) {
|
||||
echo "
|
||||
<option value={$nation['nation']} style=color:{$nation['color']};background-color:red;>【 {$nation['name']} 】</option>";
|
||||
} elseif(in_array($nation['nation'], $joinedNations)) {
|
||||
/*
|
||||
echo "
|
||||
<option value={$nation['nation']} style=color:{$nation['color']};background-color:red; disabled>【 {$nation['name']} 】</option>";
|
||||
*/
|
||||
} else {
|
||||
echo "
|
||||
<option value={$nation['nation']} style=color:{$nation['color']};>【 {$nation['name']} 】</option>";
|
||||
}
|
||||
}
|
||||
echo "
|
||||
<?php foreach($nationList as $nation): ?>
|
||||
<?php if($nation['availableJoin']): ?>
|
||||
<option value='<?=$nation['nation']?>' style='color:<?=$nation['color']?>;background-color:red;' <?=$onlyRandom?'disabled':''?>>【 <?=$nation['name']?> 】</option>
|
||||
<?php else: ?>
|
||||
<option value='<?=$nation['nation']?>' style='color:<?=$nation['color']?>;' <?=$onlyRandom?'disabled':''?>>【 <?=$nation['name']?> 】</option>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
|
||||
</select>
|
||||
<input type=submit value=임관>
|
||||
<input type=hidden name=command value=$command>";
|
||||
for($i=0; $i < count($turn); $i++) {
|
||||
echo "
|
||||
<input type=hidden name=turn[] value=$turn[$i]>";
|
||||
}
|
||||
|
||||
echo "
|
||||
</form>";
|
||||
<input type=hidden name=command value=<?=$command?>>
|
||||
<?php foreach(range(0, count(turn) - 1) as $i): ?>
|
||||
<input type=hidden name=turn[] value=<?=$turn[$i]?>>
|
||||
<?php endforeach; ?>
|
||||
</form>
|
||||
<?php
|
||||
echo getInvitationList($nationList);
|
||||
|
||||
ender();
|
||||
|
||||
@@ -40,7 +40,7 @@ class GameConstBase
|
||||
/** @var float 훈련시 사기 감소율*/
|
||||
public static $atmosSideEffectByTraining = 1;
|
||||
/** @var float 계략 기본 성공률*/
|
||||
public static $sabotageDefaultProb = 0.15;
|
||||
public static $sabotageDefaultProb = 0.35;
|
||||
/** @var int 계략시 확률 가중치(수치가 클수록 변화가 적음 : (지력차/$firing + $basefiring)*/
|
||||
public static $sabotageProbCoefByStat = 300;
|
||||
/** @var int 계략시 최소 수치 감소량*/
|
||||
@@ -74,6 +74,9 @@ class GameConstBase
|
||||
/** @var int 최대 기술 레벨 */
|
||||
public static $maxTechLevel = 12;
|
||||
|
||||
/** @var int 초기 제한시 장수 제한 */
|
||||
public static $initialNationGenLimitForRandInit = 3;
|
||||
|
||||
/** @var int 초기 제한시 장수 제한 */
|
||||
public static $initialNationGenLimit = 10;
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ class GameUnitConstBase{
|
||||
100, 100, 7, 0, 0, 99, 9,
|
||||
999999, null, null, 999999,
|
||||
[],//성벽은 공격할 수 없다.
|
||||
[],
|
||||
[self::T_FOOTMAN=>1.2],
|
||||
['성벽입니다.','생성할 수 없습니다.']
|
||||
],
|
||||
|
||||
|
||||
@@ -107,7 +107,8 @@ class ResetHelper{
|
||||
int $extend,
|
||||
int $npcmode,
|
||||
int $show_img_level,
|
||||
int $tournament_trig
|
||||
int $tournament_trig,
|
||||
string $join_mode
|
||||
):array{
|
||||
//FIXME: 분리할 것
|
||||
if(120 % $turnterm != 0){
|
||||
@@ -193,6 +194,8 @@ class ResetHelper{
|
||||
'startyear'=>$startyear,
|
||||
'year'=> $year,
|
||||
'month'=> $month,
|
||||
'init_year'=> $year,
|
||||
'init_month'=>$month,
|
||||
'map_theme' => $scenarioObj->getMapTheme(),
|
||||
'msg'=>'공지사항',//TODO:공지사항
|
||||
'maxgeneral'=>GameConst::$defaultMaxGeneral,
|
||||
@@ -207,6 +210,7 @@ class ResetHelper{
|
||||
'killturn'=>$killturn,
|
||||
'genius'=>GameConst::$defaultMaxGenius,
|
||||
'show_img_level'=>$show_img_level,
|
||||
'join_mode'=>$join_mode,
|
||||
'npcmode'=>$npcmode,
|
||||
'extended_general'=>$extend,
|
||||
'fiction'=>$fiction,
|
||||
|
||||
@@ -8,6 +8,9 @@ use \sammo\GameUnitConst;
|
||||
|
||||
class NPC{
|
||||
|
||||
public $generalID = null;
|
||||
public $realName = null;
|
||||
|
||||
public $affinity;
|
||||
public $name;
|
||||
public $picturePath;
|
||||
@@ -24,6 +27,16 @@ class NPC{
|
||||
public $charWar = 0;
|
||||
public $npc = 2;
|
||||
public $text;
|
||||
static $prefixList = [
|
||||
1 => 'ⓝ', //빙의 NPC
|
||||
2 => 'ⓝ', //NPC
|
||||
3 => 'ⓜ', //인탐 장수
|
||||
4 => 'ⓖ', //의병장(전략)
|
||||
5 => '㉥', //부대장
|
||||
6 => 'ⓤ', //unselectable npc, 빙의 불가 npc
|
||||
|
||||
9 => 'ⓞ', //오랑캐?
|
||||
];
|
||||
|
||||
//[ 1, "헌제",1002, 1, null, 17, 13, 61, 0, 170, 250, "안전", null, "산 넘어 산이로구나..."],
|
||||
public function __construct(
|
||||
@@ -156,7 +169,8 @@ class NPC{
|
||||
$charDomestic = 0;
|
||||
}
|
||||
|
||||
$name = 'ⓝ'.$this->name;
|
||||
$name = (static::$prefixList[$this->npc]?:'ⓧ').$this->name;
|
||||
$this->realName = $name;
|
||||
|
||||
$picturePath = $this->picturePath;
|
||||
if($env['show_img_level'] < 3){
|
||||
@@ -260,6 +274,7 @@ class NPC{
|
||||
'bornyear'=>$this->birth,
|
||||
'deadyear'=>$this->death
|
||||
]);
|
||||
$this->generalID = $db->insertId();
|
||||
|
||||
return true; //생성되었다.
|
||||
}
|
||||
|
||||
+15
-8
@@ -337,8 +337,8 @@ class WarUnit{
|
||||
$this->computeWarPower();
|
||||
}
|
||||
|
||||
function checkBattleBeginSkill():bool{
|
||||
return false;
|
||||
function checkBattleBeginSkill(){
|
||||
yield true;
|
||||
}
|
||||
|
||||
function checkBattleBeginItem():bool{
|
||||
@@ -353,6 +353,13 @@ class WarUnit{
|
||||
return $this->activatedSkill[$skillName] ?? false;
|
||||
}
|
||||
|
||||
function hasActivatedSkillOnLog(string $skillName):bool{
|
||||
if(key_exists($skillName, $this->logActivatedSkill)){
|
||||
return true;
|
||||
}
|
||||
return $this->hasActivatedSkill($skillName);
|
||||
}
|
||||
|
||||
function activateSkill(... $skillNames){
|
||||
foreach($skillNames as $skillName){
|
||||
$this->activatedSkill[$skillName] = true;
|
||||
@@ -365,16 +372,16 @@ class WarUnit{
|
||||
}
|
||||
}
|
||||
|
||||
function checkPreActiveSkill():bool{
|
||||
return false;
|
||||
function checkPreActiveSkill(){
|
||||
yield true;
|
||||
}
|
||||
|
||||
function checkActiveSkill():bool{
|
||||
return false;
|
||||
function checkActiveSkill(){
|
||||
yield true;
|
||||
}
|
||||
|
||||
function checkPostActiveSkill():bool{
|
||||
return false;
|
||||
function checkPostActiveSkill(){
|
||||
yield true;
|
||||
}
|
||||
|
||||
function applyActiveSkill(){
|
||||
|
||||
@@ -219,6 +219,9 @@ class WarUnitGeneral extends WarUnit{
|
||||
else if($specialWar == 61){
|
||||
$myWarPowerMultiply *= 1.10;
|
||||
}
|
||||
else if($specialWar == 62){
|
||||
$opposeWarPowerMultiply *= 0.95;
|
||||
}
|
||||
else if($specialWar == 50){
|
||||
if($this->isAttacker){
|
||||
$opposeWarPowerMultiply *= 0.9;
|
||||
@@ -346,22 +349,35 @@ class WarUnitGeneral extends WarUnit{
|
||||
}
|
||||
|
||||
///전투 개시 시에 작동하여 매 장수마다 작동하는 스킬
|
||||
function checkBattleBeginSkill():bool{
|
||||
$skillResult = false;
|
||||
function checkBattleBeginSkill(){
|
||||
$oppose = $this->getOppose();
|
||||
|
||||
$specialWar = $this->getSpecialWar();
|
||||
|
||||
if(
|
||||
$this->getCrewType()->armType == GameUnitConst::T_SIEGE &&
|
||||
$oppose->getCrewType()->armType == GameUnitConst::T_CASTLE
|
||||
){
|
||||
$this->activateSkill('부상무효');
|
||||
}
|
||||
yield true;
|
||||
|
||||
if($specialWar == 62){
|
||||
$oppose->activateSkill('저격불가');
|
||||
$this->activateSkill('부상무효');
|
||||
}
|
||||
yield true;
|
||||
|
||||
if (
|
||||
$specialWar == 70 &&
|
||||
$this->oppose instanceof WarUnitGeneral &&
|
||||
!$this->hasActivatedSkill('저격') &&
|
||||
!$this->hasActivatedSkill('저격불가') &&
|
||||
Util::randBool(1/3)
|
||||
) {
|
||||
$this->activateSkill('저격');
|
||||
$skillResult = true;
|
||||
}
|
||||
|
||||
return $skillResult;
|
||||
yield true;
|
||||
}
|
||||
|
||||
///전투 개시 시에 작동하여 매 장수마다 작동하는 아이템
|
||||
@@ -380,6 +396,7 @@ class WarUnitGeneral extends WarUnit{
|
||||
$item == 2 &&
|
||||
$this->oppose instanceof WarUnitGeneral &&
|
||||
!$this->hasActivatedSkill('저격') &&
|
||||
!$this->hasActivatedSkill('저격불가') &&
|
||||
Util::randBool(1/5)
|
||||
){
|
||||
//수극
|
||||
@@ -491,7 +508,7 @@ class WarUnitGeneral extends WarUnit{
|
||||
return $this->killed;
|
||||
}
|
||||
|
||||
function checkPreActiveSkill():bool{
|
||||
function checkPreActiveSkill(){
|
||||
$activated = false;
|
||||
|
||||
$oppose = $this->getOppose();
|
||||
@@ -499,34 +516,33 @@ class WarUnitGeneral extends WarUnit{
|
||||
$item = $this->getItem();
|
||||
$crewType = $this->getCrewType();
|
||||
|
||||
if($specialWar == 62){
|
||||
$oppose->activateSkill('필살불가');
|
||||
$oppose->activateSkill('위압불가');
|
||||
$oppose->activateSkill('격노불가');
|
||||
$oppose->activateSkill('계략약화');
|
||||
}
|
||||
yield true;
|
||||
|
||||
if(
|
||||
$specialWar == 63 &&
|
||||
$this->getPhase() == 0 &&
|
||||
$this->getHP() >= 1000 &&
|
||||
$this->getComputedAtmos() >= 90 &&
|
||||
$this->getComputedTrain() >= 90
|
||||
$this->getComputedTrain() >= 90 &&
|
||||
!$this->hasActivatedSkill('위압불가')
|
||||
){
|
||||
$this->activateSkill('위압');
|
||||
$activated = true;
|
||||
}
|
||||
|
||||
if($specialWar == 62){
|
||||
$oppose->activateSkill('필살불가');
|
||||
$oppose->activateSkill('계략약화');
|
||||
$activated = true;
|
||||
}
|
||||
yield true;
|
||||
|
||||
if($specialWar == 60){
|
||||
$oppose->activateSkill('회피불가');
|
||||
$oppose->activateSkill('저지불가');
|
||||
}
|
||||
|
||||
return $activated;
|
||||
yield true;
|
||||
}
|
||||
|
||||
function checkActiveSkill():bool{
|
||||
$activated = false;
|
||||
|
||||
function checkActiveSkill(){
|
||||
$oppose = $this->getOppose();
|
||||
$specialWar = $this->getSpecialWar();
|
||||
$item = $this->getItem();
|
||||
@@ -542,9 +558,9 @@ class WarUnitGeneral extends WarUnit{
|
||||
$ratio = $this->getComputedAtmos() + $this->getComputedTrain();
|
||||
if(Util::randBool($ratio / 400)){
|
||||
$this->activateSkill('특수', '저지');
|
||||
$activated = true;
|
||||
}
|
||||
}
|
||||
yield true;
|
||||
|
||||
if(
|
||||
!$this->hasActivatedSkill('특수') &&
|
||||
@@ -552,8 +568,8 @@ class WarUnitGeneral extends WarUnit{
|
||||
Util::randBool($this->getComputedCriticalRatio())
|
||||
){
|
||||
$this->activateSkill('특수', '필살시도', '필살');
|
||||
$activated = true;
|
||||
}
|
||||
yield true;
|
||||
|
||||
if(
|
||||
!$this->hasActivatedSkill('특수') &&
|
||||
@@ -561,8 +577,8 @@ class WarUnitGeneral extends WarUnit{
|
||||
Util::randBool($this->getComputedAvoidRatio())
|
||||
){
|
||||
$this->activateSkill('특수', '회피시도', '회피');
|
||||
$activated = true;
|
||||
}
|
||||
yield true;
|
||||
|
||||
//계략
|
||||
if($crewType->magicCoef){
|
||||
@@ -607,17 +623,16 @@ class WarUnitGeneral extends WarUnit{
|
||||
}
|
||||
}
|
||||
}
|
||||
yield true;
|
||||
|
||||
//의술
|
||||
if($specialWar == 73 && Util::randBool(0.2)){
|
||||
$this->activateSkill('치료');
|
||||
$activated = true;
|
||||
}
|
||||
|
||||
return $activated;
|
||||
yield true;
|
||||
}
|
||||
|
||||
function checkPostActiveSkill():bool{
|
||||
function checkPostActiveSkill(){
|
||||
$activated = false;
|
||||
|
||||
$oppose = $this->getOppose();
|
||||
@@ -625,7 +640,11 @@ class WarUnitGeneral extends WarUnit{
|
||||
$item = $this->getItem();
|
||||
$crewType = $this->getCrewType();
|
||||
|
||||
if($specialWar == 74 && $oppose->hasActivatedSkill('필살')){
|
||||
if(
|
||||
$specialWar == 74 &&
|
||||
$oppose->hasActivatedSkill('필살') &&
|
||||
!$this->hasActivatedSkill('격노불가')
|
||||
){
|
||||
if($this->isAttacker){
|
||||
if(Util::randBool(1/3)){
|
||||
$this->activateSkill('진노', '격노');
|
||||
@@ -643,10 +662,12 @@ class WarUnitGeneral extends WarUnit{
|
||||
}
|
||||
}
|
||||
}
|
||||
yield true;
|
||||
|
||||
if(
|
||||
$specialWar == 74 &&
|
||||
$oppose->hasActivatedSkill('회피')
|
||||
$oppose->hasActivatedSkill('회피') &&
|
||||
!$this->hasActivatedSkill('격노불가')
|
||||
){
|
||||
if($this->isAttacker){
|
||||
if(Util::randBool(1/3)){
|
||||
@@ -668,6 +689,7 @@ class WarUnitGeneral extends WarUnit{
|
||||
}
|
||||
}
|
||||
}
|
||||
yield true;
|
||||
|
||||
if(
|
||||
($item == 23 || $item == 24) &&
|
||||
@@ -677,6 +699,7 @@ class WarUnitGeneral extends WarUnit{
|
||||
$this->activateSkill('치료');
|
||||
$activated = true;
|
||||
}
|
||||
yield true;
|
||||
|
||||
//계략
|
||||
if(
|
||||
@@ -688,6 +711,7 @@ class WarUnitGeneral extends WarUnit{
|
||||
$oppose->deactivateSkill('계략');
|
||||
$activated = true;
|
||||
}
|
||||
yield true;
|
||||
|
||||
if(
|
||||
$specialWar == 42 &&
|
||||
@@ -695,6 +719,7 @@ class WarUnitGeneral extends WarUnit{
|
||||
){
|
||||
$this->warPowerMultiply *= 1.3;
|
||||
}
|
||||
yield true;
|
||||
|
||||
if(
|
||||
$specialWar == 43 &&
|
||||
@@ -702,8 +727,7 @@ class WarUnitGeneral extends WarUnit{
|
||||
){
|
||||
$this->warPowerMultiply *= 1.5;
|
||||
}
|
||||
|
||||
return $activated;
|
||||
yield true;
|
||||
}
|
||||
|
||||
function applyActiveSkill(){
|
||||
@@ -717,8 +741,8 @@ class WarUnitGeneral extends WarUnit{
|
||||
|
||||
if($this->hasActivatedSkill('저지')){
|
||||
|
||||
$this->addDex($oppose->getCrewType(), $oppose->getWarPower() * 0.5 * 0.9);
|
||||
$this->addDex($this->getCrewType(), $this->getWarPower() * 0.5 * 0.9);
|
||||
$this->addDex($oppose->getCrewType(), $oppose->getWarPower() * 0.9);
|
||||
$this->addDex($this->getCrewType(), $this->getWarPower() * 0.9);
|
||||
|
||||
$this->setWarPowerMultiply(0);
|
||||
$oppose->setWarPowerMultiply(0);
|
||||
@@ -784,7 +808,7 @@ class WarUnitGeneral extends WarUnit{
|
||||
foreach($table as $skillKey => $skillMultiply){
|
||||
if($oppose->hasActivatedSkill($skillKey)){
|
||||
$josaUl = \sammo\JosaUtil::pick($skillKey, '을');
|
||||
$thisLogger->pushGeneralBattleDetailLog("<C>●</><C>반계</>로 상대의 <D>{$skillKey}</>{$josaUl} 되돌렸다!");
|
||||
$thisLogger->pushGeneralBattleDetailLog("<C>반계</>로 상대의 <D>{$skillKey}</>{$josaUl} 되돌렸다!");
|
||||
$opposeLogger->pushGeneralBattleDetailLog("<D>{$skillKey}</>{$josaUl} <R>역으로</> 당했다!");
|
||||
|
||||
$this->multiplyWarPowerMultiply($skillMultiply);
|
||||
@@ -894,6 +918,9 @@ class WarUnitGeneral extends WarUnit{
|
||||
}
|
||||
|
||||
function tryWound():bool{
|
||||
if($this->hasActivatedSkillOnLog('부상무효')){
|
||||
return false;
|
||||
}
|
||||
if(!Util::randBool(0.05)){
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -13,27 +13,30 @@
|
||||
"npcMax":95
|
||||
},
|
||||
"general":[
|
||||
[0, "흑뢰의라이제", null, 0, null, 91, 71, 93, 0, 160, 300, null, null],
|
||||
[0, "성녀마리안", null, 0, null, 92, 71, 92, 0, 160, 300, null, null],
|
||||
[0, "팔방미녀세정", null, 0, null, 88, 87, 80, 0, 160, 300, null, null],
|
||||
[0, "빛나는아이오네", null, 0, null, 91, 93, 71, 0, 160, 300, null, null],
|
||||
[0, "포근한라유", null, 0, null, 92, 92, 71, 0, 160, 300, null, null],
|
||||
[0, "은랑의제로스", null, 0, null, 93, 91, 71, 0, 160, 300, null, null],
|
||||
[0, "흑뢰의라이제", null, 0, null, 91, 71, 93, 0, 160, 300, "의협", "집중"],
|
||||
[0, "성녀마리안", null, 0, null, 92, 71, 92, 0, 160, 300, null, "환술"],
|
||||
[0, "팔방미녀세정", null, 0, null, 88, 87, 80, 0, 160, 300, "출세", "위압"],
|
||||
[0, "빛나는아이오네", null, 0, null, 91, 93, 71, 0, 160, 300, "패권", "위압"],
|
||||
[0, "포근한라유", null, 0, null, 92, 92, 71, 0, 160, 300, null, "돌격"],
|
||||
[0, "은랑의제로스", null, 0, null, 93, 91, 71, 0, 160, 300, null, "무쌍"],
|
||||
[0, "악몽술사테리온", null, 0, null, 91, 71, 92, 0, 160, 300, null, null],
|
||||
[0, "마검의제시카", null, 0, null, 91, 92, 71, 0, 160, 300, null, null],
|
||||
[0, "분노의슈란", null, 0, null, 91, 92, 71, 0, 160, 300, null, null],
|
||||
[0, "영광의시아레스", null, 0, null, 92, 91, 71, 0, 160, 300, null, null],
|
||||
[0, "마검의제시카", null, 0, null, 91, 92, 71, 0, 160, 300, null, "기병"],
|
||||
[0, "분노의슈란", null, 0, null, 91, 92, 71, 0, 160, 300, null, "궁병"],
|
||||
[0, "영광의시아레스", null, 0, null, 92, 91, 71, 0, 160, 300, null, "돌격"],
|
||||
[0, "홍련의리오스", null, 0, null, 92, 91, 71, 0, 160, 300, null, null],
|
||||
[0, "붉은눈의마모아", null, 0, null, 87, 80, 86, 0, 160, 300, null, null],
|
||||
[0, "블리자드네로스", null, 0, null, 92, 90, 71, 0, 160, 300, null, null],
|
||||
[0, "혈광의바이넨", null, 0, null, 91, 92, 70, 0, 160, 300, null, null],
|
||||
[0, "붉은유성하네드", null, 0, null, 90, 93, 69, 0, 160, 300, null, null],
|
||||
[0, "아랍왕자애버스", null, 0, null, 86, 81, 85, 0, 160, 300, null, null],
|
||||
[0, "성검의알렌시아", null, 0, null, 91, 91, 70, 0, 160, 300, null, null],
|
||||
[0, "뉴페이스마르첼라", null, 0, null, 90, 70, 91, 0, 160, 300, null, null],
|
||||
[0, "환영의에루다", null, 0, null, 88, 73, 90, 0, 160, 300, null, null],
|
||||
[0, "마도사나나리", null, 0, null, 91, 70, 90, 0, 160, 300, null, null],
|
||||
[0, "의유혹의유메리아", null, 0, null, 86, 79, 86, 0, 160, 300, null, null],
|
||||
[0, "유혹의유메리아", null, 0, null, 86, 79, 86, 0, 160, 300, null, null],
|
||||
[0, "풍요로운오리헨", null, 0, null, 92, 91, 68, 0, 160, 300, null, null],
|
||||
[0, "리에하르", null, 0, null, 90, 68, 92, 0, 160, 300, null, null],
|
||||
[0, "유혹의리에하르", null, 0, null, 90, 68, 92, 0, 160, 300, null, null],
|
||||
[0, "용의기사카헬", null, 0, null, 87, 92, 71, 0, 160, 300, null, null],
|
||||
[0, "파괴의바스타레", null, 0, null, 85, 82, 82, 0, 160, 300, null, null],
|
||||
[0, "약속의글로리아", null, 0, null, 90, 92, 67, 0, 160, 300, null, null],
|
||||
[0, "차가운세피럴", null, 0, null, 88, 69, 91, 0, 160, 300, null, null],
|
||||
@@ -51,13 +54,15 @@
|
||||
[0, "약속의카엘", null, 0, null, 85, 89, 70, 0, 160, 300, null, null],
|
||||
[0, "신농가관지평", null, 0, null, 88, 66, 89, 0, 160, 300, null, null],
|
||||
[0, "귀여운리모네", null, 0, null, 85, 79, 79, 0, 160, 300, null, null],
|
||||
[0, "이데아", null, 0, null, 84, 89, 70, 0, 160, 300, null, null],
|
||||
[0, "공포의이데아", null, 0, null, 84, 89, 70, 0, 160, 300, null, null],
|
||||
[0, "현장복귀딘", null, 0, null, 86, 89, 68, 0, 160, 300, null, null],
|
||||
[0, "리나젤", null, 0, null, 86, 90, 67, 0, 160, 300, null, null],
|
||||
[0, "환수사세라자드", null, 0, null, 88, 89, 66, 0, 160, 300, null, null],
|
||||
[0, "섬광의마틸다", null, 0, null, 88, 92, 63, 0, 160, 300, null, null],
|
||||
[0, "건강한꼬마루피나", null, 0, null, 89, 92, 62, 0, 160, 300, null, null],
|
||||
[0, "마법상인비르시아", null, 0, null, 88, 72, 83, 0, 160, 300, null, null],
|
||||
[0, "신탁의하노아", null, 0, null, 89, 93, 60, 0, 160, 300, null, null],
|
||||
[0, "마룡의제크", null, 0, null, 88, 64, 90, 0, 160, 300, null, null],
|
||||
[0, "광역의블레악스", null, 0, null, 86, 67, 88, 0, 160, 300, null, null],
|
||||
[0, "유혹의나룬", null, 0, null, 80, 83, 78, 0, 160, 300, null, null],
|
||||
[0, "정령궁수모니카", null, 0, null, 86, 88, 67, 0, 160, 300, null, null],
|
||||
@@ -65,11 +70,11 @@
|
||||
[0, "저주의네로메루", null, 0, null, 86, 64, 90, 0, 160, 300, null, null],
|
||||
[0, "백련의마르센", null, 0, null, 86, 86, 68, 0, 160, 300, null, null],
|
||||
[0, "풍광의아이리", null, 0, null, 86, 88, 66, 0, 160, 300, null, null],
|
||||
[0, "세르펜스", null, 0, null, 89, 60, 90, 0, 160, 300, null, null],
|
||||
[0, "따뜻한세르펜스", null, 0, null, 89, 60, 90, 0, 160, 300, null, null],
|
||||
[0, "블랙어스카이로스", null, 0, null, 84, 85, 70, 0, 160, 300, null, null],
|
||||
[0, "분란의새논", null, 0, null, 87, 86, 65, 0, 160, 300, null, null],
|
||||
[0, "미소의아스트리아", null, 0, null, 82, 65, 90, 0, 160, 300, null, null],
|
||||
[0, "어맨더", null, 0, null, 83, 87, 67, 0, 160, 300, null, null],
|
||||
[0, "섬광의어맨더", null, 0, null, 83, 87, 67, 0, 160, 300, null, null],
|
||||
[0, "염사의라루아", null, 0, null, 84, 86, 66, 0, 160, 300, null, null],
|
||||
[0, "뿅망치에멜", null, 0, null, 86, 85, 65, 0, 160, 300, null, null],
|
||||
[0, "정보상인미세리아", null, 0, null, 86, 60, 89, 0, 160, 300, null, null],
|
||||
@@ -78,7 +83,7 @@
|
||||
[0, "찰나의아리스", null, 0, null, 82, 84, 69, 0, 160, 300, null, null],
|
||||
[0, "흑랑의리코스", null, 0, null, 83, 87, 65, 0, 160, 300, null, null],
|
||||
[0, "혈광의클라우드", null, 0, null, 84, 88, 63, 0, 160, 300, null, null],
|
||||
[0, "크리디", null, 0, null, 80, 64, 90, 0, 160, 300, null, null],
|
||||
[0, "미룡의크리디", null, 0, null, 80, 64, 90, 0, 160, 300, null, null],
|
||||
[0, "백작부인블론디", null, 0, null, 83, 86, 65, 0, 160, 300, null, null],
|
||||
[0, "기사렌티온", null, 0, null, 87, 88, 59, 0, 160, 300, null, null],
|
||||
[0, "셀루아", null, 0, null, 85, 83, 65, 0, 160, 300, null, null],
|
||||
@@ -86,13 +91,16 @@
|
||||
[0, "순백의티라", null, 0, null, 71, 72, 89, 0, 160, 300, null, null],
|
||||
[0, "교황의기사미라이", null, 0, null, 86, 90, 56, 0, 160, 300, null, null],
|
||||
[0, "맹독의멀독", null, 0, null, 85, 83, 63, 0, 160, 300, null, null],
|
||||
[0, "트롤왕레우기", null, 0, null, 84, 87, 60, 0, 160, 300, null, null],
|
||||
[0, "데이나", null, 0, null, 77, 63, 90, 0, 160, 300, null, null],
|
||||
[0, "바람의무희네바", null, 0, null, 83, 59, 88, 0, 160, 300, null, null],
|
||||
[0, "웨스트윈드아모스", null, 0, null, 73, 70, 87, 0, 160, 300, null, null],
|
||||
[0, "루치아", null, 0, null, 83, 80, 67, 0, 160, 300, null, null],
|
||||
[0, "하이나", null, 0, null, 81, 57, 91, 0, 160, 300, null, null],
|
||||
[0, "환상의사히누스", null, 0, null, 86, 66, 78, 0, 160, 300, null, null],
|
||||
[0, "애정의하이나", null, 0, null, 81, 57, 91, 0, 160, 300, null, null],
|
||||
[0, "스니퍼", null, 0, null, 78, 81, 70, 0, 160, 300, null, null],
|
||||
[0, "오마주", null, 0, null, 82, 87, 60, 0, 160, 300, null, null],
|
||||
[0, "분노의오마주", null, 0, null, 82, 87, 60, 0, 160, 300, null, null],
|
||||
[0, "초록늑대데빌레덤", null, 0, null, 78, 79, 72, 0, 160, 300, null, null],
|
||||
[0, "이로아스", null, 0, null, 82, 76, 70, 0, 160, 300, null, null],
|
||||
[0, "광살의에반", null, 0, null, 80, 88, 60, 0, 160, 300, null, null],
|
||||
[0, "카밀라", null, 0, null, 86, 49, 92, 0, 160, 300, null, null],
|
||||
@@ -100,19 +108,25 @@
|
||||
[0, "바람의레지나", null, 0, null, 77, 64, 86, 0, 160, 300, null, null],
|
||||
[0, "부정의퓨니엄", null, 0, null, 76, 74, 75, 0, 160, 300, null, null],
|
||||
[0, "흑묘샬롯", null, 0, null, 81, 88, 56, 0, 160, 300, null, null],
|
||||
[0, "현자제노에오스", null, 0, null, 78, 58, 89, 0, 160, 300, null, null],
|
||||
[0, "돌풍의루파로", null, 0, null, 78, 66, 81, 0, 160, 300, null, null],
|
||||
[0, "쥬곤을찾는만텔", null, 0, null, 76, 80, 67, 0, 160, 300, null, null],
|
||||
[0, "날뒤는알트", null, 0, null, 73, 62, 87, 0, 160, 300, null, null],
|
||||
[0, "날뛰는알트", null, 0, null, 73, 62, 87, 0, 160, 300, null, null],
|
||||
[0, "달아오른슈롯셔", null, 0, null, 80, 64, 78, 0, 160, 300, null, null],
|
||||
[0, "로즈", null, 0, null, 72, 63, 86, 0, 160, 300, null, null],
|
||||
[0, "막내카라", null, 0, null, 75, 67, 79, 0, 160, 300, null, null],
|
||||
[0, "리치네", null, 0, null, 78, 65, 78, 0, 160, 300, null, null],
|
||||
[0, "블랙하운드암푸스", null, 0, null, 74, 80, 66, 0, 160, 300, null, null],
|
||||
[0, "화염늑대데빌리덤", null, 0, null, 77, 81, 62, 0, 160, 300, null, null],
|
||||
[0, "검은늑대데빌사울", null, 0, null, 70, 84, 66, 0, 160, 300, null, null],
|
||||
[0, "흑월의리베스", null, 0, null, 73, 55, 91, 0, 160, 300, null, null],
|
||||
[0, "혁명의브렉", null, 0, null, 76, 77, 66, 0, 160, 300, null, null],
|
||||
[0, "에클레", null, 0, null, 73, 83, 63, 0, 160, 300, null, null],
|
||||
[0, "만텔을쫒는에클레", null, 0, null, 73, 83, 63, 0, 160, 300, null, null],
|
||||
[0, "타베나", null, 0, null, 77, 80, 62, 0, 160, 300, null, null],
|
||||
[0, "복수의이벤", null, 0, null, 72, 58, 88, 0, 160, 300, null, null],
|
||||
[0, "심연의베르", null, 0, null, 72, 79, 67, 0, 160, 300, null, null],
|
||||
[0, "탐험가로이", null, 0, null, 73, 78, 67, 0, 160, 300, null, null],
|
||||
[0, "독장미야나가", null, 0, null, 67, 86, 65, 0, 160, 300, null, null],
|
||||
[0, "미녀기사쥬니아", null, 0, null, 71, 66, 80, 0, 160, 300, null, null],
|
||||
[0, "스토마", null, 0, null, 69, 69, 79, 0, 160, 300, null, null],
|
||||
[0, "리사", null, 0, null, 70, 71, 76, 0, 160, 300, null, null],
|
||||
@@ -124,18 +138,24 @@
|
||||
[0, "귀환의류나드", null, 0, null, 80, 84, 52, 0, 160, 300, null, null],
|
||||
[0, "라타", null, 0, null, 73, 69, 73, 0, 160, 300, null, null],
|
||||
[0, "정령기사한나", null, 0, null, 70, 73, 72, 0, 160, 300, null, null],
|
||||
[0, "원스", null, 0, null, 77, 82, 56, 0, 160, 300, null, null],
|
||||
[0, "빙결체이서원스", null, 0, null, 77, 82, 56, 0, 160, 300, null, null],
|
||||
[0, "초고속승진리체", null, 0, null, 81, 47, 87, 0, 160, 300, null, null],
|
||||
[0, "푸른늑대데빌덤", null, 0, null, 82, 78, 55, 0, 160, 300, null, null],
|
||||
[0, "악동듀요라", null, 0, null, 76, 69, 69, 0, 160, 300, null, null],
|
||||
[0, "에렌디라", null, 0, null, 82, 80, 52, 0, 160, 300, null, null],
|
||||
[0, "아이스트롤레쿠", null, 0, null, 66, 70, 78, 0, 160, 300, null, null],
|
||||
[0, "파이어트롤레키", null, 0, null, 59, 73, 82, 0, 160, 300, null, null],
|
||||
[0, "은발의야마르", null, 0, null, 73, 73, 67, 0, 160, 300, null, null],
|
||||
[0, "하늘의콜리나", null, 0, null, 74, 77, 62, 0, 160, 300, null, null],
|
||||
[0, "그림자라이넌", null, 0, null, 70, 59, 84, 0, 160, 300, null, null],
|
||||
[0, "어스트롤레크", null, 0, null, 61, 72, 80, 0, 160, 300, null, null],
|
||||
[0, "루이칸", null, 0, null, 73, 66, 73, 0, 160, 300, null, null],
|
||||
[0, "레이첼", null, 0, null, 70, 68, 73, 0, 160, 300, null, null],
|
||||
[0, "테이머존덜링", null, 0, null, 67, 72, 72, 0, 160, 300, null, null],
|
||||
[0, "엘린", null, 0, null, 74, 48, 88, 0, 160, 300, null, null],
|
||||
[0, "에포스", null, 0, null, 76, 48, 86, 0, 160, 300, null, null],
|
||||
[0, "아미르", null, 0, null, 69, 64, 77, 0, 160, 300, null, null],
|
||||
[0, "에타", null, 0, null, 72, 62, 76, 0, 160, 300, null, null],
|
||||
[0, "혹한의에타", null, 0, null, 72, 62, 76, 0, 160, 300, null, null],
|
||||
[0, "정령술사도루숨", null, 0, null, 72, 64, 74, 0, 160, 300, null, null],
|
||||
[0, "차녀카미안", null, 0, null, 72, 76, 62, 0, 160, 300, null, null],
|
||||
[0, "질니아", null, 0, null, 74, 80, 56, 0, 160, 300, null, null],
|
||||
@@ -146,9 +166,11 @@
|
||||
[0, "폭주의란도루", null, 0, null, 70, 51, 87, 0, 160, 300, null, null],
|
||||
[0, "웬디", null, 0, null, 72, 77, 59, 0, 160, 300, null, null],
|
||||
[0, "찰스", null, 0, null, 72, 83, 53, 0, 160, 300, null, null],
|
||||
[0, "윈드트롤레칸", null, 0, null, 57, 75, 76, 0, 160, 300, null, null],
|
||||
[0, "산들바람젤레", null, 0, null, 68, 52, 87, 0, 160, 300, null, null],
|
||||
[0, "요하스", null, 0, null, 69, 65, 73, 0, 160, 300, null, null],
|
||||
[0, "아이노", null, 0, null, 72, 70, 65, 0, 160, 300, null, null],
|
||||
[0, "욕망의타실리", null, 0, null, 67, 54, 86, 0, 160, 300, null, null],
|
||||
[0, "홍염의케이시", null, 0, null, 76, 42, 88, 0, 160, 300, null, null],
|
||||
[0, "소태우스", null, 0, null, 72, 70, 64, 0, 160, 300, null, null],
|
||||
[0, "빙혼마도사카이", null, 0, null, 74, 43, 88, 0, 160, 300, null, null],
|
||||
@@ -169,6 +191,7 @@
|
||||
[0, "레이븐", null, 0, null, 69, 43, 86, 0, 160, 300, null, null],
|
||||
[0, "투말린", null, 0, null, 72, 69, 57, 0, 160, 300, null, null],
|
||||
[0, "게르고", null, 0, null, 72, 69, 57, 0, 160, 300, null, null],
|
||||
[0, "욕망의로이즈", null, 0, null, 73, 37, 88, 0, 160, 300, null, null],
|
||||
[0, "로렌시아", null, 0, null, 70, 42, 85, 0, 160, 300, null, null],
|
||||
[0, "달라니카", null, 0, null, 65, 59, 73, 0, 160, 300, null, null],
|
||||
[0, "레이나", null, 0, null, 66, 56, 74, 0, 160, 300, null, null],
|
||||
@@ -176,10 +199,12 @@
|
||||
[0, "안테리나", null, 0, null, 67, 68, 61, 0, 160, 300, null, null],
|
||||
[0, "쿠벤", null, 0, null, 67, 69, 60, 0, 160, 300, null, null],
|
||||
[0, "이고트리", null, 0, null, 70, 73, 53, 0, 160, 300, null, null],
|
||||
[0, "데브라", null, 0, null, 74, 73, 49, 0, 160, 300, null, null],
|
||||
[0, "데비오스", null, 0, null, 74, 73, 49, 0, 160, 300, null, null],
|
||||
[0, "리로븐", null, 0, null, 67, 48, 81, 0, 160, 300, null, null],
|
||||
[0, "칼리오스", null, 0, null, 58, 53, 84, 0, 160, 300, null, null],
|
||||
[0, "시몬느", null, 0, null, 67, 56, 72, 0, 160, 300, null, null],
|
||||
[0, "추적자주혜", null, 0, null, 67, 73, 55, 0, 160, 300, null, null],
|
||||
[0, "크리슈아", null, 0, null, 78, 85, 32, 0, 160, 300, null, null],
|
||||
[0, "감시자록시", null, 0, null, 58, 56, 80, 0, 160, 300, null, null],
|
||||
[0, "모멘트", null, 0, null, 61, 70, 63, 0, 160, 300, null, null],
|
||||
[0, "올거", null, 0, null, 58, 76, 60, 0, 160, 300, null, null],
|
||||
@@ -187,6 +212,7 @@
|
||||
[0, "비랑카", null, 0, null, 65, 70, 59, 0, 160, 300, null, null],
|
||||
[0, "야쿠", null, 0, null, 66, 69, 59, 0, 160, 300, null, null],
|
||||
[0, "휘크리스", null, 0, null, 70, 74, 50, 0, 160, 300, null, null],
|
||||
[0, "엔사그니", null, 0, null, 67, 72, 55, 0, 160, 300, null, null],
|
||||
[0, "사랑찾는메리아", null, 0, null, 68, 39, 86, 0, 160, 300, null, null],
|
||||
[0, "에벨라", null, 0, null, 62, 46, 85, 0, 160, 300, null, null],
|
||||
[0, "아리엔", null, 0, null, 72, 44, 77, 0, 160, 300, null, null],
|
||||
@@ -213,6 +239,7 @@
|
||||
[0, "모리웬", null, 0, null, 69, 64, 54, 0, 160, 300, null, null],
|
||||
[0, "메니아드", null, 0, null, 67, 67, 53, 0, 160, 300, null, null],
|
||||
[0, "담금질중인칸나", null, 0, null, 75, 84, 28, 0, 160, 300, null, null],
|
||||
[0, "약제사미리움", null, 0, null, 65, 47, 75, 0, 160, 300, null, null],
|
||||
[0, "장녀카나안", null, 0, null, 70, 32, 84, 0, 160, 300, null, null],
|
||||
[0, "하나리", null, 0, null, 59, 62, 65, 0, 160, 300, null, null],
|
||||
[0, "하로데스", null, 0, null, 70, 72, 44, 0, 160, 300, null, null],
|
||||
@@ -268,6 +295,7 @@
|
||||
[0, "토리스", null, 0, null, 58, 43, 73, 0, 160, 300, null, null],
|
||||
[0, "하카세", null, 0, null, 60, 62, 52, 0, 160, 300, null, null],
|
||||
[0, "모코", null, 0, null, 65, 70, 39, 0, 160, 300, null, null],
|
||||
[0, "고트리시", null, 0, null, 53, 47, 74, 0, 160, 300, null, null],
|
||||
[0, "크루피사냐", null, 0, null, 53, 46, 74, 0, 160, 300, null, null],
|
||||
[0, "에리키오먼", null, 0, null, 63, 38, 72, 0, 160, 300, null, null],
|
||||
[0, "로윈터", null, 0, null, 53, 64, 56, 0, 160, 300, null, null],
|
||||
@@ -292,6 +320,7 @@
|
||||
[0, "에스텔", null, 0, null, 59, 64, 47, 0, 160, 300, null, null],
|
||||
[0, "가리먼", null, 0, null, 65, 66, 39, 0, 160, 300, null, null],
|
||||
[0, "타나소스", null, 0, null, 69, 62, 39, 0, 160, 300, null, null],
|
||||
[0, "안티오그나", null, 0, null, 28, 65, 77, 0, 160, 300, null, null],
|
||||
[0, "에리사", null, 0, null, 53, 43, 73, 0, 160, 300, null, null],
|
||||
[0, "미라시아", null, 0, null, 58, 39, 72, 0, 160, 300, null, null],
|
||||
[0, "루카", null, 0, null, 58, 63, 48, 0, 160, 300, null, null],
|
||||
@@ -327,7 +356,20 @@
|
||||
[0, "알리오나", null, 0, null, 53, 34, 72, 0, 160, 300, null, null],
|
||||
[0, "케시아", null, 0, null, 53, 71, 32, 0, 160, 300, null, null],
|
||||
[0, "듀라스", null, 0, null, 48, 26, 78, 0, 160, 300, null, null],
|
||||
[0, "아카시", null, 0, null, 48, 29, 74, 0, 160, 300, null, null]
|
||||
[0, "아카시", null, 0, null, 48, 29, 74, 0, 160, 300, null, null],
|
||||
[0, "코르코슘", null, 0, null, 32, 35, 80, 0, 160, 300, null, null],
|
||||
[0, "휴식의카소아샤", null, 0, null, 33, 77, 33, 0, 160, 300, null, null],
|
||||
[0, "요청의가스우세", null, 0, null, 33, 33, 77, 0, 160, 300, null, null],
|
||||
[0, "칼로아눈", null, 0, null, 39, 19, 82, 0, 160, 300, null, null],
|
||||
[0, "나시루", null, 0, null, 28, 21, 78, 0, 160, 300, null, null],
|
||||
[0, "비로", null, 0, null, 54, 43, 74, 0, 160, 300, null, null],
|
||||
[0, "어둠의질레르", null, 0, null, 68, 65, 87, 0, 160, 300, null, null],
|
||||
[0, "암흑도둑에베넌", null, 0, null, 67, 65, 78, 0, 160, 300, null, null],
|
||||
[0, "뛰노는차이치", null, 0, null, 64, 52, 69, 0, 160, 300, null, null],
|
||||
[0, "운명의페인트", null, 0, null, 82, 63, 84, 0, 160, 300, null, null],
|
||||
[0, "케로베로스바이라", null, 0, null, 79, 73, 72, 0, 160, 300, null, null],
|
||||
[0, "대장장이시이로", null, 0, null, 78, 67, 54, 0, 160, 300, null, null],
|
||||
[0, "병기참모프레인", null, 0, null, 88, 60, 77, 0, 160, 300, null, null]
|
||||
],
|
||||
"general_ex":[
|
||||
|
||||
@@ -336,7 +378,7 @@
|
||||
|
||||
],
|
||||
"history":[
|
||||
"<C>●</>180년 1월:<L><b>【이벤트】</b></>제작자: 촉구.",
|
||||
"<C>●</>180년 1월:<L><b>【이벤트】</b></>제작자: 촉구, 관지평",
|
||||
"<C>●</>180년 1월:<L><b>【가상모드13】</b>소울소드</>",
|
||||
"<C>●</>180년 1월:<L><b>【이벤트】</b></>혼란스러운 세상을 영웅들과 함께 바로잡아라!"
|
||||
],
|
||||
|
||||
@@ -19,7 +19,7 @@ class GameUnitConst extends GameUnitConstBase
|
||||
100, 100, 7, 0, 0, 99, 9,
|
||||
999999, null, null, 999999,
|
||||
[],//성벽은 공격할 수 없다.
|
||||
[],
|
||||
[self::T_FOOTMAN=>1.2],
|
||||
['성벽입니다.','생성할 수 없습니다.']
|
||||
],
|
||||
|
||||
|
||||
+2
-2
@@ -627,8 +627,8 @@ CREATE TABLE IF NOT EXISTS `emperior` (
|
||||
`l6pic` CHAR(32) NULL DEFAULT '',
|
||||
`l5name` CHAR(64) NULL DEFAULT '',
|
||||
`l5pic` CHAR(32) NULL DEFAULT '',
|
||||
`tiger` CHAR(64) NULL DEFAULT '',
|
||||
`eagle` CHAR(64) NULL DEFAULT '',
|
||||
`tiger` VARCHAR(128) NULL DEFAULT '',
|
||||
`eagle` VARCHAR(128) NULL DEFAULT '',
|
||||
`gen` TEXT NULL DEFAULT '',
|
||||
`history` MEDIUMTEXT NULL DEFAULT '',
|
||||
`aux` MEDIUMTEXT NULL DEFAULT '' COMMENT 'json',
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<div class="world_map map_theme_<?=$mapTheme?> draw_required">
|
||||
<div class="map_title">
|
||||
<span class="map_title_text">
|
||||
<div class="map_title obj_tooltip" data-toggle="tooltip" data-placement="top" data-tooltip-class="map_title_tooltiptext">
|
||||
<span class="map_title_text ">
|
||||
</span>
|
||||
<span class="tooltiptext"></span>
|
||||
</div>
|
||||
<div class="map_body">
|
||||
<div class="map_bglayer1"></div>
|
||||
|
||||
@@ -12,6 +12,7 @@ $db = RootDB::db();
|
||||
$notice = $db->queryFirstField('SELECT `NOTICE` FROM `system` WHERE `NO`=1');
|
||||
$userGrade = $session->userGrade;
|
||||
$acl = $session->acl;
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
@@ -34,8 +35,6 @@ $acl = $session->acl;
|
||||
<?=WebUtil::printJS('../e_lib/bootstrap.bundle.min.js')?>
|
||||
<?=WebUtil::printJS('../js/func.js')?>
|
||||
<?=WebUtil::printJS('../js/entrance.js')?>
|
||||
<?=WebUtil::printJS('../d_shared/menu.js')?>
|
||||
<?=WebUtil::printJS('../js/title.js')?>
|
||||
|
||||
<?php if ($userGrade >= 5 || $acl): ?>
|
||||
<!-- 운영자 -->
|
||||
@@ -53,6 +52,7 @@ $acl = $session->acl;
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav">
|
||||
<?=WebUtil::drawMenu(ROOT.'/d_shared/menu.json')?>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -9,7 +9,7 @@ $userID = Session::getUserID();
|
||||
// 외부 파라미터
|
||||
|
||||
$db = RootDB::db();
|
||||
$member = $db->queryFirstRow('SELECT `id`, `name`, `grade`, `picture`, reg_date, third_use, acl FROM `member` WHERE `NO` = %i', $userID);
|
||||
$member = $db->queryFirstRow('SELECT `id`, `name`, `grade`, `picture`, reg_date, third_use, acl, oauth_type, token_valid_until FROM `member` WHERE `NO` = %i', $userID);
|
||||
|
||||
if(!$member['picture']){
|
||||
$picture = ServConfig::getSharedIconPath().'/default.jpg';
|
||||
@@ -30,6 +30,8 @@ else{
|
||||
}
|
||||
}
|
||||
|
||||
$tokenValidUntil = $member['token_valid_until'];
|
||||
|
||||
if($member['grade'] == 6) {
|
||||
$grade = '운영자';
|
||||
} elseif($member['grade'] == 5) {
|
||||
@@ -81,5 +83,8 @@ Json::die([
|
||||
'global_salt'=>RootDB::getGlobalSalt(),
|
||||
'join_date'=>$member['reg_date'],
|
||||
'third_use'=>($member['third_use']!=0),
|
||||
'acl'=>$acl
|
||||
'acl'=>$acl,
|
||||
'oauth_type'=>$member['oauth_type'],
|
||||
'token_valid_until'=>$tokenValidUntil
|
||||
|
||||
]);
|
||||
|
||||
@@ -23,10 +23,9 @@ require(__dir__.'/../vendor/autoload.php');
|
||||
<?=WebUtil::printJS('../e_lib/jquery-3.3.1.min.js')?>
|
||||
<?=WebUtil::printJS('../e_lib/bootstrap.bundle.min.js')?>
|
||||
<?=WebUtil::printJS('../e_lib/sha512.min.js')?>
|
||||
<?=WebUtil::printJS('../e_lib/moment.min.js')?>
|
||||
<?=WebUtil::printJS('../js/func.js')?>
|
||||
<?=WebUtil::printJS('../js/user_info.js')?>
|
||||
<?=WebUtil::printJS('../d_shared/menu.js')?>
|
||||
<?=WebUtil::printJS('../js/title.js')?>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
@@ -36,6 +35,7 @@ require(__dir__.'/../vendor/autoload.php');
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav">
|
||||
<?=WebUtil::drawMenu(ROOT.'/d_shared/menu.json')?>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
@@ -87,6 +87,14 @@ require(__dir__.'/../vendor/autoload.php');
|
||||
</td>
|
||||
<td colspan="3">개인정보 3자 제공 동의 : <span id="slot_third_use"></span><button type="button" id="third_use_disallow">철회</button></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="bg1">인증 방식</th>
|
||||
<td colspan="2">
|
||||
<span id="slot_oauth_type"></span>
|
||||
</td>
|
||||
<td colspan="3">
|
||||
<span id="slot_token_valid_until"></span>까지 유효<button type="button" id="expand_login_token">초기화</button></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="bg1"></th>
|
||||
<th class="bg1" colspan="2">회원 탈퇴</th>
|
||||
|
||||
@@ -30,12 +30,16 @@ if ($session->isLoggedIn()) {
|
||||
<?=WebUtil::printJS('e_lib/bootstrap.bundle.min.js')?>
|
||||
<?=WebUtil::printJS('e_lib/jquery.validate.min.js')?>
|
||||
<?=WebUtil::printJS('e_lib/sha512.min.js')?>
|
||||
<?=WebUtil::printJS('d_shared/menu.js')?>
|
||||
<?=WebUtil::printJS('js/common.js')?>
|
||||
<?=WebUtil::printJS('js/login.js')?>
|
||||
<?=WebUtil::printJS('js/title.js')?>
|
||||
<?=WebUtil::printCSS('e_lib/bootstrap.min.css')?>
|
||||
<?=WebUtil::printCSS('css/login.css')?>
|
||||
<meta name="description" content="실시간으로 진행되는 삼국지 웹게임(삼모전)입니다">
|
||||
<meta name="keywords" content="삼국지,삼모전,웹게임,힏체,힏체섭,히데체,히데체섭,HiDCHe,체섭">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:title" content="삼국지 모의전투 HiDCHe">
|
||||
<meta property="og:description" content="실시간으로 진행되는 삼국지 웹게임(삼모전)입니다">
|
||||
<meta property="og:url" content="https://sam.hided.net">
|
||||
<script>
|
||||
|
||||
var oauthMode = null;
|
||||
@@ -64,8 +68,17 @@ function getOAuthToken(mode, scope_list){
|
||||
|
||||
function sendTempPasswordToKakaoTalk(){
|
||||
$.post({
|
||||
url:'j_change_pw.php',
|
||||
url:'oauth_kakao/j_login_oauth.php',
|
||||
dataType:'json'
|
||||
}).then(function(obj){
|
||||
var t = $.Deferred();
|
||||
if(!obj.result){
|
||||
t.reject();
|
||||
}
|
||||
return $.post({
|
||||
url:'oauth_kakao/j_change_pw.php',
|
||||
dataType:'json'
|
||||
});
|
||||
}).then(function(obj){
|
||||
if(!obj.result){
|
||||
alert(obj.reason);
|
||||
@@ -81,12 +94,18 @@ function doLoginUsingOAuth(){
|
||||
url:'oauth_kakao/j_login_oauth.php',
|
||||
dataType:'json'
|
||||
}).then(function(obj){
|
||||
if(!obj.result){
|
||||
alert(obj.reason);
|
||||
}
|
||||
else{
|
||||
if(obj.result){
|
||||
window.location.href = "./";
|
||||
return;
|
||||
}
|
||||
if(!obj.reqOTP){
|
||||
alert(obj.reason);
|
||||
return;
|
||||
}
|
||||
$('#modalOTP').modal().on('shown.bs.modal', function(){
|
||||
$('#otp_code').focus();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@@ -94,6 +113,9 @@ function postOAuthResult(result){
|
||||
if(result == 'join'){
|
||||
window.location.href = 'oauth_kakao/join.php';
|
||||
}
|
||||
else if(result == 'req_email'){
|
||||
alert('이메일 정보 공유를 허가해 주셔야 합니다.');
|
||||
}
|
||||
else if(result == 'login'){
|
||||
console.log('로그인모드');
|
||||
if(oauthMode=='change_pw'){
|
||||
@@ -117,6 +139,7 @@ function postOAuthResult(result){
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav">
|
||||
<?=WebUtil::drawMenu(ROOT.'/d_shared/menu.json')?>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
@@ -126,7 +149,7 @@ function postOAuthResult(result){
|
||||
<h1 class="row justify-content-md-center">삼국지 모의전투 HiDCHe</h1>
|
||||
<div class="row justify-content-md-center">
|
||||
<div class="col" style="max-width:450px;">
|
||||
<div class="card">
|
||||
<div class="card" id="login_card">
|
||||
<h3 class="card-header">
|
||||
로그인
|
||||
</h3>
|
||||
@@ -152,29 +175,62 @@ function postOAuthResult(result){
|
||||
|
||||
<input type="hidden" id="global_salt" name="global_salt" value="<?=RootDB::getGlobalSalt()?>">
|
||||
<div class="form-group row">
|
||||
<div class="col-5 col-md-4 " style="position:relative;"><button type="button" onclick="getOAuthToken('login');" id="btn_kakao_login" title="카카오톡으로 가입&로그인"></button></div>
|
||||
<div class="col-5 col-md-4 " style="position:relative;"><button type="button" onclick="getOAuthToken('login', ['account_email','talk_message']);" id="btn_kakao_login" title="카카오톡으로 가입&로그인"></button></div>
|
||||
<div class="col-7 col-md-8">
|
||||
<button type="submit" class="btn btn-primary btn-lg btn-block login-button">로그인</button>
|
||||
<div class="btn-group btn-group-lg d-flex login_btn_group" role="group">
|
||||
<button type="submit" class="btn btn-primary login-button w-100">로그인</button>
|
||||
<div class="btn-group" role="group">
|
||||
<button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> </button>
|
||||
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="btnGroupDrop1">
|
||||
<a class="dropdown-item" href="javascript:getOAuthToken('change_pw', 'talk_message');">비밀번호 초기화</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<!--
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-4"><a href="javascript:getOAuthToken('change_pw', 'talk_message');">비밀번호 찾기<img src="oauth_kakao/kakao_to_me.png"></a></div>
|
||||
<div class="col-sm-8">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="bottom_box">
|
||||
<div class="container"><a href="terms.2.html">개인정보처리방침</a> & <a href="terms.1.html">이용약관</a><br>© 2018 • HideD
|
||||
<br>크롬과 파이어폭스에 최적화되어있습니다.</div></div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="modalOTP" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<form id="otp_form" method="post" action="#">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="exampleModalLabel">인증 코드 필요</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div>
|
||||
인증 코드가 필요합니다.<br><br>
|
||||
카카오톡의 '나와의 채팅'란을 확인해 주세요.<br>
|
||||
(별도의 알림[소리, 진동, 숫자]이 발생하지 않습니다.)
|
||||
</div>
|
||||
<div class="input-group mt-4" role="group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">인증 코드</span>
|
||||
</div>
|
||||
<input type="number" class="form-control" name='otp' id="otp_code" placeholder="인증 코드">
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">취소</button>
|
||||
<button type="submit" class="btn btn-primary">제출</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+100
-2
@@ -2,7 +2,9 @@
|
||||
namespace sammo;
|
||||
|
||||
require(__dir__.'/vendor/autoload.php');
|
||||
require(__dir__.'/oauth_kakao/lib.join.php');
|
||||
|
||||
use \kakao\Kakao_REST_API_Helper as Kakao_REST_API_Helper;
|
||||
|
||||
$RootDB = RootDB::db();
|
||||
$session = Session::getInstance();
|
||||
@@ -16,12 +18,92 @@ $password = Util::getReq('password');
|
||||
if(!$username || !$password){
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reqOTP'=>false,
|
||||
'reason'=>'올바르지 않은 입력입니다.'
|
||||
]);
|
||||
}
|
||||
|
||||
function kakaoOAuthCheck(array $userInfo) : ?array {
|
||||
|
||||
if(!\kakao\KakaoKey::REST_KEY){
|
||||
return [false, '카카오 API 앱이 등록되지 않았습니다. 관리자에게 문의해 주세요.'];
|
||||
}
|
||||
|
||||
$oauthID = $userInfo['oauth_id'];
|
||||
$oauthInfo = Json::decode($userInfo['oauth_info'])??[];
|
||||
if(!$oauthInfo){
|
||||
return [false, 'OAuth 정보가 보관되어 있지 않습니다. 카카오 로그인을 수행해 주세요.'];
|
||||
}
|
||||
|
||||
$accessToken = $oauthInfo['accessToken']??null;
|
||||
$refreshToken = $oauthInfo['refreshToken']??null;
|
||||
$accessTokenValidUntil = $oauthInfo['accessTokenValidUntil']??null;
|
||||
$refreshTokenValidUntil = $oauthInfo['refreshTokenValidUntil']??null;
|
||||
$OTPValue = $oauthInfo['OTPValue']??null;
|
||||
$OTPTrialUntil = $oauthInfo['OTPTrialUntil']??null;
|
||||
$tokenValidUntil = $userInfo['token_valid_until'];
|
||||
|
||||
if(!$accessToken || !$refreshToken || !$accessTokenValidUntil || !$refreshTokenValidUntil){
|
||||
return [false, 'OAuth 정보가 보관되어 있지 않습니다. 카카오 로그인을 수행해 주세요.'];
|
||||
}
|
||||
|
||||
$now = TimeUtil::DatetimeNow();
|
||||
|
||||
if($now > $refreshTokenValidUntil){
|
||||
return [false, '로그인 토큰이 만료되었습니다. 카카오 로그인을 수행해 주세요.'];
|
||||
}
|
||||
|
||||
if($now > $accessTokenValidUntil){
|
||||
$apiHelper = new Kakao_REST_API_Helper($accessToken);
|
||||
$refreshResult = $apiHelper->refresh_access_token($refreshToken);
|
||||
if(!$refreshResult){
|
||||
return [false, '로그인 토큰 자동 갱신을 실패했습니다. 카카오 로그인을 수행해 주세요.'];
|
||||
}
|
||||
|
||||
$accessToken = $refreshResult['access_token']??null;
|
||||
|
||||
if(!$accessToken){
|
||||
trigger_error("refreshToken 에러 ".Json::encode($refreshResult).",".$refreshToken.",".substr(\kakao\KakaoKey::REST_KEY, 0, 6), E_USER_NOTICE);
|
||||
return [false, '로그인 토큰 자동 갱신을 실패했습니다. 카카오 로그인을 수행해 주세요.'];
|
||||
}
|
||||
$accessTokenValidUntil = TimeUtil::DatetimeFromNowSecond($refreshResult['expires_in']);
|
||||
|
||||
$oauthInfo['accessToken'] = $accessToken;
|
||||
$oauthInfo['accessTokenValidUntil'] = $accessTokenValidUntil;
|
||||
|
||||
$refreshToken = $refreshResult['refresh_token']??null;
|
||||
if($refreshToken){
|
||||
$refreshTokenValidUntil = TimeUtil::DatetimeFromNowSecond($refreshResult['refresh_token_expires_in']);
|
||||
|
||||
$oauthInfo['refreshToken'] = $refreshToken;
|
||||
$oauthInfo['refresh_token_expires_in'] = $refresh_token_expires_in;
|
||||
}
|
||||
|
||||
RootDB::db()->update('member', [
|
||||
'oauth_info'=>Json::encode($oauthInfo)
|
||||
], 'no=%i', $userInfo['no']);
|
||||
}
|
||||
|
||||
if($tokenValidUntil && $now <= $tokenValidUntil){
|
||||
return null;
|
||||
}
|
||||
|
||||
//인증 시스템 가동
|
||||
$session = Session::getInstance();
|
||||
$session->access_token = $accessToken;
|
||||
$session->expires = $accessTokenValidUntil;
|
||||
$session->refresh_token = $refreshToken;
|
||||
$session->refresh_token_expires = $refreshTokenValidUntil;
|
||||
|
||||
if(!createOTPbyUserNO($userInfo['no'])){
|
||||
return [false, '인증 코드를 보내는데 실패했습니다.'];
|
||||
}
|
||||
|
||||
return [true, '인증 코드를 입력해주세요'];
|
||||
}
|
||||
|
||||
$userInfo = $RootDB->queryFirstRow(
|
||||
'SELECT `no`, `id`, `name`, `grade`, `delete_after`, `acl` '.
|
||||
'SELECT `no`, `id`, `name`, `grade`, `delete_after`, `acl`, oauth_id, oauth_type, oauth_info, token_valid_until '.
|
||||
'from member where id=%s_username AND '.
|
||||
'pw=sha2(concat(salt, %s_password, salt), 512)',[
|
||||
'username'=>$username,
|
||||
@@ -31,6 +113,7 @@ $userInfo = $RootDB->queryFirstRow(
|
||||
if(!$userInfo){
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reqOTP'=>false,
|
||||
'reason'=>'아이디나 비밀번호가 올바르지 않습니다.'
|
||||
]);
|
||||
}
|
||||
@@ -49,12 +132,14 @@ if($userInfo['delete_after']){
|
||||
$RootDB->delete('member', 'no=%i', $userInfo['no']);
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reqOTP'=>false,
|
||||
'reason'=>"기간 만기로 삭제되었습니다. 재 가입을 시도해주세요."
|
||||
]);
|
||||
}
|
||||
else{
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reqOTP'=>false,
|
||||
'reason'=>"삭제 요청된 계정입니다.[{$userInfo['delete_after']}]"
|
||||
]);
|
||||
}
|
||||
@@ -70,10 +155,23 @@ $RootDB->insert('member_log',[
|
||||
])
|
||||
]);
|
||||
|
||||
if($userInfo['oauth_type'] == 'KAKAO'){
|
||||
$oauthFailResult = kakaoOAuthCheck($userInfo);
|
||||
if($oauthFailResult !== null){
|
||||
$session->login($userInfo['no'], $userInfo['id'], $userInfo['grade'], true, $userInfo['token_valid_until'], Json::decode($userInfo['acl']??'{}'));
|
||||
[$oauthReqOTP, $oauthFailReason] = $oauthFailResult;
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reqOTP'=>$oauthReqOTP,
|
||||
'reason'=>$oauthFailReason
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$session->login($userInfo['no'], $userInfo['id'], $userInfo['grade'], Json::decode($userInfo['acl']??'{}'));
|
||||
$session->login($userInfo['no'], $userInfo['id'], $userInfo['grade'], false, $userInfo['token_valid_until'], Json::decode($userInfo['acl']??'{}'));
|
||||
Json::die([
|
||||
'result'=>true,
|
||||
'reqOTP'=>false,
|
||||
'reason'=>'로그인 되었습니다.'
|
||||
]);
|
||||
+1
-1
@@ -111,7 +111,7 @@ $(document).ready( function () {
|
||||
alert(obj.reason);
|
||||
}
|
||||
else{
|
||||
alert('정상적으로 가입되었습니다.');
|
||||
alert('회원 등록되었습니다.\n첫 로그인 과정에서 인증 코드를 입력하는 것으로 계정이 활성화됩니다.');
|
||||
}
|
||||
|
||||
window.location.href = "../";
|
||||
|
||||
+36
-4
@@ -48,14 +48,46 @@ $(document).ready( function () {
|
||||
'password':hash_pw
|
||||
}
|
||||
}).then(function(obj){
|
||||
if(!obj.result){
|
||||
if(obj.result){
|
||||
window.location.href = "./";
|
||||
return;
|
||||
}
|
||||
if(!obj.reqOTP){
|
||||
alert(obj.reason);
|
||||
return;
|
||||
}
|
||||
else{
|
||||
window.location.href = 'i_entrance/entrance.php';
|
||||
}
|
||||
|
||||
var $modal = $('#modalOTP').modal();
|
||||
$modal.on('shown.bs.modal', function(){
|
||||
$('#otp_code').focus();
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#otp_form').submit(function(){
|
||||
$.post({
|
||||
url:'oauth_kakao/j_check_OTP.php',
|
||||
dataType:'json',
|
||||
data:{
|
||||
'otp':$('#otp_code').val(),
|
||||
}
|
||||
}).then(function(obj){
|
||||
if(obj.result){
|
||||
alert(obj.reason);
|
||||
window.location.href = "./";
|
||||
return;
|
||||
}
|
||||
|
||||
alert(obj.reason);
|
||||
|
||||
if(obj.reset){
|
||||
$('#modalOTP').modal('hide')
|
||||
return;
|
||||
}
|
||||
});
|
||||
return false;
|
||||
})
|
||||
} );
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
jQuery(function($){
|
||||
$.each(window.sammo_menu, function(idx){
|
||||
var href = this[0];
|
||||
var name = this[1];
|
||||
var target = (this.length > 2)?this[2]:null;
|
||||
|
||||
var $a = $('<a class="nav-link" href="{0}">{1}</a>'.format(href, name));
|
||||
if(target){
|
||||
$a.attr('target', target);
|
||||
}
|
||||
var $li = $('<li class="nav-item"></li>').append($a);
|
||||
$('#navbarNav .navbar-nav').append($li);
|
||||
console.log(this);
|
||||
});
|
||||
});
|
||||
@@ -20,6 +20,16 @@ function fillUserInfo(result){
|
||||
if(result.third_use){
|
||||
$('#third_use_disallow').show();
|
||||
}
|
||||
$('#slot_oauth_type').text(result.oauth_type);
|
||||
if(result.oauth_type != 'NONE'){
|
||||
$('#slot_token_valid_until').text(result.token_valid_until);
|
||||
}
|
||||
else{
|
||||
$('#slot_token_valid_until').parent().html('');
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
function changeIconPreview(){
|
||||
@@ -217,6 +227,37 @@ function deleteMe(){
|
||||
});
|
||||
}
|
||||
|
||||
function extendAuth(){
|
||||
var validUntil = $('#slot_token_valid_until').html();
|
||||
var availableAt = moment(validUntil).subtract(5, 'days').format('YYYY-MM-DD HH:mm:ss');
|
||||
var now = moment().format('YYYY-MM-DD HH:mm:ss');
|
||||
|
||||
if(now < availableAt){
|
||||
alert('{0}부터 초기화할 수 있습니다.'.format(availableAt));
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!confirm('로그아웃됩니다. 진행할까요?')){
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
type:'post',
|
||||
url:'../oauth_kakao/j_reset_token.php',
|
||||
dataType:'json',
|
||||
}).then(function(result){
|
||||
if(!result.result){
|
||||
alert(result.reason);
|
||||
}
|
||||
else{
|
||||
alert('초기화했습니다. 다시 로그인해 주십시오.');
|
||||
location.href='../';
|
||||
}
|
||||
},function(){
|
||||
alert('알 수 없는 이유로 로그인 토큰 연장에 실패했습니다.');
|
||||
});
|
||||
}
|
||||
|
||||
$(function(){
|
||||
$('#slot_icon, #slot_new_icon').attr('src', pathConfig.sharedIcon+'/default.jpg');
|
||||
$.ajax({
|
||||
@@ -255,4 +296,6 @@ $(function(){
|
||||
deleteMe(e);
|
||||
}
|
||||
});
|
||||
|
||||
$('#expand_login_token').click(extendAuth);
|
||||
})
|
||||
|
||||
@@ -64,6 +64,9 @@ function postOAuthResult(result){
|
||||
if(result == 'join'){
|
||||
window.location.href = 'join.php';
|
||||
}
|
||||
else if(result == 'req_email'){
|
||||
alert('이메일 정보 공유를 허가해 주셔야 합니다.');
|
||||
}
|
||||
else if(result == 'login'){
|
||||
console.log('로그인모드');
|
||||
if(oauthMode=='change_pw'){
|
||||
|
||||
+23
-31
@@ -5,7 +5,7 @@ require(__dir__.'/../vendor/autoload.php');
|
||||
|
||||
use \kakao\Kakao_REST_API_Helper as Kakao_REST_API_Helper;
|
||||
|
||||
$nowDate = TimeUtil::DatetimeNow();
|
||||
$now = TimeUtil::DatetimeNow();
|
||||
|
||||
$session = Session::requireLogin([
|
||||
'reason'=>'로그인이 되어있지 않습니다'
|
||||
@@ -13,8 +13,6 @@ $session = Session::requireLogin([
|
||||
$userID = Session::getUserID();
|
||||
$access_token = $session->access_token;
|
||||
$expires = $session->expires;
|
||||
$refresh_token = $session->refresh_token;
|
||||
$refresh_token_expires = $session->refresh_token_expires;
|
||||
|
||||
if(!$access_token || !$expires){
|
||||
Json::die([
|
||||
@@ -27,44 +25,37 @@ if(!$access_token || !$expires){
|
||||
//TODO: join과 login의 동작이 비슷하다. helper class로 묶자.
|
||||
$restAPI = new Kakao_REST_API_Helper($access_token);
|
||||
|
||||
if($expires < $nowDate && (!$refresh_token || ($refresh_token_expires < $nowDate))){
|
||||
$session->logout();
|
||||
|
||||
if($expires < $now){
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reason'=>'로그인 토큰 만료.'.$refresh_token_expires.' 다시 카카오로그인을 수행해주세요.'
|
||||
'reason'=>'로그인 토큰 만료. 카카오 로그인을 먼저 수행해주세요.'
|
||||
]);
|
||||
}
|
||||
|
||||
if($expires < $nowDate){
|
||||
$session->kaccount_email = null;
|
||||
$email = null;
|
||||
|
||||
$result = $restAPI->refresh_access_token($refresh_token);
|
||||
if(!isset($refresh_token)){
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reason'=>'카카오 로그인 과정 중 추가 갱신 절차를 실패했습니다'
|
||||
]);
|
||||
}
|
||||
|
||||
$access_token = $result['access_token'];
|
||||
$expires = TimeUtil::DatetimeFromNowSecond($result['expires_in']);
|
||||
if(isset($result['refresh_token'])){
|
||||
$refresh_token = Util::array_get($result['refresh_token']);
|
||||
$refresh_token_expires = TimeUtil::DatetimeFromNowSecond($result['refresh_token_expires_in']);
|
||||
}
|
||||
}
|
||||
|
||||
RootDB::db()->query("lock tables member write, member_log write");
|
||||
|
||||
$isUser = RootDB::db()->queryFirstRow(
|
||||
'SELECT count(`no`) from member where no=%i',$userID);
|
||||
if(!$isUser){
|
||||
$oauthInfo = Json::decode(RootDB::db()->queryFirstField('SELECT oauth_info from member where no=%i',$userID))??[];
|
||||
if(!$oauthInfo){
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reason'=>'회원이 아닙니다. 관리자에게 문의해주세요.'
|
||||
'reason'=>'제대로 로그인이 이루어져 있지 않습니다.'
|
||||
]);
|
||||
}
|
||||
|
||||
$nextPasswordChange = $oauthInfo['nextPasswordChange']??null;
|
||||
if($nextPasswordChange && $now < $nextPasswordChange){
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reason'=>'비밀번호를 초기화한지 얼마 지나지 않았습니다.'
|
||||
]);
|
||||
}
|
||||
|
||||
$nextPasswordChange = TimeUtil::DatetimeFromNowHour(4);
|
||||
$oauthInfo['nextPasswordChange'] = $nextPasswordChange;
|
||||
|
||||
|
||||
$newPassword = Util::randomStr(6);
|
||||
$tmpPassword = Util::hashPassword(RootDB::getGlobalSalt(), $newPassword);
|
||||
$newSalt = bin2hex(random_bytes(8));
|
||||
@@ -89,12 +80,13 @@ if($sendResult['code'] < 0){
|
||||
|
||||
RootDB::db()->update('member', [
|
||||
'pw'=>$newFinalPassword,
|
||||
'salt'=>$newSalt
|
||||
'salt'=>$newSalt,
|
||||
'oauth_info'=>Json::encode($oauthInfo),
|
||||
],'no=%i', $userID);
|
||||
|
||||
RootDB::db()->insert('member_log', [
|
||||
'member_no'=>$userID,
|
||||
'date'=>$nowDate,
|
||||
'date'=>$now,
|
||||
'action_type'=>'change_pw',
|
||||
'action'=>Json::encode([
|
||||
'type'=>'kakao',
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
namespace sammo;
|
||||
|
||||
require(__dir__.'/../vendor/autoload.php');
|
||||
|
||||
$RootDB = RootDB::db();
|
||||
$session = Session::getInstance();
|
||||
if($session->isLoggedIn()){
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reset'=>true,
|
||||
'reason'=>'이미 로그인 되어있습니다.'
|
||||
]);
|
||||
}
|
||||
|
||||
if(!$session->isLoggedIn(true)){
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reset'=>true,
|
||||
'reason'=>'인증 코드를 입력할 수 있는 상태가 아닙니다.'
|
||||
]);
|
||||
}
|
||||
|
||||
$otp = Util::getReq('otp', 'int');
|
||||
if(!$otp){
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reset'=>false,
|
||||
'reason'=>'인증 코드가 입력되지 않았습니다.'
|
||||
]);
|
||||
}
|
||||
|
||||
$userNo = $session->getUserID();
|
||||
|
||||
$oauthInfo = Json::decode(RootDB::db()->queryFirstField('SELECT oauth_info FROM member WHERE no=%i', $userNo))??[];
|
||||
if(!$oauthInfo){
|
||||
$session->logout();
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reset'=>true,
|
||||
'reason'=>'계정이 정상적으로 등록되어있지 않습니다.'
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
$OTPValue = $oauthInfo['OTPValue']??null;
|
||||
$OTPTrialUntil = $oauthInfo['OTPTrialUntil']??null;
|
||||
$OTPTrialCount = $oauthInfo['OTPTrialCount']??0;
|
||||
$now = TimeUtil::DatetimeNow();
|
||||
|
||||
if(!$OTPTrialUntil || $OTPTrialUntil <= $now){
|
||||
$session->logout();
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reset'=>true,
|
||||
'reason'=>'인증 기한이 만료되었습니다. 다시 로그인해주세요.'
|
||||
]);
|
||||
}
|
||||
|
||||
if($OTPTrialCount <= 0){
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reset'=>false,
|
||||
'reason'=>"인증 실패 횟수를 초과했습니다. {$OTPTrialUntil}까지 기다려주세요."
|
||||
]);
|
||||
}
|
||||
|
||||
if($OTPValue != $otp){
|
||||
$OTPTrialCount -= 1;
|
||||
$oauthInfo['OTPTrialCount'] = $OTPTrialCount;
|
||||
RootDB::db()->update('member', [
|
||||
'oauth_info'=>Json::encode($oauthInfo)
|
||||
], 'no=%i', $userNo);
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reset'=>false,
|
||||
'reason'=>"인증 번호가 틀렸습니다. {$OTPTrialCount}회 더 시도할 수 있습니다."
|
||||
]);
|
||||
}
|
||||
|
||||
$tokenValidUntil = TimeUtil::DatetimeFromNowDay(10);
|
||||
$session->setReqOTP(false, $tokenValidUntil);
|
||||
RootDB::db()->update('member', [
|
||||
'oauth_info'=>Json::encode($oauthInfo),
|
||||
'token_valid_until'=>$tokenValidUntil,
|
||||
], 'no=%i', $userNo);
|
||||
|
||||
Json::die([
|
||||
'result'=>true,
|
||||
'reset'=>false,
|
||||
'reason'=>"로그인을 성공했습니다. {$tokenValidUntil}까지 유효합니다."
|
||||
]);
|
||||
@@ -125,23 +125,35 @@ if(!$kakaoID && Util::array_get($signupResult['msg'])!='already registered'){
|
||||
|
||||
$me = $restAPI->meWithEmail();
|
||||
$me['code'] = Util::array_get($me['code'], 0);
|
||||
$kakao_account = $me['kakao_account']??[];
|
||||
if ($me['code']< 0) {
|
||||
$restAPI->unlink();
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reason'=>'카카오로그인이 정상적으로 수행되지 않았습니다.'
|
||||
'reason'=>'카카오로그인이 정상적으로 수행되지 않았습니다.',
|
||||
]);
|
||||
}
|
||||
|
||||
if(!Util::array_get($me['kaccount_email_verified'],false)){
|
||||
if(!($kakao_account['has_email']??false)){
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reqOTP'=>false,
|
||||
'reason'=>'이메일 정보 공유를 허가해 주셔야 합니다.',
|
||||
]);
|
||||
}
|
||||
|
||||
$validEmail = $kakao_account['is_email_valid']??false;
|
||||
$verifiedEmail = $kakao_account['is_email_verified']??false;
|
||||
|
||||
if(!$validEmail || !$verifiedEmail){
|
||||
$restAPI->unlink();
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reason'=>'카카오 계정 이메일이 아직 인증되지 않았습니다'
|
||||
'reason'=>'카카오 계정 이메일이 아직 인증되지 않았습니다',
|
||||
]);
|
||||
}
|
||||
|
||||
$email = $me['kaccount_email'];
|
||||
$email = $kakao_account['email']??null;
|
||||
$session->kaccount_email = $email;
|
||||
$emailChk = checkEmailDup($email);
|
||||
if($emailChk !== true){
|
||||
@@ -162,7 +174,13 @@ RootDB::db()->insert('member',[
|
||||
'pw' => $finalPassword,
|
||||
'salt' => $userSalt,
|
||||
'name'=>$nickname,
|
||||
'reg_date'=>$nowDate
|
||||
'reg_date'=>$nowDate,
|
||||
'oauth_info'=>Json::encode([
|
||||
'accessToken'=>$access_token,
|
||||
'refreshToken'=>$refresh_token,
|
||||
'accessTokenValidUntil'=>$expires,
|
||||
'refreshTokenValidUntil'=>$refresh_token_expires
|
||||
])
|
||||
]);
|
||||
$userID = RootDB::db()->insertId();
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
namespace sammo;
|
||||
|
||||
require(__dir__.'/../vendor/autoload.php');
|
||||
|
||||
require('lib.join.php');
|
||||
use \kakao\Kakao_REST_API_Helper as Kakao_REST_API_Helper;
|
||||
|
||||
$RootDB = RootDB::db();
|
||||
@@ -15,12 +15,13 @@ $canLogin = RootDB::db()->queryFirstField('SELECT `LOGIN` FROM `system` WHERE `N
|
||||
if($canLogin != 'Y'){
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reqOTP'=>false,
|
||||
'reason'=>'현재는 로그인이 금지되어있습니다!',
|
||||
'noRetry'=>true
|
||||
]);
|
||||
}
|
||||
|
||||
$nowDate = TimeUtil::DatetimeNow();
|
||||
$now = TimeUtil::DatetimeNow();
|
||||
|
||||
$access_token = $session->access_token;
|
||||
$expires = $session->expires;
|
||||
@@ -32,6 +33,7 @@ $email = $session->kaccount_email;
|
||||
if(!$access_token || !$expires){
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reqOTP'=>false,
|
||||
'reason'=>'카카오로그인이 이루어지지 않았습니다.'
|
||||
]);
|
||||
}
|
||||
@@ -39,14 +41,15 @@ if(!$access_token || !$expires){
|
||||
//TODO: join과 login의 동작이 비슷하다. helper class로 묶자.
|
||||
$restAPI = new Kakao_REST_API_Helper($access_token);
|
||||
|
||||
if($expires < $nowDate && (!$refresh_token || ($refresh_token_expires < $nowDate))){
|
||||
if($expires < $now && (!$refresh_token || ($refresh_token_expires < $now))){
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reqOTP'=>false,
|
||||
'reason'=>'로그인 토큰 만료.'.$refresh_token_expires.' 다시 카카오로그인을 수행해주세요.'
|
||||
]);
|
||||
}
|
||||
|
||||
if($expires < $nowDate){
|
||||
if($expires < $now){
|
||||
$session->kaccount_email = null;
|
||||
$email = null;
|
||||
|
||||
@@ -54,6 +57,7 @@ if($expires < $nowDate){
|
||||
if(!isset($refresh_token)){
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reqOTP'=>false,
|
||||
'reason'=>'카카오 로그인 과정 중 추가 갱신 절차를 실패했습니다'
|
||||
]);
|
||||
}
|
||||
@@ -73,43 +77,68 @@ if(!$email){
|
||||
if ($me['code']< 0) {
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reqOTP'=>false,
|
||||
'reason'=>'카카오로그인이 정상적으로 수행되지 않았습니다.'
|
||||
]);
|
||||
}
|
||||
|
||||
if(!Util::array_get($me['kaccount_email_verified'],false)){
|
||||
$kakao_account = $me['kakao_account']??null;
|
||||
if (!$kakao_account) {
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reqOTP'=>false,
|
||||
'reason'=>'카카오 로그인 정보를 제대로 받아오지 못했습니다.'
|
||||
]);
|
||||
}
|
||||
|
||||
if(!($kakao_account['has_email']??false)){
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reqOTP'=>false,
|
||||
'reason'=>'이메일 정보 공유를 허가해 주셔야 합니다.',
|
||||
]);
|
||||
}
|
||||
|
||||
$validEmail = $kakao_account['is_email_valid']??false;
|
||||
$verifiedEmail = $kakao_account['is_email_verified']??false;
|
||||
|
||||
if(!$validEmail || !$verifiedEmail){
|
||||
$restAPI->unlink();
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reason'=>'카카오 계정 이메일이 아직 인증되지 않았습니다'
|
||||
'reqOTP'=>false,
|
||||
'reason'=>'카카오 계정 이메일이 아직 인증되지 않았습니다',
|
||||
]);
|
||||
}
|
||||
|
||||
$email = $me['kaccount_email'];
|
||||
$email = $kakao_account['email'];
|
||||
$session->kaccount_email = $email;
|
||||
}
|
||||
|
||||
|
||||
$userInfo = $RootDB->queryFirstRow(
|
||||
'SELECT `no`, `id`, `name`, `grade`, `delete_after`, `acl` from member where email=%s',$email);
|
||||
'SELECT `no`, `id`, `name`, `grade`, `delete_after`, `acl`, oauth_info, token_valid_until from member where email=%s',$email);
|
||||
|
||||
if(!$userInfo){
|
||||
$restAPI->unlink();
|
||||
$session->access_token = null;
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reason'=>'카카오로그인에 해당하는 계정이 없습니다. 재 가입을 시도해주세요.'
|
||||
'reqOTP'=>false,
|
||||
'reason'=>'카카오로그인에 해당하는 계정이 없습니다. 재 가입을 시도해주세요.',
|
||||
'aux'=>$session->tmpx,
|
||||
]);
|
||||
}
|
||||
|
||||
if($userInfo['delete_after']){
|
||||
if($userInfo['delete_after'] < $nowDate){
|
||||
if($userInfo['delete_after'] < $now){
|
||||
$restAPI->unlink();
|
||||
$session->access_token = null;
|
||||
$RootDB->delete('member', 'no=%i', $userInfo['no']);
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reason'=>"기간 만기로 삭제되었습니다. 재 가입을 시도해주세요."
|
||||
'reqOTP'=>false,
|
||||
'reason'=>"기간 만기로 삭제되었습니다. 재 가입을 시도해주세요.",
|
||||
]);
|
||||
}
|
||||
else{
|
||||
@@ -117,12 +146,45 @@ if($userInfo['delete_after']){
|
||||
$session->access_token = null;
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reqOTP'=>false,
|
||||
'reason'=>"삭제 요청된 계정입니다.[{$userInfo['delete_after']}]"
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$oauthInfo = Json::decode($userInfo['oauth_info'])??[];
|
||||
$oauthInfo['accessToken'] = $access_token;
|
||||
$oauthInfo['accessTokenValidUntil'] = $expires;
|
||||
if($refresh_token){
|
||||
$oauthInfo['refreshToken'] = $refresh_token;
|
||||
$oauthInfo['refreshTokenValidUntil'] = $refresh_token_expires;
|
||||
}
|
||||
|
||||
RootDB::db()->update('member', [
|
||||
'oauth_info'=>Json::encode($oauthInfo)
|
||||
], 'no=%i', $userInfo['no']);
|
||||
|
||||
|
||||
$tokenValidUntil = $userInfo['token_valid_until'];
|
||||
|
||||
if(!$tokenValidUntil || $tokenValidUntil < $now){
|
||||
if(!createOTPbyUserNO($userInfo['no'])){
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reqOTP'=>false,
|
||||
'reason'=>'인증 코드를 보내는데 실패했습니다.'
|
||||
]);
|
||||
}
|
||||
$session->login($userInfo['no'], $userInfo['id'], $userInfo['grade'], true, $userInfo['token_valid_until'], Json::decode($userInfo['acl']??'{}'));
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reqOTP'=>true,
|
||||
'reason'=>'인증 코드를 입력해주세요.'
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
$RootDB->insert('member_log',[
|
||||
'member_no'=>$userInfo['no'],
|
||||
'action_type'=>'login',
|
||||
@@ -132,8 +194,9 @@ $RootDB->insert('member_log',[
|
||||
])
|
||||
]);
|
||||
|
||||
$session->login($userInfo['no'], $userInfo['id'], $userInfo['grade'], Json::decode($userInfo['acl']??'{}'));
|
||||
$session->login($userInfo['no'], $userInfo['id'], $userInfo['grade'], false, $userInfo['token_valid_until'], Json::decode($userInfo['acl']??'{}'));
|
||||
Json::die([
|
||||
'result'=>true,
|
||||
'reqOTP'=>false,
|
||||
'reason'=>'로그인 되었습니다.'
|
||||
]);
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
namespace sammo;
|
||||
|
||||
require(__dir__.'/../vendor/autoload.php');
|
||||
|
||||
use \kakao\Kakao_REST_API_Helper as Kakao_REST_API_Helper;
|
||||
|
||||
$session = Session::requireLogin([
|
||||
'reason'=>'로그인이 되어있지 않습니다'
|
||||
]);
|
||||
|
||||
|
||||
$userID = Session::getUserID();
|
||||
|
||||
$tokenValidUntil = RootDB::db()->queryFirstField('SELECT token_valid_until from member where no=%i',$userID);
|
||||
|
||||
if(!$tokenValidUntil){
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reason'=>'초기화 가능한 로그인 상태가 아닙니다.'
|
||||
]);
|
||||
}
|
||||
|
||||
$now = TimeUtil::DatetimeNow();
|
||||
$expectedDate = TimeUtil::DatetimeFromNowDay(5);
|
||||
|
||||
if($expectedDate <= $tokenValidUntil){
|
||||
Json::die([
|
||||
'result'=>false,
|
||||
'reason'=>'아직 연장 가능하지 않습니다.'
|
||||
]);
|
||||
}
|
||||
|
||||
RootDB::db()->update('member', [
|
||||
'token_valid_until'=> null
|
||||
], 'no=%i', $userID);
|
||||
|
||||
$session->logout();
|
||||
|
||||
Json::die([
|
||||
'result'=>true,
|
||||
'reason'=>'초기화 완료.'
|
||||
]);
|
||||
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
namespace sammo;
|
||||
use \kakao\Kakao_REST_API_Helper as Kakao_REST_API_Helper;
|
||||
|
||||
function checkUsernameDup($username){
|
||||
if(!$username){
|
||||
@@ -62,4 +63,67 @@ function checkEmailDup($email){
|
||||
RootDB::db()->delete('member', 'no=%i', $userInfo['no']);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function createOTPbyUserNO(int $userNo):bool{
|
||||
$userInfo = RootDB::db()->queryFirstRow('SELECT oauth_info FROM member WHERE no=%i', $userNo);
|
||||
if(!$userInfo){
|
||||
return false;
|
||||
}
|
||||
|
||||
$oauthInfo = Json::decode($userInfo['oauth_info']);
|
||||
if(!$oauthInfo){
|
||||
return false;
|
||||
}
|
||||
|
||||
$accessToken = $oauthInfo['accessToken'];
|
||||
$OTPValue = $oauthInfo['OTPValue']??null;
|
||||
$OTPTrialUntil = $oauthInfo['OTPTrialUntil']??null;
|
||||
|
||||
$now = TimeUtil::DatetimeNow();
|
||||
|
||||
|
||||
if($OTPTrialUntil && $OTPValue && $OTPTrialUntil > $now){
|
||||
return true;
|
||||
}
|
||||
|
||||
[$OTPValue, $OTPTrialUntil] = createOTP($accessToken);
|
||||
|
||||
if(!$OTPValue){
|
||||
return false;
|
||||
}
|
||||
|
||||
$oauthInfo['OTPValue'] = $OTPValue;
|
||||
$oauthInfo['OTPTrialUntil'] = $OTPTrialUntil;
|
||||
$oauthInfo['OTPTrialCount'] = 3;
|
||||
|
||||
RootDB::db()->update('member', [
|
||||
'oauth_info'=>Json::encode($oauthInfo)
|
||||
], 'no=%i', $userNo);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function createOTP(string $accessToken):?array{
|
||||
$restAPI = new Kakao_REST_API_Helper($accessToken);
|
||||
|
||||
$OTPValue = Util::randRangeInt(1000, 9999);
|
||||
$OTPTrialUntil = TimeUtil::DatetimeFromNowSecond(180);
|
||||
|
||||
$sendResult = $restAPI->talk_to_me_default([
|
||||
"object_type"=> "text",
|
||||
"text"=> "인증 코드는 $OTPValue 입니다. $OTPTrialUntil 이내에 입력해주세요.",
|
||||
"link"=> [
|
||||
"web_url"=> ServConfig::getServerBasepath(),
|
||||
"mobile_web_url" => ServConfig::getServerBasepath()
|
||||
],
|
||||
"button_title"=> "로그인 페이지 열기"
|
||||
]);
|
||||
$sendResult['code'] = Util::array_get($sendResult['code'], 0);
|
||||
if($sendResult['code'] < 0){
|
||||
return null;
|
||||
}
|
||||
|
||||
return [$OTPValue, $OTPTrialUntil];
|
||||
}
|
||||
|
||||
|
||||
+10
-2
@@ -47,7 +47,15 @@ $me = $restAPI->meWithEmail();
|
||||
$oauth_mode = 'login';
|
||||
|
||||
$me['code'] = Util::array_get($me['code'], 0);
|
||||
if($me['code']< 0){
|
||||
$signed = $me['has_signed_up']??false;
|
||||
$kakao_account = $me['kakao_account']??[];
|
||||
if(!($kakao_account['has_email']??false)||!($kakao_account['email']??false)){
|
||||
$oauth_mode = 'req_email';
|
||||
}
|
||||
else if(!$signed){
|
||||
$oauth_mode = 'join';
|
||||
}
|
||||
else if($me['code']< 0){
|
||||
switch(Util::array_get($me['msg'])){
|
||||
case 'NotRegisteredUserException':
|
||||
$oauth_mode = 'join';
|
||||
@@ -57,7 +65,7 @@ if($me['code']< 0){
|
||||
}
|
||||
}
|
||||
else{
|
||||
$session->kaccount_email = $me['kaccount_email'];
|
||||
$session->kaccount_email = $kakao_account['email'];
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8">>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=1024" />
|
||||
<title>OAuth 로그인 실패</title>
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
User-agent: *
|
||||
Allow: /
|
||||
@@ -25,7 +25,7 @@ class User_Management_Path
|
||||
public static $SIGNUP = "/v1/user/signup";
|
||||
public static $UNLINK = "/v1/user/unlink";
|
||||
public static $LOGOUT = "/v1/user/logout";
|
||||
public static $ME = "/v1/user/me";
|
||||
public static $ME = "/v2/user/me";
|
||||
public static $UPDATE_PROFILE = "/v1/user/update_profile";
|
||||
public static $USER_IDS = "/v1/user/ids";
|
||||
}
|
||||
@@ -97,12 +97,12 @@ class Kakao_REST_API_Helper
|
||||
$requestUrl .= '?'.$params;
|
||||
}
|
||||
|
||||
$opts = array(
|
||||
CURLOPT_URL => $requestUrl,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_SSL_VERIFYPEER => false,
|
||||
CURLOPT_SSLVERSION => 1,
|
||||
);
|
||||
$opts = [
|
||||
CURLOPT_URL => $requestUrl,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_SSL_VERIFYPEER => false,
|
||||
CURLOPT_SSLVERSION => CURL_SSLVERSION_TLSv1,
|
||||
];
|
||||
|
||||
if ($api_path != '/oauth/token') {
|
||||
if (in_array($api_path, self::$admin_apis)) {
|
||||
@@ -182,8 +182,7 @@ class Kakao_REST_API_Helper
|
||||
$params = [
|
||||
'grant_type'=>'refresh_token',
|
||||
'client_id'=>$this->REST_KEY,
|
||||
'redirect_uri'=>$this->REDIRECT_URI,
|
||||
'code'=>$refresh_token
|
||||
'refresh_token'=>$refresh_token
|
||||
];
|
||||
$result = $this->_create_or_refresh_access_token($params);
|
||||
|
||||
@@ -213,8 +212,12 @@ class Kakao_REST_API_Helper
|
||||
public function meWithEmail()
|
||||
{
|
||||
$params = [
|
||||
'propertyKeys'=>'["id","kaacount_email","kaccount_email_verified"]'
|
||||
];
|
||||
'property_keys'=>'['.
|
||||
'"id",'.
|
||||
'"kakao_account.has_email","kakao_account.email",'.
|
||||
'"kakao_account.is_email_valid","kakao_account.is_email_verified"'.
|
||||
']'
|
||||
];
|
||||
return $this->request(User_Management_Path::$ME);
|
||||
}
|
||||
|
||||
|
||||
+27
-3
@@ -8,12 +8,15 @@ namespace sammo;
|
||||
* @property string $userName 유저명
|
||||
* @property int $userGrade 유저등급
|
||||
* @property string $ip IP
|
||||
* @property bool $reqOTP 인증 코드 필요
|
||||
* @property array $acl 권한
|
||||
* @property string $tokenValidUntil 로그인 토큰 길이
|
||||
*/
|
||||
class Session
|
||||
{
|
||||
const PROTECTED_NAMES = [
|
||||
'ip'=>true,
|
||||
'reqOTP'=>true,
|
||||
'time'=>true,
|
||||
'userID'=>true,
|
||||
'userName'=>true,
|
||||
@@ -21,6 +24,7 @@ class Session
|
||||
'writeClosed'=>true,
|
||||
'generalID'=>true,
|
||||
'generalName'=>true,
|
||||
'tokenValidUntil'=>true,
|
||||
'acl'=>true
|
||||
];
|
||||
|
||||
@@ -163,7 +167,7 @@ class Session
|
||||
return Util::array_get($_SESSION[$name]);
|
||||
}
|
||||
|
||||
public function login(int $userID, string $userName, int $grade, array $acl): Session
|
||||
public function login(int $userID, string $userName, int $grade, bool $reqOTP, ?string $tokenValidUntil, array $acl): Session
|
||||
{
|
||||
$this->set('userID', $userID);
|
||||
$this->set('userName', $userName);
|
||||
@@ -171,10 +175,16 @@ class Session
|
||||
$this->set('time', time());
|
||||
$this->set('userGrade', $grade);
|
||||
$this->set('acl', $acl);
|
||||
$this->set('access_token', null);
|
||||
$this->set('reqOTP', $reqOTP);
|
||||
$this->set('tokenValidUntil', $tokenValidUntil);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setReqOTP(bool $reqOTP=false, string $tokenValidUntil){
|
||||
$this->set('reqOTP', $reqOTP);
|
||||
$this->set('tokenValidUntil', $tokenValidUntil);
|
||||
}
|
||||
|
||||
|
||||
public function logout(): Session
|
||||
{
|
||||
@@ -189,6 +199,7 @@ class Session
|
||||
$this->set('userName', null);
|
||||
$this->set('userGrade', null);
|
||||
$this->set('acl', null);
|
||||
$this->set('reqOTP', null);
|
||||
$this->set('time', time());
|
||||
return $this;
|
||||
}
|
||||
@@ -324,8 +335,21 @@ class Session
|
||||
return $obj->userID;
|
||||
}
|
||||
|
||||
public function isLoggedIn(): bool
|
||||
public function isLoggedIn(bool $ignoreOTP = false): bool
|
||||
{
|
||||
if(!$ignoreOTP){
|
||||
if($this->reqOTP){
|
||||
return false;
|
||||
}
|
||||
if(!$this->tokenValidUntil){
|
||||
return false;
|
||||
}
|
||||
$now = TimeUtil::DatetimeNow();
|
||||
if($this->tokenValidUntil < $now){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->userID) {
|
||||
return true;
|
||||
} else {
|
||||
|
||||
@@ -177,7 +177,7 @@ class StringUtil
|
||||
public static function removeSpecialCharacter($str)
|
||||
{
|
||||
return str_replace([
|
||||
'"', '\'', 'ⓝ', 'ⓜ', 'ⓖ', '\\', '/', '`', '#',
|
||||
'"', '\'', 'ⓝ', 'ⓜ', 'ⓖ', 'ⓞ', 'ⓧ', '㉥', '\\', '/', '`', '#',
|
||||
'-', '#', '|'
|
||||
], '', $str);
|
||||
}
|
||||
|
||||
@@ -63,4 +63,30 @@ class TimeUtil
|
||||
{
|
||||
return date('H:i:s', strtotime('00:00:00') + $second);
|
||||
}
|
||||
|
||||
/**
|
||||
* $baseYear, $baseMonth 부터 $afterMonth 개월 이내인지. $afterMonth 포함.
|
||||
*
|
||||
*/
|
||||
public static function IsRangeMonth(int $baseYear, int $baseMonth, int $afterMonth, int $askYear, int $askMonth):bool{
|
||||
if($baseMonth < 1 || $baseMonth > 12){
|
||||
throw new \InvalidArgumentException('개월이 올바르지 않음');
|
||||
}
|
||||
if($askMonth < 1 || $askMonth > 12){
|
||||
throw new \InvalidArgumentException('개월이 올바르지 않음');
|
||||
}
|
||||
|
||||
$minMonth = $baseYear * 12 + $baseMonth;
|
||||
if($afterMonth < 0){
|
||||
$maxMonth = $minMonth;
|
||||
$minMonth = $maxMonth - $afterMonth;
|
||||
}
|
||||
|
||||
$maxMonth = $minMonth + $afterMonth;
|
||||
$askMonth = $askYear * 12 + $askMonth;
|
||||
if($askMonth < $minMonth || $maxMonth < $askMonth){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,24 @@ class Util extends \utilphp\util
|
||||
throw new \InvalidArgumentException('올바르지 않은 type 지정');
|
||||
}
|
||||
|
||||
public static function zip(iterable ...$iterators){
|
||||
while(true){
|
||||
$hasValue = false;
|
||||
$values = [];
|
||||
foreach($iterators as $iter){
|
||||
if($iter->valid()){
|
||||
$values[] = $iter->send(NULL);
|
||||
$hasValue = true;
|
||||
}
|
||||
}
|
||||
yield $values;
|
||||
|
||||
if(!$hasValue){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* $_POST, $_GET에서 값을 가져오는 함수. Util::array_get($_POST[$name])을 축약 가능.
|
||||
* 타입이 복잡해질 경우 이 함수를 통하지 않고 json으로 요청할 것을 권장.
|
||||
|
||||
@@ -132,7 +132,34 @@ class WebUtil
|
||||
}
|
||||
|
||||
$config = \HTMLPurifier_HTML5Config::createDefault();
|
||||
$def = $config->getHTMLDefinition();
|
||||
$def->info_global_attr['data-flip'] = new \HTMLPurifier_AttrDef_Text;
|
||||
$purifier = new \HTMLPurifier($config);
|
||||
return $purifier->purify($text);
|
||||
}
|
||||
|
||||
public static function drawMenu(string $path): string{
|
||||
if(!file_exists($path)){
|
||||
return '';
|
||||
}
|
||||
$json = Json::decode(file_get_contents($path));
|
||||
|
||||
$result = [];
|
||||
foreach($json as $menuItem){
|
||||
if (count($menuItem) == 2) {
|
||||
[$url, $title] = $menuItem;
|
||||
$targetAttr = '';
|
||||
}
|
||||
else{
|
||||
[$url, $title, $target] = $menuItem;
|
||||
$target = htmlspecialchars($target);
|
||||
$targetAttr = "target='$target' ";
|
||||
}
|
||||
$title = htmlspecialchars($title);
|
||||
$url = htmlspecialchars($url);
|
||||
$result[] = "<a class='nav-link' href='$url' $targetAttr>$title</a>";
|
||||
}
|
||||
|
||||
return join("\n", $result);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user