feat: 새 부대편성 페이지 (#223)
Reviewed-on: https://storage.hided.net/gitea/devsam/core/pulls/223
This commit was merged in pull request #223.
This commit is contained in:
@@ -57,7 +57,6 @@ return [
|
|||||||
'hwe/b_myPage.php',
|
'hwe/b_myPage.php',
|
||||||
'hwe/v_processing.php',
|
'hwe/v_processing.php',
|
||||||
'hwe/b_tournament.php',
|
'hwe/b_tournament.php',
|
||||||
'hwe/b_troop.php',
|
|
||||||
'hwe/c_tournament.php',
|
'hwe/c_tournament.php',
|
||||||
'hwe/func_auction.php',
|
'hwe/func_auction.php',
|
||||||
'hwe/func_command.php',
|
'hwe/func_command.php',
|
||||||
|
|||||||
-251
@@ -1,251 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace sammo;
|
|
||||||
|
|
||||||
include "lib.php";
|
|
||||||
include "func.php";
|
|
||||||
//로그인 검사
|
|
||||||
$session = Session::requireGameLogin()->setReadOnly();
|
|
||||||
$userID = Session::getUserID();
|
|
||||||
|
|
||||||
$db = DB::db();
|
|
||||||
|
|
||||||
increaseRefresh("부대편성", 1);
|
|
||||||
|
|
||||||
$me = $db->queryFirstRow('SELECT no,nation,troop,`officer_level`,permission,penalty FROM general WHERE owner=%i', $userID);
|
|
||||||
$permission = checkSecretPermission($me, false);
|
|
||||||
|
|
||||||
$troops = [];
|
|
||||||
foreach ($db->query('SELECT troop_leader,name FROM troop WHERE nation = %i', $me['nation']) as $rawTroop) {
|
|
||||||
$troops[$rawTroop['troop_leader']] = [
|
|
||||||
'name' => $rawTroop['name'],
|
|
||||||
'users' => []
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($db->query(
|
|
||||||
'SELECT no,name,turntime,troop,city FROM general WHERE troop!=0 AND nation = %i ORDER BY turntime ASC',
|
|
||||||
$me['nation']
|
|
||||||
) as $general) {
|
|
||||||
if (!key_exists($general['troop'], $troops)) {
|
|
||||||
trigger_error("올바르지 않은 부대 소속 {$general['no']}, {$general['name']} : {$general['troop']}");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$general['cityText'] = CityConst::byID($general['city'])->name;
|
|
||||||
|
|
||||||
$troops[$general['troop']]['users'][] = $general;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($troops) {
|
|
||||||
$troopLeaders = $db->query(
|
|
||||||
'SELECT no,name,picture,imgsvr,turntime,city,troop FROM general WHERE no IN %li',
|
|
||||||
array_keys($troops)
|
|
||||||
);
|
|
||||||
$generalTurnList = [];
|
|
||||||
|
|
||||||
foreach ($db->queryAllLists(
|
|
||||||
'SELECT general_id, turn_idx, brief FROM general_turn WHERE general_id IN %li AND turn_idx < 5 ORDER BY general_id ASC, turn_idx ASC',
|
|
||||||
array_column($troopLeaders, 'no')
|
|
||||||
) as [$generalID, $turnIdx, $brief]) {
|
|
||||||
if (!key_exists($generalID, $generalTurnList)) {
|
|
||||||
$generalTurnList[$generalID] = [];
|
|
||||||
}
|
|
||||||
$generalTurnList[$generalID][$turnIdx] = $brief;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($troopLeaders as $troopLeader) {
|
|
||||||
$imageTemp = GetImageURL($troopLeader['imgsvr']);
|
|
||||||
|
|
||||||
$troopLeader['pictureFullPath'] = "$imageTemp/{$troopLeader['picture']}";
|
|
||||||
$troopLeader['cityText'] = CityConst::byID($troopLeader['city'])->name;
|
|
||||||
|
|
||||||
$turnText = [];
|
|
||||||
foreach ($generalTurnList[$troopLeader['no']] as $rawTurnIdx => $brief) {
|
|
||||||
if ($brief != '집합') {
|
|
||||||
$brief = '~';
|
|
||||||
}
|
|
||||||
$turnIdx = $rawTurnIdx + 1;
|
|
||||||
$turnText[] = "{$turnIdx} : {$brief}";
|
|
||||||
}
|
|
||||||
$troopLeader['turnText'] = join('<br>', $turnText);
|
|
||||||
$troops[$troopLeader['troop']]['leader'] = $troopLeader;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uasort($troops, function ($lhs, $rhs) {
|
|
||||||
return $lhs['leader']['turntime'] <=> $rhs['leader']['turntime'];
|
|
||||||
})
|
|
||||||
|
|
||||||
?>
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
<meta name="viewport" content="width=1024" />
|
|
||||||
<title><?= UniqueConst::$serverName ?>: 부대편성</title>
|
|
||||||
<?= WebUtil::printJS('../d_shared/common_path.js') ?>
|
|
||||||
<?= WebUtil::printCSS('../d_shared/common.css') ?>
|
|
||||||
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/static/pretendard.css" />
|
|
||||||
<?= WebUtil::printDist('ts', ['common', 'troop'], true) ?>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div style="width:1000px;margin:auto;">
|
|
||||||
<table width=1000 class='tb_layout bg0'>
|
|
||||||
<tr>
|
|
||||||
<td>부 대 편 성<br><?= backButton() ?></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<table id="troop_list" class='tb_layout bg0'>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<td width=64 class='bg1 center'>선 택</td>
|
|
||||||
<td width=130 class='bg1 center'>부 대 정 보</td>
|
|
||||||
<td width=100 class='bg1 center'>부 대 장</td>
|
|
||||||
<td width=576 class='bg1 center' style=table-layout:fixed;word-break:break-all;>장 수</td>
|
|
||||||
<td width=130 class='bg1 center' style=table-layout:fixed;word-break:break-all;>부대장행동</td>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tfoot>
|
|
||||||
<tr>
|
|
||||||
<td colspan='5'>
|
|
||||||
<?php if (!$troops) : ?>
|
|
||||||
<?php elseif ($me['troop'] == 0) : ?>
|
|
||||||
<input type=button id='btnJoinTroop' value='부 대 가 입'>
|
|
||||||
<?php else : ?>
|
|
||||||
<input type=button id="btnLeaveTroop" value='부 대 탈 퇴'>
|
|
||||||
<?php endif; ?>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tfoot>
|
|
||||||
<tbody>
|
|
||||||
<?php
|
|
||||||
foreach ($troops as $troopNo => $troop) {
|
|
||||||
$troopLeader = $troop['leader'];
|
|
||||||
$genlistText = [];
|
|
||||||
$cityText = $troopLeader['cityText'];
|
|
||||||
$cityID = $troopLeader['city'];
|
|
||||||
$leaderID = $troopLeader['no'];
|
|
||||||
|
|
||||||
foreach ($troop['users'] as $troopUser) {
|
|
||||||
$spanClass = 'troopUser';
|
|
||||||
if ($troopUser['city'] !== $cityID) {
|
|
||||||
$spanClass .= ' diffCity';
|
|
||||||
}
|
|
||||||
if ($troopUser['no'] == $leaderID) {
|
|
||||||
$spanClass .= ' leader';
|
|
||||||
}
|
|
||||||
$genlistText[] = "<span class='$spanClass' data-general-id='{$troopUser['no']}'
|
|
||||||
><span class='generalName'>{$troopUser['name']}</span><span class='cityText'>【{$troopUser['cityText']}】</span
|
|
||||||
></span>";
|
|
||||||
}
|
|
||||||
|
|
||||||
$genlistText = sprintf('%s (%d명)', join(', ', $genlistText), count($genlistText)); ?>
|
|
||||||
|
|
||||||
<?php if ($me['troop'] == 0) : ?>
|
|
||||||
<tr>
|
|
||||||
<td align=center rowspan=2><input type='radio' class='troopId' name='troop' value='<?= $troopNo ?>'></td>
|
|
||||||
<td align=center><?= $troop['name'] ?><br>【 <?= $cityText ?> 】</td>
|
|
||||||
<td height=64 class='generalIcon' style='background:no-repeat center url("<?= $troopLeader['pictureFullPath'] ?>");background-size:64px;'> </td>
|
|
||||||
<td rowspan=2 width=62><?= $genlistText ?></td>
|
|
||||||
<td rowspan=2><?= $troopLeader['turnText'] ?></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align=center>
|
|
||||||
<font size=2>【턴】 <?= substr($troopLeader['turntime'], 14, 5) ?></font>
|
|
||||||
</td>
|
|
||||||
<td align=center>
|
|
||||||
<font size=1><?= $troopLeader['name'] ?></font>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan=5>
|
|
||||||
|
|
||||||
<?php else : ?>
|
|
||||||
<tr>
|
|
||||||
<td align=center rowspan=2> </td>
|
|
||||||
<td align=center><?= $troop['name'] ?><br>【 <?= $cityText ?> 】</td>
|
|
||||||
<td height=64 class='generalIcon' style='background:no-repeat center url("<?= $troopLeader['pictureFullPath'] ?>");background-size:64px;'> </td>
|
|
||||||
<td rowspan=2 width=62><?= $genlistText ?></td>
|
|
||||||
<td rowspan=2>
|
|
||||||
<?php if ($me['no'] == $troopLeader['no']) : ?>
|
|
||||||
<select id='genNo' name=gen size=3 style=color:white;background-color:black;font-size:14px;width:128px;>";
|
|
||||||
<?php foreach ($troop['users'] as $troopUser) : ?>
|
|
||||||
<?php if ($troopUser['no'] == $me['no']) {
|
|
||||||
continue;
|
|
||||||
} ?>
|
|
||||||
<option value='<?= $troopUser['no'] ?>'><?= $troopUser['name'] ?></option>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</select><br>
|
|
||||||
<input type=button id='btnKickTroop' value='부 대 추 방' style=width:130px;height:25px;>
|
|
||||||
<?php else : ?>
|
|
||||||
<?= $troopLeader['turnText'] ?>
|
|
||||||
<?php endif; ?>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align=center>
|
|
||||||
<font size=2>【턴】 <?= substr($troopLeader['turntime'], 14, 5) ?></font>
|
|
||||||
</td>
|
|
||||||
<td align=center>
|
|
||||||
<font size=1><?= $troopLeader['name'] ?></font>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan=5></td>
|
|
||||||
</tr>
|
|
||||||
<?php endif;
|
|
||||||
} //foreach ($troops as $troopNo=>$troop) {
|
|
||||||
?>
|
|
||||||
|
|
||||||
</tbody>
|
|
||||||
|
|
||||||
</table>
|
|
||||||
<br>
|
|
||||||
<div class="row">
|
|
||||||
<?php if($me['troop'] == 0): ?>
|
|
||||||
<div class="col-6"><?php /*TODO: 모바일 */ ?>
|
|
||||||
<div class="row gx-0 bg0">
|
|
||||||
<div class="bg1 col d-grid"><span class="align-self-center center">부대명</span></div>
|
|
||||||
<div class="col d-grid"><input type=text style=color:white;background-color:black; size=18 maxlength=18 id='newTroopName'></div>
|
|
||||||
<div class="col d-grid"><button type='button' id="btnCreateTroop" class='btn btn-sm btn-secondary'>부대 창설</button></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if($troops && ($me['troop'] == $me['no'] || $permission == 4)): ?>
|
|
||||||
<div class="col-6">
|
|
||||||
<div class="row gx-0 bg0">
|
|
||||||
<div class="bg1 col d-grid"><span class="align-self-center center">부대명</span></div>
|
|
||||||
<div class="col d-grid"><select class="form-select" id="changeNameTroopID">
|
|
||||||
<?php if($permission != 4): ?>
|
|
||||||
<option value="<?=$me['troop']?>"><?=$troops[$me['troop']]['name']?></option>
|
|
||||||
<?php else: ?>
|
|
||||||
<?php foreach ($troops as $troopNo => $troop): ?>
|
|
||||||
<option value="<?=$troopNo?>"><?=$troop['name']?></option>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
<?php endif; ?>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="col d-grid"><input type=text style=color:white;background-color:black; size=18 maxlength=18 id='changeTroopName'></div>
|
|
||||||
<div class="col d-grid"><button type='button' id="btnChangeTroopName" class='btn btn-sm btn-secondary'>이름 변경</button></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<table width=1000 class='tb_layout bg0'>
|
|
||||||
<tr>
|
|
||||||
<td><?= backButton() ?></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><?= banner() ?> </td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace sammo\API\Nation;
|
namespace sammo\API\Nation;
|
||||||
|
|
||||||
|
use ArrayObject;
|
||||||
use sammo\DB;
|
use sammo\DB;
|
||||||
use sammo\General;
|
use sammo\General;
|
||||||
use sammo\Session;
|
use sammo\Session;
|
||||||
@@ -45,10 +46,11 @@ class GeneralList extends \sammo\BaseAPI
|
|||||||
'connect' => 0,
|
'connect' => 0,
|
||||||
|
|
||||||
'troop' => 0,
|
'troop' => 0,
|
||||||
|
'city' => 0,
|
||||||
|
|
||||||
'con' => 1,
|
'con' => 1,
|
||||||
'specage' => 1,
|
'specage' => 0,
|
||||||
'specage2' => 1,
|
'specage2' => 0,
|
||||||
'leadership_exp' => 1,
|
'leadership_exp' => 1,
|
||||||
'strength_exp' => 1,
|
'strength_exp' => 1,
|
||||||
'intel_exp' => 1,
|
'intel_exp' => 1,
|
||||||
@@ -58,7 +60,6 @@ class GeneralList extends \sammo\BaseAPI
|
|||||||
'dex4' => 1,
|
'dex4' => 1,
|
||||||
'dex5' => 1,
|
'dex5' => 1,
|
||||||
|
|
||||||
'city' => 1,
|
|
||||||
'experience' => 1,
|
'experience' => 1,
|
||||||
'dedication' => 1,
|
'dedication' => 1,
|
||||||
|
|
||||||
@@ -158,35 +159,59 @@ class GeneralList extends \sammo\BaseAPI
|
|||||||
|
|
||||||
$rawGeneralList = Util::convertArrayToDict($db->query('SELECT %l from general WHERE nation = %i ORDER BY turntime ASC', Util::formatListOfBackticks($queryColumns), $nationID), 'no');
|
$rawGeneralList = Util::convertArrayToDict($db->query('SELECT %l from general WHERE nation = %i ORDER BY turntime ASC', Util::formatListOfBackticks($queryColumns), $nationID), 'no');
|
||||||
|
|
||||||
|
/** @var ArrayObject[] */
|
||||||
|
$troops = [];
|
||||||
|
foreach ($db->queryAllLists('SELECT troop_leader,name FROM troop WHERE nation = %i', $nationID) as [$troopLeaderID, $troopName]) {
|
||||||
|
if (!key_exists($troopLeaderID, $rawGeneralList)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$troopTurnTime = $rawGeneralList[$troopLeaderID]['turntime'];
|
||||||
|
$troops[$troopLeaderID] = new ArrayObject([
|
||||||
|
'id' => $troopLeaderID,
|
||||||
|
'name' => $troopName,
|
||||||
|
'turntime' => $troopTurnTime,
|
||||||
|
'reservedCommand' => [],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
$reservedCommand = [];
|
$reservedCommand = [];
|
||||||
if ($this->permission >= 1) {
|
if ($this->permission >= 1 || count($troops)) {
|
||||||
$nonNPCGeneralIDList = [];
|
$reservedCommandTargetGeneralIDList = [];
|
||||||
foreach ($rawGeneralList as $rawGeneral) {
|
|
||||||
if ($rawGeneral['npc'] < 2) {
|
if($this->permission >= 1){
|
||||||
$nonNPCGeneralIDList[] = $rawGeneral['no'];
|
foreach ($rawGeneralList as $rawGeneral) {
|
||||||
|
if ($rawGeneral['npc'] < 2) {
|
||||||
|
$reservedCommandTargetGeneralIDList[$rawGeneral['no']] = $rawGeneral['no'];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
foreach($troops as $troop){
|
||||||
|
$reservedCommandTargetGeneralIDList[$troop['id']] = $troop['id'];
|
||||||
|
}
|
||||||
|
|
||||||
$rawTurnList = $db->query(
|
|
||||||
'SELECT general_id, turn_idx, action, arg, brief FROM general_turn WHERE general_id IN %li AND turn_idx < 5 ORDER BY general_id asc, turn_idx asc',
|
|
||||||
$nonNPCGeneralIDList
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach ($rawTurnList as $rawTurn) {
|
if($reservedCommandTargetGeneralIDList){
|
||||||
[
|
$rawTurnList = $db->query(
|
||||||
'general_id' => $generalID,
|
'SELECT general_id, turn_idx, action, arg, brief FROM general_turn WHERE general_id IN %li AND turn_idx < 5 ORDER BY general_id asc, turn_idx asc',
|
||||||
'action' => $action,
|
array_values($reservedCommandTargetGeneralIDList)
|
||||||
'arg' => $arg,
|
);
|
||||||
'brief' => $brief,
|
|
||||||
] = $rawTurn;
|
foreach ($rawTurnList as $rawTurn) {
|
||||||
if (!key_exists($generalID, $reservedCommand)) {
|
[
|
||||||
$reservedCommand[$generalID] = [];
|
'general_id' => $generalID,
|
||||||
|
'action' => $action,
|
||||||
|
'arg' => $arg,
|
||||||
|
'brief' => $brief,
|
||||||
|
] = $rawTurn;
|
||||||
|
if (!key_exists($generalID, $reservedCommand)) {
|
||||||
|
$reservedCommand[$generalID] = [];
|
||||||
|
}
|
||||||
|
$reservedCommand[$generalID][] = [
|
||||||
|
'action' => $action,
|
||||||
|
'arg' => $arg,
|
||||||
|
'brief' => $brief
|
||||||
|
];
|
||||||
}
|
}
|
||||||
$reservedCommand[$generalID][] = [
|
|
||||||
'action' => $action,
|
|
||||||
'arg' => $arg,
|
|
||||||
'brief' => $brief
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,6 +277,17 @@ class GeneralList extends \sammo\BaseAPI
|
|||||||
$resultColumns[$column] = $column;
|
$resultColumns[$column] = $column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach ($troops as $troop){
|
||||||
|
$troopLeaderID = $troop['id'];
|
||||||
|
$troop['reservedCommand'] = array_map(function($turnObj){
|
||||||
|
$brief = $turnObj['brief'];
|
||||||
|
if($brief == '집합'){
|
||||||
|
return $brief;
|
||||||
|
}
|
||||||
|
return '-';
|
||||||
|
}, $specialViewFilter['reservedCommand']($rawGeneralList[$troopLeaderID]));
|
||||||
|
}
|
||||||
|
|
||||||
$generalList = [];
|
$generalList = [];
|
||||||
foreach ($rawGeneralList as $rawGeneral) {
|
foreach ($rawGeneralList as $rawGeneral) {
|
||||||
//General 생성?
|
//General 생성?
|
||||||
@@ -272,25 +308,14 @@ class GeneralList extends \sammo\BaseAPI
|
|||||||
$generalList[] = $item;
|
$generalList[] = $item;
|
||||||
}
|
}
|
||||||
|
|
||||||
$troops = [];
|
|
||||||
foreach ($db->queryAllLists('SELECT troop_leader,name FROM troop WHERE nation = %i', $nationID) as [$troopLeaderID, $troopName]) {
|
|
||||||
if (!key_exists($troopLeaderID, $rawGeneralList)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$troopTurnTime = $rawGeneralList[$troopLeaderID]['turntime'];
|
|
||||||
$troops[] = [
|
|
||||||
'id' => $troopLeaderID,
|
|
||||||
'name' => $troopName,
|
|
||||||
'turntime' => $troopTurnTime
|
|
||||||
];
|
|
||||||
}
|
|
||||||
$result = [
|
$result = [
|
||||||
'result' => true,
|
'result' => true,
|
||||||
'permission' => $this->permission,
|
'permission' => $this->permission,
|
||||||
'column' => array_keys($resultColumns),
|
'column' => array_keys($resultColumns),
|
||||||
'list' => $generalList,
|
'list' => $generalList,
|
||||||
'troops' => $troops,
|
'troops' => array_values($troops),
|
||||||
'env' => $env,
|
'env' => $env,
|
||||||
|
'myGeneralID' => $session->generalID,
|
||||||
];
|
];
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use sammo\Validator;
|
|||||||
|
|
||||||
use function sammo\checkSecretPermission;
|
use function sammo\checkSecretPermission;
|
||||||
|
|
||||||
|
/** @deprecated */
|
||||||
class SetTroopName extends \sammo\BaseAPI
|
class SetTroopName extends \sammo\BaseAPI
|
||||||
{
|
{
|
||||||
public function validateArgs(): ?string
|
public function validateArgs(): ?string
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace sammo\API\Troop;
|
||||||
|
|
||||||
|
use sammo\Session;
|
||||||
|
use DateTimeInterface;
|
||||||
|
use sammo\DB;
|
||||||
|
|
||||||
|
class ExitTroop extends \sammo\BaseAPI
|
||||||
|
{
|
||||||
|
public function validateArgs(): ?string
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSessionMode(): int
|
||||||
|
{
|
||||||
|
return static::REQ_GAME_LOGIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag)
|
||||||
|
{
|
||||||
|
$generalID = $session->generalID;
|
||||||
|
$db = DB::db();
|
||||||
|
|
||||||
|
$troopID = $db->queryFirstField('SELECT troop FROM general WHERE no = %i', $generalID);
|
||||||
|
if($troopID == 0){
|
||||||
|
return '부대에 소속되어 있지 않습니다.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if($generalID != $troopID){
|
||||||
|
$db->update('general', [
|
||||||
|
'troop' => 0,
|
||||||
|
], '`no` = %i', $generalID);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//부대장이다.
|
||||||
|
$db->update('general', [
|
||||||
|
'troop' => 0,
|
||||||
|
], '`troop` = %i', $troopID);
|
||||||
|
$db->delete('troop', 'troop_leader = %i', $troopID);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace sammo\API\Troop;
|
||||||
|
|
||||||
|
use sammo\Session;
|
||||||
|
use DateTimeInterface;
|
||||||
|
use sammo\DB;
|
||||||
|
use sammo\StringUtil;
|
||||||
|
use sammo\Validator;
|
||||||
|
|
||||||
|
class JoinTroop extends \sammo\BaseAPI
|
||||||
|
{
|
||||||
|
public function validateArgs(): ?string
|
||||||
|
{
|
||||||
|
$v = new Validator($this->args);
|
||||||
|
$v->rule('required', [
|
||||||
|
'troopID',
|
||||||
|
])
|
||||||
|
->rule('integer', 'troopID');
|
||||||
|
|
||||||
|
if (!$v->validate()) {
|
||||||
|
return $v->errorStr();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSessionMode(): int
|
||||||
|
{
|
||||||
|
return static::REQ_GAME_LOGIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag)
|
||||||
|
{
|
||||||
|
$userID = $session->userID;
|
||||||
|
$troopID = $this->args['troopID'];
|
||||||
|
|
||||||
|
$db = DB::db();
|
||||||
|
$me = $db->queryFirstRow('SELECT `no`,nation,`troop`,`officer_level`,permission,penalty FROM general WHERE `owner`=%i', $userID);
|
||||||
|
if ($me['troop'] != 0) {
|
||||||
|
return '이미 부대에 소속되어 있습니다.';
|
||||||
|
}
|
||||||
|
$nationID = $me['nation'];
|
||||||
|
if ($nationID == 0) {
|
||||||
|
return '국가에 소속되어 있지 않습니다.';
|
||||||
|
}
|
||||||
|
|
||||||
|
$troopExists = $db->queryFirstField('SELECT `troop_leader` FROM `troop` WHERE `troop_leader` = %i AND `nation` = %i', $troopID, $nationID);
|
||||||
|
if (!$troopExists) {
|
||||||
|
return '부대가 올바르지 않습니다.';
|
||||||
|
}
|
||||||
|
$generalID = $me['no'];
|
||||||
|
|
||||||
|
$db->update('general', [
|
||||||
|
'troop' => $troopID,
|
||||||
|
], '`no` = %i AND `troop` = %i', $generalID, 0);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace sammo\API\Troop;
|
||||||
|
|
||||||
|
use sammo\Session;
|
||||||
|
use DateTimeInterface;
|
||||||
|
use sammo\DB;
|
||||||
|
use sammo\Validator;
|
||||||
|
|
||||||
|
class KickFromTroop extends \sammo\BaseAPI
|
||||||
|
{
|
||||||
|
public function validateArgs(): ?string
|
||||||
|
{
|
||||||
|
$v = new Validator($this->args);
|
||||||
|
$v->rule('required', [
|
||||||
|
'troopID',
|
||||||
|
'generalID',
|
||||||
|
])
|
||||||
|
->rule('integer', 'troopID')
|
||||||
|
->rule('integer', 'generalID');
|
||||||
|
|
||||||
|
if (!$v->validate()) {
|
||||||
|
return $v->errorStr();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getRequiredSessionMode(): int
|
||||||
|
{
|
||||||
|
return static::REQ_GAME_LOGIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag)
|
||||||
|
{
|
||||||
|
$generalID = $this->args['generalID'];
|
||||||
|
$db = DB::db();
|
||||||
|
|
||||||
|
$troopID = $db->queryFirstField('SELECT troop FROM general WHERE no = %i', $generalID);
|
||||||
|
if($troopID == 0){
|
||||||
|
return '부대에 소속되어 있지 않습니다.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if($troopID != $this->args['troopID']){
|
||||||
|
return '다른 부대에 소속되어 있습니다.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if($troopID == $generalID){
|
||||||
|
return '부대장을 추방할 수 없습니다.';
|
||||||
|
}
|
||||||
|
|
||||||
|
$db->update('general', [
|
||||||
|
'troop' => 0,
|
||||||
|
], '`no` = %i AND `troop` = %i', $generalID, $troopID);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace sammo\API\Troop;
|
||||||
|
|
||||||
|
use sammo\Session;
|
||||||
|
use DateTimeInterface;
|
||||||
|
use sammo\DB;
|
||||||
|
use sammo\StringUtil;
|
||||||
|
use sammo\Validator;
|
||||||
|
|
||||||
|
class NewTroop extends \sammo\BaseAPI
|
||||||
|
{
|
||||||
|
public function validateArgs(): ?string
|
||||||
|
{
|
||||||
|
$v = new Validator($this->args);
|
||||||
|
$v->rule('required', [
|
||||||
|
'troopName',
|
||||||
|
])
|
||||||
|
->rule('stringWidthBetween', 'troopName', 1, 18);
|
||||||
|
|
||||||
|
if (!$v->validate()) {
|
||||||
|
return $v->errorStr();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSessionMode(): int
|
||||||
|
{
|
||||||
|
return static::REQ_GAME_LOGIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag)
|
||||||
|
{
|
||||||
|
$userID = $session->userID;
|
||||||
|
$troopName = StringUtil::neutralize($this->args['troopName']);
|
||||||
|
if(!$troopName){
|
||||||
|
return '부대 이름이 없습니다.';
|
||||||
|
}
|
||||||
|
|
||||||
|
$db = DB::db();
|
||||||
|
$me = $db->queryFirstRow('SELECT `no`,nation,`troop`,`officer_level`,permission,penalty FROM general WHERE `owner`=%i', $userID);
|
||||||
|
if($me['troop'] != 0){
|
||||||
|
return '이미 부대에 소속되어 있습니다.';
|
||||||
|
}
|
||||||
|
$nationID = $me['nation'];
|
||||||
|
if($nationID == 0){
|
||||||
|
return '국가에 소속되어 있지 않습니다.';
|
||||||
|
}
|
||||||
|
|
||||||
|
$generalID = $me['no'];
|
||||||
|
|
||||||
|
$db->insert('troop', [
|
||||||
|
'name'=>$troopName,
|
||||||
|
'troop_leader'=>$generalID,
|
||||||
|
'nation'=>$nationID,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if($db->affectedRows() == 0){
|
||||||
|
return '부대가 생성되지 않았습니다. 버그일 수 있습니다.';
|
||||||
|
}
|
||||||
|
|
||||||
|
$db->update('general', [
|
||||||
|
'troop'=>$generalID
|
||||||
|
], '`no` = %i', $generalID);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace sammo\API\Troop;
|
||||||
|
|
||||||
|
use sammo\Session;
|
||||||
|
use DateTimeInterface;
|
||||||
|
use sammo\DB;
|
||||||
|
use sammo\StringUtil;
|
||||||
|
use sammo\Validator;
|
||||||
|
|
||||||
|
use function sammo\checkSecretPermission;
|
||||||
|
|
||||||
|
class SetTroopName extends \sammo\BaseAPI
|
||||||
|
{
|
||||||
|
public function validateArgs(): ?string
|
||||||
|
{
|
||||||
|
$v = new Validator($this->args);
|
||||||
|
$v->rule('required', [
|
||||||
|
'troopID',
|
||||||
|
'troopName',
|
||||||
|
])
|
||||||
|
->rule('stringWidthBetween', 'troopName', 1, 18)
|
||||||
|
->rule('integer', 'troopID');
|
||||||
|
|
||||||
|
if (!$v->validate()) {
|
||||||
|
return $v->errorStr();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSessionMode(): int
|
||||||
|
{
|
||||||
|
return static::REQ_GAME_LOGIN | static::REQ_READ_ONLY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag)
|
||||||
|
{
|
||||||
|
$userID = $session->userID;
|
||||||
|
$db = DB::db();
|
||||||
|
$me = $db->queryFirstRow('SELECT `no`,nation,`officer_level`,permission,penalty FROM general WHERE `owner`=%i', $userID);
|
||||||
|
$permission = checkSecretPermission($me, false);
|
||||||
|
$troopID = $this->args['troopID'];
|
||||||
|
|
||||||
|
$generalID = $me['no'];
|
||||||
|
if($generalID != $troopID && $permission < 4){
|
||||||
|
return "권한이 부족합니다.";
|
||||||
|
}
|
||||||
|
|
||||||
|
$troopName = StringUtil::neutralize($this->args['troopName']);
|
||||||
|
if(!$troopName){
|
||||||
|
return '부대 이름이 없습니다.';
|
||||||
|
}
|
||||||
|
|
||||||
|
$nationID = $me['nation'];
|
||||||
|
$db->update('troop', [
|
||||||
|
'name'=>$troopName
|
||||||
|
], 'troop_leader=%i AND `nation`=%i',$troopID, $nationID);
|
||||||
|
|
||||||
|
if($db->affectedRows() == 0){
|
||||||
|
return '부대가 없습니다.';
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?=$btnBegin??''?><a href='v_board.php' class='commandButton <?=$btnClass??""?> <?= $meLevel >= 1 ? '' : 'disabled' ?>'>회 의 실</a><?=$btnEnd??''?>
|
<?=$btnBegin??''?><a href='v_board.php' class='commandButton <?=$btnClass??""?> <?= $meLevel >= 1 ? '' : 'disabled' ?>'>회 의 실</a><?=$btnEnd??''?>
|
||||||
<?=$btnBegin??''?><a href='v_board.php?isSecret=true' class='commandButton <?= $permission >= 2 ? '' : 'disabled' ?> <?=$btnClass??""?>'>기 밀 실</a><?=$btnEnd??''?>
|
<?=$btnBegin??''?><a href='v_board.php?isSecret=true' class='commandButton <?= $permission >= 2 ? '' : 'disabled' ?> <?=$btnClass??""?>'>기 밀 실</a><?=$btnEnd??''?>
|
||||||
<?=$btnBegin??''?><a href='b_troop.php' class='commandButton <?= ($meLevel >= 1 && $nationLevel >= 1) ? '' : 'disabled' ?> <?=$btnClass??""?>'>부대 편성</a><?=$btnEnd??''?>
|
<?=$btnBegin??''?><a href='v_troop.php' class='commandButton <?= ($meLevel >= 1 && $nationLevel >= 1) ? '' : 'disabled' ?> <?=$btnClass??""?>'>부대 편성</a><?=$btnEnd??''?>
|
||||||
<?=$btnBegin??''?><a href='t_diplomacy.php' class='commandButton <?= $showSecret ? '' : 'disabled' ?> <?=$btnClass??""?>'>외 교 부</a><?=$btnEnd??''?>
|
<?=$btnBegin??''?><a href='t_diplomacy.php' class='commandButton <?= $showSecret ? '' : 'disabled' ?> <?=$btnClass??""?>'>외 교 부</a><?=$btnEnd??''?>
|
||||||
<?=$btnBegin??''?><a href='b_myBossInfo.php' class='commandButton <?= $meLevel >= 1 ? '' : 'disabled' ?> <?=$btnClass??""?>'>인 사 부</a><?=$btnEnd??''?>
|
<?=$btnBegin??''?><a href='b_myBossInfo.php' class='commandButton <?= $meLevel >= 1 ? '' : 'disabled' ?> <?=$btnClass??""?>'>인 사 부</a><?=$btnEnd??''?>
|
||||||
<?=$btnBegin??''?><a href='v_nationStratFinan.php' class='commandButton <?= $showSecret ? '' : 'disabled' ?> <?=$btnClass??""?>'>내 무 부</a><?=$btnEnd??''?>
|
<?=$btnBegin??''?><a href='v_nationStratFinan.php' class='commandButton <?= $showSecret ? '' : 'disabled' ?> <?=$btnClass??""?>'>내 무 부</a><?=$btnEnd??''?>
|
||||||
|
|||||||
@@ -0,0 +1,645 @@
|
|||||||
|
<template>
|
||||||
|
<BContainer id="container" ref="container" :toast="{ root: true }">
|
||||||
|
<TopBackBar :reloadable="true" title="부대 편성" @reload="refresh" />
|
||||||
|
<div v-if="asyncReady && gameConstStore && me" id="troopList" class="bg0">
|
||||||
|
<div v-for="[troopID, troop] of troopList" :key="troopID" class="troopItem">
|
||||||
|
<div class="troopInfo">
|
||||||
|
{{ troop.troopName }}<br />
|
||||||
|
【 {{ gameConstStore.cityConst[troop.troopLeader.city].name }} 】
|
||||||
|
</div>
|
||||||
|
<div class="troopTurn">【턴】 {{ troop.turnTime.slice(14, 19) }}</div>
|
||||||
|
<div class="troopLeaderIcon">
|
||||||
|
<img height="64" width="64" :src="getIconPath(troop.troopLeader.imgsvr, troop.troopLeader.picture)" />
|
||||||
|
</div>
|
||||||
|
<div class="troopLeaderName">
|
||||||
|
{{ troop.troopLeader.name }}
|
||||||
|
</div>
|
||||||
|
<div class="troopMembers">
|
||||||
|
<template v-for="(member, idx) in troop.members" :key="member.no">
|
||||||
|
<template v-if="idx != 0">, </template>
|
||||||
|
<span
|
||||||
|
v-if="member.troop == member.no"
|
||||||
|
class="troopMember troopLeader"
|
||||||
|
@mouseover="setPopup($event, member)"
|
||||||
|
@mouseout="setPopup($event, undefined)"
|
||||||
|
>
|
||||||
|
{{ member.name }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-else
|
||||||
|
class="troopMember"
|
||||||
|
@mouseover="setPopup($event, member)"
|
||||||
|
@mouseout="setPopup($event, undefined)"
|
||||||
|
>
|
||||||
|
{{ member.name }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
({{ troop.members.length }}명)
|
||||||
|
</div>
|
||||||
|
<div class="troopReservedCommand">
|
||||||
|
<div v-for="(brief, idx) in troop.reservedCommandBrief" :key="idx">
|
||||||
|
{{ `${idx + 1}: ${brief}` }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="troopAction">
|
||||||
|
<div
|
||||||
|
v-if="tryTroopActionTarget.type === undefined || tryTroopActionTarget.targetTroop !== troop.troopID"
|
||||||
|
class="d-grid"
|
||||||
|
>
|
||||||
|
<BButton v-if="!me.troop" variant="primary" @click="joinTroop(troop.troopID)">부대 탑승</BButton>
|
||||||
|
<BButton
|
||||||
|
v-if="me.troop == troop.troopID"
|
||||||
|
:variant="me.troop == me.no ? 'danger' : 'primary'"
|
||||||
|
@click="exitTroop()"
|
||||||
|
>{{ me.no == me.troop ? "부대 해산" : "부대 탈퇴" }}</BButton
|
||||||
|
>
|
||||||
|
<BButton
|
||||||
|
v-if="me.troop == troop.troopID && me.no == me.troop"
|
||||||
|
@click="openKickTroopMemberDialog(troop)"
|
||||||
|
>부대원 추방...</BButton
|
||||||
|
>
|
||||||
|
<BButton variant="info" @click="openChangeTroopNameDialog(troop)"
|
||||||
|
>부대명 변경...</BButton
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="tryTroopActionTarget.type === 'changeName'" class="row gx-0">
|
||||||
|
<div class="col-12 bg1 center" style="padding: 0.2em">부대명 변경</div>
|
||||||
|
<div class="col-12 d-grid">
|
||||||
|
<BFormInput v-model="newTroopName" :trim="true" type="text" />
|
||||||
|
</div>
|
||||||
|
<div class="col-6 d-grid">
|
||||||
|
<BButton variant="secondary" @click="tryTroopActionTarget = { type: undefined, targetTroop: 0 }"
|
||||||
|
>취소</BButton
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="col-6 d-grid">
|
||||||
|
<BButton variant="primary" @click="changeTroopName(troop)">변경</BButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="tryTroopActionTarget.type === 'kickMember'" class="row gx-0">
|
||||||
|
<div class="col-12 bg1 center" style="padding: 0.2em">부대명 추방</div>
|
||||||
|
<div class="col-12 d-grid">
|
||||||
|
<BFormSelect v-model="kickTroopMemberID">
|
||||||
|
<template v-for="member of troop.members" :key="member.no">
|
||||||
|
<BFormSelectOption v-if="member.no != troop.troopID" :value="member.no">
|
||||||
|
{{ member.name }}
|
||||||
|
</BFormSelectOption>
|
||||||
|
</template>
|
||||||
|
</BFormSelect>
|
||||||
|
</div>
|
||||||
|
<div class="col-6 d-grid">
|
||||||
|
<BButton variant="secondary" @click="tryTroopActionTarget = { type: undefined, targetTroop: 0 }"
|
||||||
|
>취소</BButton
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="col-6 d-grid">
|
||||||
|
<BButton variant="primary" @click="kickTroopMember(troop)">추방</BButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="asyncReady && gameConstStore && me" class="row additionalTroopOptions">
|
||||||
|
<div v-if="me.troop == 0" class="col-6 col-md-3">
|
||||||
|
<div class="row gx-0 makeNewTroop">
|
||||||
|
<div class="bg1 col-12 center" style="font-size: 1.2em">부대 창설</div>
|
||||||
|
<div class="troopNameField col-8 d-grid">
|
||||||
|
<BFormInput v-model="newTroopName" :trim="true" type="text" />
|
||||||
|
</div>
|
||||||
|
<div class="col-4 d-grid">
|
||||||
|
<BButton @click="makeTroop">부대 창설</BButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<BottomBar />
|
||||||
|
<div
|
||||||
|
v-if="asyncReady && gameConstStore"
|
||||||
|
id="generalPopup"
|
||||||
|
:style="{ display: popupGeneralTarget ? 'block' : 'none', top: `${popupTop}px` }"
|
||||||
|
>
|
||||||
|
<div v-if="!popupGeneralTarget || !nationInfo"></div>
|
||||||
|
<template v-else-if="popupGeneralTarget.st1">
|
||||||
|
<GeneralBasicCard
|
||||||
|
:general="popupGeneralTarget"
|
||||||
|
:troopInfo="convBasicCardTroopInfo(popupGeneralTarget)"
|
||||||
|
:nation="nationInfo"
|
||||||
|
/>
|
||||||
|
<GeneralSupplementCard :general="popupGeneralTarget" :showCommandList="true" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<GeneralLiteCard v-else :general="popupGeneralTarget" :nation="nationInfo" />
|
||||||
|
</div>
|
||||||
|
</BContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import TopBackBar from "@/components/TopBackBar.vue";
|
||||||
|
import BottomBar from "@/components/BottomBar.vue";
|
||||||
|
import { onMounted, provide, ref } from "vue";
|
||||||
|
import { SammoAPI } from "./SammoAPI";
|
||||||
|
import { BContainer, BButton, useToast, BFormInput, BFormSelect, BFormSelectOption } from "bootstrap-vue-3";
|
||||||
|
import { unwrap } from "./util/unwrap";
|
||||||
|
import { isString } from "lodash";
|
||||||
|
import type { GeneralListItem, GeneralListItemP1 } from "./defs/API/Nation";
|
||||||
|
import { merge2DArrToObjectArr } from "./util/merge2DArrToObjectArr";
|
||||||
|
import { convertIterableToMap } from "@/util/convertIterableToMap";
|
||||||
|
import { GameConstStore, getGameConstStore } from "./GameConstStore";
|
||||||
|
import { getIconPath } from "@/util/getIconPath";
|
||||||
|
import GeneralBasicCard from "./components/GeneralBasicCard.vue";
|
||||||
|
import type { NationStaticItem } from "./defs";
|
||||||
|
import GeneralLiteCard from "./components/GeneralLiteCard.vue";
|
||||||
|
import GeneralSupplementCard from "./components/GeneralSupplementCard.vue";
|
||||||
|
import { pick } from "./util/JosaUtil";
|
||||||
|
const toasts = unwrap(useToast());
|
||||||
|
|
||||||
|
const asyncReady = ref(false);
|
||||||
|
const gameConstStore = ref<GameConstStore>();
|
||||||
|
provide("gameConstStore", gameConstStore);
|
||||||
|
const storeP = getGameConstStore().then((store) => {
|
||||||
|
gameConstStore.value = store;
|
||||||
|
});
|
||||||
|
|
||||||
|
void Promise.all([storeP]).then(() => {
|
||||||
|
asyncReady.value = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
const container = ref<HTMLElement>();
|
||||||
|
|
||||||
|
const popupGeneralTarget = ref<GeneralListItem>();
|
||||||
|
const popupTop = ref<number>(0);
|
||||||
|
|
||||||
|
function convBasicCardTroopInfo(general: GeneralListItemP1): { leader: GeneralListItemP1; name: string } | undefined {
|
||||||
|
const troop = troopList.value.get(general.troop);
|
||||||
|
if (!troop) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const troopLeader = troop.troopLeader;
|
||||||
|
if (!troopLeader.st1) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
leader: troopLeader,
|
||||||
|
name: troop.troopName,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function setPopup(e: MouseEvent, general: GeneralListItem | undefined) {
|
||||||
|
if (general === undefined || !e.target) {
|
||||||
|
popupTop.value = 0;
|
||||||
|
popupGeneralTarget.value = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log("e", e);
|
||||||
|
|
||||||
|
const target = e.target as HTMLElement;
|
||||||
|
popupGeneralTarget.value = general;
|
||||||
|
let parent = target.parentElement;
|
||||||
|
console.log("parent", parent);
|
||||||
|
while (parent !== null && !parent.classList.contains("troopMembers")) {
|
||||||
|
parent = parent.parentElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent === null) {
|
||||||
|
popupTop.value = 0;
|
||||||
|
popupGeneralTarget.value = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(parent.offsetTop, parent.offsetHeight, parent);
|
||||||
|
|
||||||
|
popupTop.value = parent.offsetTop + parent.offsetHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
type TroopInfo = {
|
||||||
|
troopID: number;
|
||||||
|
troopName: string;
|
||||||
|
troopLeader: GeneralListItem;
|
||||||
|
turnTime: string;
|
||||||
|
reservedCommandBrief: string[];
|
||||||
|
members: GeneralListItem[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const me = ref<GeneralListItem>({} as GeneralListItem);
|
||||||
|
|
||||||
|
const troopList = ref(new Map<number, TroopInfo>());
|
||||||
|
const generalList = ref(new Map<number, GeneralListItem>());
|
||||||
|
const nationInfo = ref<NationStaticItem>();
|
||||||
|
|
||||||
|
const newTroopName = ref("");
|
||||||
|
const kickTroopMemberID = ref(0);
|
||||||
|
|
||||||
|
type TroopAction = {
|
||||||
|
type: "kickMember" | "changeName" | undefined;
|
||||||
|
targetTroop: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const tryTroopActionTarget = ref<TroopAction>({
|
||||||
|
type: undefined,
|
||||||
|
targetTroop: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
async function refresh() {
|
||||||
|
try {
|
||||||
|
const generalListP = SammoAPI.Nation.GeneralList();
|
||||||
|
const nationP = SammoAPI.Nation.GetNationInfo({});
|
||||||
|
nationInfo.value = (await nationP).nation;
|
||||||
|
const { column, list, permission, troops, env, myGeneralID } = await generalListP;
|
||||||
|
|
||||||
|
//빠른 턴 순서로 정렬되어있다.
|
||||||
|
|
||||||
|
//XXX: 로직상 똑같은데....
|
||||||
|
if (permission == 0) {
|
||||||
|
const rawGeneralList = merge2DArrToObjectArr(column, list);
|
||||||
|
generalList.value = convertIterableToMap(
|
||||||
|
rawGeneralList.map((v) => {
|
||||||
|
return { permission, st0: true, st1: false, st2: false, ...v };
|
||||||
|
}),
|
||||||
|
"no"
|
||||||
|
);
|
||||||
|
} else if (permission == 1) {
|
||||||
|
const rawGeneralList = merge2DArrToObjectArr(column, list);
|
||||||
|
generalList.value = convertIterableToMap(
|
||||||
|
rawGeneralList.map((v) => {
|
||||||
|
return { permission, st0: true, st1: true, st2: false, ...v };
|
||||||
|
}),
|
||||||
|
"no"
|
||||||
|
);
|
||||||
|
} else if ([2, 3, 4].includes(permission)) {
|
||||||
|
const rawGeneralList = merge2DArrToObjectArr(column, list);
|
||||||
|
generalList.value = convertIterableToMap(
|
||||||
|
rawGeneralList.map((v) => {
|
||||||
|
return { permission, st0: true, st1: true, st2: true, ...v };
|
||||||
|
}),
|
||||||
|
"no"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw `?? ${permission}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
me.value = unwrap(generalList.value.get(myGeneralID));
|
||||||
|
|
||||||
|
troopList.value = new Map();
|
||||||
|
|
||||||
|
for (const { id: troopLeaderID, name: troopName, turntime: troopTurntime, reservedCommand } of troops) {
|
||||||
|
if (!generalList.value.has(troopLeaderID)) {
|
||||||
|
toasts.warning({
|
||||||
|
title: "경고",
|
||||||
|
body: `${troopName} 부대장(${troopLeaderID})이 아국 소속이 아닌 것 같습니다.`,
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
troopList.value.set(troopLeaderID, {
|
||||||
|
troopID: troopLeaderID,
|
||||||
|
troopName,
|
||||||
|
troopLeader: unwrap(generalList.value.get(troopLeaderID)),
|
||||||
|
turnTime: troopTurntime,
|
||||||
|
reservedCommandBrief: reservedCommand,
|
||||||
|
members: [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const general of generalList.value.values()) {
|
||||||
|
const troopID = general.troop;
|
||||||
|
if (!troopID) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const troop = troopList.value.get(troopID);
|
||||||
|
if (troop === undefined) {
|
||||||
|
console.error(`부대 소속 오류: ${general.no} in ${troopID}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
troop.members.push(general);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (isString(e)) {
|
||||||
|
toasts.danger({
|
||||||
|
title: "오류",
|
||||||
|
body: e,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.error(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
void refresh();
|
||||||
|
});
|
||||||
|
|
||||||
|
async function makeTroop() {
|
||||||
|
const troopName = newTroopName.value;
|
||||||
|
try {
|
||||||
|
await SammoAPI.Troop.NewTroop({
|
||||||
|
troopName,
|
||||||
|
});
|
||||||
|
newTroopName.value = "";
|
||||||
|
toasts.info({
|
||||||
|
title: "완료",
|
||||||
|
body: `${troopName} 부대가 생성되었습니다.`,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
if (isString(e)) {
|
||||||
|
toasts.danger({
|
||||||
|
title: "오류",
|
||||||
|
body: e,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
await refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function joinTroop(troopID: number) {
|
||||||
|
try {
|
||||||
|
await SammoAPI.Troop.JoinTroop({
|
||||||
|
troopID,
|
||||||
|
});
|
||||||
|
toasts.info({
|
||||||
|
title: "완료",
|
||||||
|
body: ` ${troopList.value.get(troopID)?.troopName} 부대에 가입했습니다.`,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
if (isString(e)) {
|
||||||
|
toasts.danger({
|
||||||
|
title: "오류",
|
||||||
|
body: e,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
await refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function exitTroop() {
|
||||||
|
const isTroopLeader = me.value.troop == me.value.no;
|
||||||
|
const troopName = troopList.value.get(me.value.troop)?.troopName ?? "??";
|
||||||
|
|
||||||
|
if (isTroopLeader) {
|
||||||
|
if (!confirm(`${troopName} 부대를 해산하겠습니까?`)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!confirm(`${troopName} 부대에서 탈퇴하겠습니까?`)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await SammoAPI.Troop.ExitTroop();
|
||||||
|
|
||||||
|
if (isTroopLeader) {
|
||||||
|
toasts.info({
|
||||||
|
title: "완료",
|
||||||
|
body: `부대를 해산했습니다.`,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
toasts.info({
|
||||||
|
title: "완료",
|
||||||
|
body: `부대에서 탈퇴했습니다.`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (isString(e)) {
|
||||||
|
toasts.danger({
|
||||||
|
title: "오류",
|
||||||
|
body: e,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
await refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
function openChangeTroopNameDialog(troop: TroopInfo){
|
||||||
|
tryTroopActionTarget.value = {
|
||||||
|
type: 'changeName',
|
||||||
|
targetTroop: troop.troopID
|
||||||
|
}
|
||||||
|
newTroopName.value = troop.troopName;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function changeTroopName(troop: TroopInfo) {
|
||||||
|
const troopID = troop.troopID;
|
||||||
|
const troopName = newTroopName.value;
|
||||||
|
const oldTroopName = troop.troopName;
|
||||||
|
const josaRo = pick(troopName, "로");
|
||||||
|
if (!confirm(`${oldTroopName} 부대의 이름을 ${troopName}${josaRo} 바꾸시겠습니까?`)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await SammoAPI.Troop.SetTroopName({
|
||||||
|
troopID,
|
||||||
|
troopName,
|
||||||
|
});
|
||||||
|
|
||||||
|
toasts.info({
|
||||||
|
title: "완료",
|
||||||
|
body: `부대명을 변경했습니다.`,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
if (isString(e)) {
|
||||||
|
toasts.danger({
|
||||||
|
title: "오류",
|
||||||
|
body: e,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
await refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function openKickTroopMemberDialog(troop: TroopInfo){
|
||||||
|
tryTroopActionTarget.value = {
|
||||||
|
type: 'kickMember',
|
||||||
|
targetTroop: troop.troopID
|
||||||
|
}
|
||||||
|
for(const member of troop.members){
|
||||||
|
if(member.no == troop.troopID){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
kickTroopMemberID.value = member.no;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function kickTroopMember(troop: TroopInfo) {
|
||||||
|
const kickMember = generalList.value.get(kickTroopMemberID.value);
|
||||||
|
if(kickMember === undefined){
|
||||||
|
toasts.danger({
|
||||||
|
title: "오류",
|
||||||
|
body: "잘못된 접근입니다.",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const troopID = troop.troopID;
|
||||||
|
const troopName = troop.troopName;
|
||||||
|
|
||||||
|
const generalID = kickMember.no;
|
||||||
|
const generalName = kickMember.name;
|
||||||
|
const josaUl = pick(generalName, "을");
|
||||||
|
if (!confirm(`${troopName} 부대에서 ${generalName}${josaUl} 추방하시겠습니까?`)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await SammoAPI.Troop.KickFromTroop({
|
||||||
|
troopID,
|
||||||
|
generalID,
|
||||||
|
});
|
||||||
|
|
||||||
|
toasts.info({
|
||||||
|
title: "완료",
|
||||||
|
body: `${generalName}${josaUl} 추방했습니다.`,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
if (isString(e)) {
|
||||||
|
toasts.danger({
|
||||||
|
title: "오류",
|
||||||
|
body: e,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
await refresh();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import "@scss/common/break_500px.scss";
|
||||||
|
|
||||||
|
#container {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
#generalPopup {
|
||||||
|
position: absolute;
|
||||||
|
width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.additionalTroopOptions {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.makeNewTroop {
|
||||||
|
border: solid 1px gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.troopItem {
|
||||||
|
display: grid;
|
||||||
|
border-right: solid 1px gray;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
border-top: solid 1px gray;
|
||||||
|
border-left: solid 1px gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.troopInfo {
|
||||||
|
grid-column: 1/2;
|
||||||
|
grid-row: 1/2;
|
||||||
|
display: grid;
|
||||||
|
align-content: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.troopTurn {
|
||||||
|
grid-column: 1/2;
|
||||||
|
grid-row: 2/3;
|
||||||
|
display: grid;
|
||||||
|
align-content: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.troopLeaderIcon {
|
||||||
|
grid-column: 2/3;
|
||||||
|
grid-row: 1/2;
|
||||||
|
display: grid;
|
||||||
|
align-content: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.troopLeaderName {
|
||||||
|
grid-column: 2/3;
|
||||||
|
grid-row: 2/3;
|
||||||
|
display: grid;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.troopReservedCommand {
|
||||||
|
grid-column: 4/5;
|
||||||
|
grid-row: 1/3;
|
||||||
|
font-size: 85%;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.troopAction {
|
||||||
|
grid-column: 5/6;
|
||||||
|
grid-row: 1/3;
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
padding-top: 0.2em;
|
||||||
|
padding-bottom: 0.2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.troopMembers {
|
||||||
|
text-align: left;
|
||||||
|
padding: 0.5em 0.7em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@include media-1000px {
|
||||||
|
#container {
|
||||||
|
width: 1000px;
|
||||||
|
margin: 0 auto;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
#generalPopup {
|
||||||
|
left: 260px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.troopItem {
|
||||||
|
grid-template-rows: 65px 28px;
|
||||||
|
grid-template-columns: 130px 130px 1fr 100px 140px;
|
||||||
|
|
||||||
|
.troopMembers {
|
||||||
|
grid-column: 3/4;
|
||||||
|
grid-row: 1/3;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-of-type {
|
||||||
|
border-bottom: solid 1px gray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@include media-500px {
|
||||||
|
#container {
|
||||||
|
width: 500px;
|
||||||
|
margin: 0 auto;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
#generalPopup {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.troopItem {
|
||||||
|
grid-template-rows: 65px 28px auto;
|
||||||
|
grid-template-columns: 130px 130px 0px 100px 140px;
|
||||||
|
|
||||||
|
.troopMembers {
|
||||||
|
grid-column: 2/6;
|
||||||
|
grid-row: 3/4;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-of-type {
|
||||||
|
.troopMembers,
|
||||||
|
.troopTurn {
|
||||||
|
border-bottom: solid 1px gray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -227,12 +227,30 @@ const apiRealPath = {
|
|||||||
},
|
},
|
||||||
GetGeneralLogResponse
|
GetGeneralLogResponse
|
||||||
>,
|
>,
|
||||||
|
/** @deprecated */
|
||||||
SetTroopName: PATCH as APICallT<{
|
SetTroopName: PATCH as APICallT<{
|
||||||
troopID: number;
|
troopID: number;
|
||||||
troopName: string;
|
troopName: string;
|
||||||
}>,
|
}>,
|
||||||
GetNationInfo: GET as APICallT<{full?: boolean}, NationInfoResponse>,
|
GetNationInfo: GET as APICallT<{full?: boolean}, NationInfoResponse>,
|
||||||
},
|
},
|
||||||
|
Troop: {
|
||||||
|
NewTroop: POST as APICallT<{
|
||||||
|
troopName: string;
|
||||||
|
}>,
|
||||||
|
JoinTroop: PATCH as APICallT<{
|
||||||
|
troopID: number;
|
||||||
|
}>,
|
||||||
|
ExitTroop: PATCH as APICallT<undefined>,
|
||||||
|
SetTroopName: PATCH as APICallT<{
|
||||||
|
troopID: number;
|
||||||
|
troopName: string;
|
||||||
|
}>,
|
||||||
|
KickFromTroop: PATCH as APICallT<{
|
||||||
|
troopID: number;
|
||||||
|
generalID: number;
|
||||||
|
}>
|
||||||
|
},
|
||||||
Vote: {
|
Vote: {
|
||||||
AddComment: POST as APICallT<{
|
AddComment: POST as APICallT<{
|
||||||
voteID: number;
|
voteID: number;
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
"v_nationBetting": "v_nationBetting.ts",
|
"v_nationBetting": "v_nationBetting.ts",
|
||||||
"v_nationGeneral": "v_nationGeneral.ts",
|
"v_nationGeneral": "v_nationGeneral.ts",
|
||||||
"v_globalDiplomacy": "v_globalDiplomacy",
|
"v_globalDiplomacy": "v_globalDiplomacy",
|
||||||
|
"v_troop": "v_troop.ts",
|
||||||
"v_vote": "v_vote.ts"
|
"v_vote": "v_vote.ts"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
<template>
|
||||||
|
<div class="general-card-basic bg2">
|
||||||
|
<div class="general-icon" :style="{
|
||||||
|
backgroundImage: `url(${iconPath})`,
|
||||||
|
}"></div>
|
||||||
|
|
||||||
|
<div class="general-name" :style="{
|
||||||
|
color: isBrightColor(nation.color) ? '#000' : '#fff',
|
||||||
|
backgroundColor: nation.color,
|
||||||
|
}">
|
||||||
|
{{ general.name }} 【{{ general.officerLevelText }} | {{ generalTypeCall }} |
|
||||||
|
<span :style="{ color: injuryInfo.color }">{{ injuryInfo.text }}</span>
|
||||||
|
】
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg1">통솔</div>
|
||||||
|
<div>
|
||||||
|
<div class="row gx-0">
|
||||||
|
<div class="col">
|
||||||
|
<span :style="{ color: injuryInfo.color }">{{ general.leadership }}</span>
|
||||||
|
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||||
|
<span v-if="general.lbonus > 0" style="color: cyan">+{{ general.lbonus }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bg1">무력</div>
|
||||||
|
<div>
|
||||||
|
<div class="row gx-0">
|
||||||
|
<div class="col" :style="{ color: injuryInfo.color }">
|
||||||
|
{{ general.strength }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bg1">지력</div>
|
||||||
|
<div>
|
||||||
|
<div class="row gx-0">
|
||||||
|
<div class="col" :style="{ color: injuryInfo.color }">
|
||||||
|
{{ general.intel }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg1">자금</div>
|
||||||
|
<div>{{ general.gold.toLocaleString() }}</div>
|
||||||
|
<div class="bg1">군량</div>
|
||||||
|
<div>{{ general.rice.toLocaleString() }}</div>
|
||||||
|
<div class="bg1">성격</div>
|
||||||
|
<div v-b-tooltip.hover :title="personal.info ?? undefined">{{ personal.name }}</div>
|
||||||
|
|
||||||
|
<div class="bg1">특기</div>
|
||||||
|
<div>
|
||||||
|
<span v-b-tooltip.hover :title="specialDomestic.info ?? undefined"> {{ specialDomestic.name }}</span> /
|
||||||
|
<span v-b-tooltip.hover :title="specialWar.info ?? undefined"> {{ specialWar.name }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg1">Lv</div>
|
||||||
|
<div class="general-exp-level">
|
||||||
|
{{ general.explevel }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg1">연령</div>
|
||||||
|
<div :style="{ color: ageColor }">{{ general.age }}세</div>
|
||||||
|
|
||||||
|
<div class="bg1">삭턴</div>
|
||||||
|
<div>{{ general.killturn }} 턴</div>
|
||||||
|
|
||||||
|
<div class="bg1">벌점</div>
|
||||||
|
<div class="general-connect-score">
|
||||||
|
{{ formatConnectScore(general.connect) }} {{ general.connect.toLocaleString() }}점
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { GeneralListItemP0 } from "@/defs/API/Nation";
|
||||||
|
import { computed, inject, toRefs, type Ref } from "vue";
|
||||||
|
import { getIconPath } from "@/util/getIconPath";
|
||||||
|
import { isBrightColor } from "@/util/isBrightColor";
|
||||||
|
import { formatInjury } from "@/utilGame/formatInjury";
|
||||||
|
import type { NationStaticItem } from "@/defs";
|
||||||
|
import { unwrap } from "@/util/unwrap";
|
||||||
|
import type { GameConstStore } from "@/GameConstStore";
|
||||||
|
import { formatGeneralTypeCall } from "@/utilGame/formatGeneralTypeCall";
|
||||||
|
import { formatConnectScore } from "@/utilGame/formatConnectScore";
|
||||||
|
const gameConstStore = unwrap(inject<Ref<GameConstStore>>("gameConstStore"));
|
||||||
|
const props = defineProps<{
|
||||||
|
general: GeneralListItemP0;
|
||||||
|
nation: NationStaticItem;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const { general, nation } = toRefs(props);
|
||||||
|
const iconPath = computed(() => getIconPath(general.value.imgsvr, general.value.picture));
|
||||||
|
const injuryInfo = computed(() => {
|
||||||
|
const [text, color] = formatInjury(general.value.injury);
|
||||||
|
return {
|
||||||
|
text,
|
||||||
|
color,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const generalTypeCall = computed(() =>
|
||||||
|
formatGeneralTypeCall(
|
||||||
|
general.value.leadership,
|
||||||
|
general.value.strength,
|
||||||
|
general.value.intel,
|
||||||
|
gameConstStore.value.gameConst
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const personal = computed(
|
||||||
|
() => gameConstStore.value.iActionInfo.personality[general.value.personal] ?? { value: "None", name: "-" }
|
||||||
|
);
|
||||||
|
const specialDomestic = computed(
|
||||||
|
() =>
|
||||||
|
gameConstStore.value.iActionInfo.specialDomestic[general.value.specialDomestic] ?? {
|
||||||
|
value: "None",
|
||||||
|
name: `-`,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const specialWar = computed(
|
||||||
|
() =>
|
||||||
|
gameConstStore.value.iActionInfo.specialWar[general.value.specialWar] ?? {
|
||||||
|
value: "None",
|
||||||
|
name: `-`,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const ageColor = computed(() => {
|
||||||
|
const age = general.value.age;
|
||||||
|
const retirementYear = gameConstStore.value.gameConst.retirementYear;
|
||||||
|
if (age < retirementYear * 0.75) {
|
||||||
|
return "limegreen";
|
||||||
|
}
|
||||||
|
if (age < retirementYear) {
|
||||||
|
return "yellow";
|
||||||
|
}
|
||||||
|
return "red";
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.general-card-basic {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 64px repeat(3, 2fr 5fr);
|
||||||
|
grid-template-rows: repeat(9, calc(64px / 3));
|
||||||
|
text-align: center;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
border-bottom: 1px solid gray;
|
||||||
|
border-right: 1px solid gray;
|
||||||
|
|
||||||
|
>div.bg1,
|
||||||
|
>.general-crew-type-icon,
|
||||||
|
>.general-icon {
|
||||||
|
border-left: 1px solid gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
>div {
|
||||||
|
border-top: 1px solid gray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.general-icon {
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
background-size: contain;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
grid-row: 1 / 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.general-name {
|
||||||
|
grid-row: 1 / 2;
|
||||||
|
grid-column: 2 / 8;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.general-connect-score {
|
||||||
|
grid-column: 5 / 8;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -38,24 +38,24 @@
|
|||||||
<div class="bg1">피살</div>
|
<div class="bg1">피살</div>
|
||||||
<div>{{ general.deathcrew.toLocaleString() }}</div>
|
<div>{{ general.deathcrew.toLocaleString() }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div :class="[props.showCommandList ? 'col-7' : 'col', 'general-card-dex']">
|
<div :class="[(props.showCommandList && general.reservedCommand) ? 'col-8' : 'col', 'general-card-dex']">
|
||||||
<div class="part-title bg1">숙련도</div>
|
<div class="part-title bg1">숙련도</div>
|
||||||
<template v-for="[dexType, dex, dexInfo] of dexList" :key="dexType">
|
<template v-for="[dexType, dex, dexInfo] of dexList" :key="dexType">
|
||||||
<div class="bg1">{{ dexType }}</div>
|
<div class="bg1">{{ dexType }}</div>
|
||||||
<div :style="{ color: dexInfo.color }">{{ dexInfo.name }}</div>
|
<div :style="{ color: dexInfo.color }">{{ dexInfo.name }}</div>
|
||||||
<div>{{ (dex / 1000).toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 }) }}K</div>
|
<div>{{ (dex / 1000).toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 }) }}K</div>
|
||||||
<div class="d-grid">
|
<div class="d-grid">
|
||||||
<div class="align-self-center"><SammoBar :height="10" :percent="dex / 1_000_000 * 100" /></div>
|
<div class="align-self-center"><SammoBar :height="10" :percent="(dex / 1_000_000) * 100" /></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="props.showCommandList && general.reservedCommand && general.reservedCommand.length > 0"
|
v-if="props.showCommandList && general.reservedCommand && general.reservedCommand.length > 0"
|
||||||
class="col-5 general-card-turn"
|
class="col-4 general-card-turn"
|
||||||
>
|
>
|
||||||
<div class="part-title">예약턴</div>
|
<div class="part-title bg1">예약턴</div>
|
||||||
<div v-for="(turn, idx) in general.reservedCommand.slice(0, 5)" :key="idx">
|
<div v-for="(turn, idx) in general.reservedCommand.slice(0, 5)" :key="idx" class="command">
|
||||||
{{ turn.brief }}
|
<span>{{ turn.brief }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -161,12 +161,16 @@ const dexList = computed((): [string, number, DexInfo][] => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.general-card-turn {
|
.general-card-turn {
|
||||||
.bg1 {
|
> div {
|
||||||
border-left: solid 1px gray;
|
height: calc(64px / 3);
|
||||||
|
border-bottom: solid 1px gray;
|
||||||
|
border-right: solid 1px gray;
|
||||||
}
|
}
|
||||||
|
|
||||||
> div {
|
> .command{
|
||||||
border-bottom: solid 1px gray;
|
font-size: 90%;
|
||||||
|
display: grid;
|
||||||
|
align-content: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
+112
-107
@@ -7,79 +7,79 @@ export type SetBlockWarResponse = ValidResponse & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type GeneralListItemP0 = {
|
export type GeneralListItemP0 = {
|
||||||
no: number,
|
no: number;
|
||||||
name: string,
|
name: string;
|
||||||
nation: number,
|
nation: number;
|
||||||
npc: number,
|
npc: number;
|
||||||
injury: number,
|
injury: number;
|
||||||
leadership: number,
|
leadership: number;
|
||||||
strength: number,
|
strength: number;
|
||||||
intel: number,
|
intel: number;
|
||||||
explevel: number,
|
explevel: number;
|
||||||
dedlevel: number,
|
dedlevel: number;
|
||||||
gold: number,
|
gold: number;
|
||||||
rice: number,
|
rice: number;
|
||||||
killturn: number,
|
killturn: number;
|
||||||
picture: string,
|
picture: string;
|
||||||
imgsvr: 0 | 1,
|
imgsvr: 0 | 1;
|
||||||
age: number,
|
age: number;
|
||||||
specialDomestic: GameObjClassKey,
|
specialDomestic: GameObjClassKey;
|
||||||
specialWar: GameObjClassKey,
|
specialWar: GameObjClassKey;
|
||||||
personal: GameObjClassKey,
|
personal: GameObjClassKey;
|
||||||
belong: number,
|
belong: number;
|
||||||
connect: number,
|
connect: number;
|
||||||
|
|
||||||
officerLevel: number, //권한에따라 태수,군사,시종 노출 여부가 다름
|
officerLevel: number; //권한에따라 태수,군사,시종 노출 여부가 다름
|
||||||
officerLevelText: string,
|
officerLevelText: string;
|
||||||
lbonus: number,
|
lbonus: number;
|
||||||
ownerName: string | null, //NPC 출력용에 따라 결과가 다름
|
ownerName: string | null; //NPC 출력용에 따라 결과가 다름
|
||||||
honorText: string,
|
honorText: string;
|
||||||
dedLevelText: string,
|
dedLevelText: string;
|
||||||
bill: number,
|
bill: number;
|
||||||
reservedCommand: TurnObj[] | null,
|
reservedCommand: TurnObj[] | null;
|
||||||
|
|
||||||
autorun_limit: number,
|
autorun_limit: number;
|
||||||
|
|
||||||
troop: number,
|
city: number;
|
||||||
}
|
troop: number;
|
||||||
|
};
|
||||||
|
|
||||||
export type GeneralListItemP1 = {
|
export type GeneralListItemP1 = {
|
||||||
con: number,
|
con: number;
|
||||||
specage: number,
|
specage: number;
|
||||||
specage2: number,
|
specage2: number;
|
||||||
leadership_exp: number,
|
leadership_exp: number;
|
||||||
strength_exp: number,
|
strength_exp: number;
|
||||||
intel_exp: number,
|
intel_exp: number;
|
||||||
|
|
||||||
dex1: number,
|
dex1: number;
|
||||||
dex2: number,
|
dex2: number;
|
||||||
dex3: number,
|
dex3: number;
|
||||||
dex4: number,
|
dex4: number;
|
||||||
dex5: number,
|
dex5: number;
|
||||||
|
|
||||||
city: number,
|
experience: number;
|
||||||
experience: number,
|
dedication: number;
|
||||||
dedication: number,
|
officer_level: number;
|
||||||
officer_level: number,
|
officer_city: number;
|
||||||
officer_city: number,
|
defence_train: number;
|
||||||
defence_train: number,
|
crewtype: GameObjClassKey;
|
||||||
crewtype: GameObjClassKey,
|
crew: number;
|
||||||
crew: number,
|
train: number;
|
||||||
train: number,
|
atmos: number;
|
||||||
atmos: number,
|
turntime: string;
|
||||||
turntime: string,
|
recent_war: string;
|
||||||
recent_war: string,
|
horse: GameObjClassKey;
|
||||||
horse: GameObjClassKey,
|
weapon: GameObjClassKey;
|
||||||
weapon: GameObjClassKey,
|
book: GameObjClassKey;
|
||||||
book: GameObjClassKey,
|
item: GameObjClassKey;
|
||||||
item: GameObjClassKey,
|
|
||||||
|
|
||||||
warnum: number,
|
warnum: number;
|
||||||
killnum: number,
|
killnum: number;
|
||||||
deathnum: number,
|
deathnum: number;
|
||||||
killcrew: number,
|
killcrew: number;
|
||||||
deathcrew: number,
|
deathcrew: number;
|
||||||
firenum: number,
|
firenum: number;
|
||||||
} & GeneralListItemP0;
|
} & GeneralListItemP0;
|
||||||
|
|
||||||
export type GeneralListItemP2 = GeneralListItemP1;
|
export type GeneralListItemP2 = GeneralListItemP1;
|
||||||
@@ -87,45 +87,48 @@ export type GeneralListItemP2 = GeneralListItemP1;
|
|||||||
export type RawGeneralListItem = GeneralListItemP0 | GeneralListItemP1 | GeneralListItemP2;
|
export type RawGeneralListItem = GeneralListItemP0 | GeneralListItemP1 | GeneralListItemP2;
|
||||||
|
|
||||||
export type GeneralListItem =
|
export type GeneralListItem =
|
||||||
(GeneralListItemP0 & { st0: true, st1: false, st2: false, permission: 0 }) |
|
| (GeneralListItemP0 & { st0: true; st1: false; st2: false; permission: 0 })
|
||||||
(GeneralListItemP1 & { st0: true, st1: true, st2: false, permission: 1 }) |
|
| (GeneralListItemP1 & { st0: true; st1: true; st2: false; permission: 1 })
|
||||||
(GeneralListItemP2 & { st0: true, st1: true, st2: true, permission: 2 | 3 | 4 });
|
| (GeneralListItemP2 & { st0: true; st1: true; st2: true; permission: 2 | 3 | 4 });
|
||||||
|
|
||||||
type ResponseEnv = {
|
type ResponseEnv = {
|
||||||
year: number,
|
year: number;
|
||||||
month: number,
|
month: number;
|
||||||
turntime: string,
|
turntime: string;
|
||||||
turnterm: number,
|
turnterm: number;
|
||||||
killturn: number,
|
killturn: number;
|
||||||
autorun_user?: {
|
autorun_user?: {
|
||||||
limit_minutes: number,
|
limit_minutes: number;
|
||||||
options: Record<string, number>,
|
options: Record<string, number>;
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
export type RawGeneralListP0 = ValidResponse & {
|
type RawGeneralCommon = {
|
||||||
permission: 0,
|
troops: { id: number; name: string; turntime: string; reservedCommand: string[] }[];
|
||||||
column: (keyof GeneralListItemP0)[],
|
env: ResponseEnv;
|
||||||
list: ValuesOf<GeneralListItemP0>[][],
|
myGeneralID: number;
|
||||||
troops: {id: number, name: string, turntime: string}[],
|
};
|
||||||
env: ResponseEnv,
|
|
||||||
}
|
|
||||||
|
|
||||||
export type RawGeneralListP1 = ValidResponse & {
|
export type RawGeneralListP0 = ValidResponse &
|
||||||
permission: 1,
|
RawGeneralCommon & {
|
||||||
column: (keyof GeneralListItemP1)[],
|
permission: 0;
|
||||||
list: ValuesOf<GeneralListItemP1>[][],
|
column: (keyof GeneralListItemP0)[];
|
||||||
troops: {id: number, name: string, turntime: string}[],
|
list: ValuesOf<GeneralListItemP0>[][];
|
||||||
env: ResponseEnv,
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export type RawGeneralListP2 = ValidResponse & {
|
export type RawGeneralListP1 = ValidResponse &
|
||||||
permission: 2 | 3 | 4,
|
RawGeneralCommon & {
|
||||||
column: (keyof GeneralListItemP2)[],
|
permission: 1;
|
||||||
list: ValuesOf<GeneralListItemP2>[][],
|
column: (keyof GeneralListItemP1)[];
|
||||||
troops: {id: number, name: string, turntime: string}[],
|
list: ValuesOf<GeneralListItemP1>[][];
|
||||||
env: ResponseEnv,
|
};
|
||||||
}
|
|
||||||
|
export type RawGeneralListP2 = ValidResponse &
|
||||||
|
RawGeneralCommon & {
|
||||||
|
permission: 2 | 3 | 4;
|
||||||
|
column: (keyof GeneralListItemP2)[];
|
||||||
|
list: ValuesOf<GeneralListItemP2>[][];
|
||||||
|
};
|
||||||
|
|
||||||
export type GeneralListResponse = RawGeneralListP0 | RawGeneralListP1 | RawGeneralListP2;
|
export type GeneralListResponse = RawGeneralListP0 | RawGeneralListP1 | RawGeneralListP2;
|
||||||
|
|
||||||
@@ -141,16 +144,18 @@ export type NationItem = NationStaticItem & {
|
|||||||
strategic_cmd_limit: number;
|
strategic_cmd_limit: number;
|
||||||
surlimit: number;
|
surlimit: number;
|
||||||
tech: number;
|
tech: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type LiteNationInfoResponse = ValidResponse & {
|
export type LiteNationInfoResponse = ValidResponse & {
|
||||||
nation: NationStaticItem;
|
nation: NationStaticItem;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type NationInfoResponse = (ValidResponse & {
|
export type NationInfoResponse =
|
||||||
nation: NationStaticItem;
|
| (ValidResponse & {
|
||||||
}) | (ValidResponse &{
|
nation: NationStaticItem;
|
||||||
nation: NationItem;
|
})
|
||||||
impossibleStrategicCommandLists: [string, number][];
|
| (ValidResponse & {
|
||||||
troops: Record<number, string>;
|
nation: NationItem;
|
||||||
})
|
impossibleStrategicCommandLists: [string, number][];
|
||||||
|
troops: Record<number, string>;
|
||||||
|
});
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
export function convertIterableToMap<T extends object, K extends keyof T, V extends T[K] & (string | number | symbol)>(
|
||||||
|
values: Iterable<T>,
|
||||||
|
key: K
|
||||||
|
): Map<V, T> {
|
||||||
|
const result = new Map<V, T>();
|
||||||
|
for (const obj of values) {
|
||||||
|
result.set(obj[key] as V, obj);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import "@scss/troop.scss";
|
||||||
|
|
||||||
|
import { createApp } from 'vue'
|
||||||
|
import PageTroop from '@/PageTroop.vue';
|
||||||
|
import { BootstrapVue3, BToastPlugin } from 'bootstrap-vue-3'
|
||||||
|
import { auto500px } from "./util/auto500px";
|
||||||
|
import { htmlReady } from "./util/htmlReady";
|
||||||
|
import { insertCustomCSS } from "./util/customCSS";
|
||||||
|
|
||||||
|
auto500px();
|
||||||
|
|
||||||
|
htmlReady(() => {
|
||||||
|
insertCustomCSS();
|
||||||
|
});
|
||||||
|
createApp(PageTroop).use(BootstrapVue3).use(BToastPlugin).mount('#app')
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace sammo;
|
||||||
|
|
||||||
|
include "lib.php";
|
||||||
|
include "func.php";
|
||||||
|
//로그인 검사
|
||||||
|
$session = Session::requireGameLogin()->setReadOnly();
|
||||||
|
$userID = Session::getUserID();
|
||||||
|
|
||||||
|
$db = DB::db();
|
||||||
|
$gameStor = KVStorage::getStorage($db, 'game_env');
|
||||||
|
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=500" />
|
||||||
|
<title><?= UniqueConst::$serverName ?>: 부대 편성</title>
|
||||||
|
<?= WebUtil::printStaticValues([
|
||||||
|
'staticValues' => [
|
||||||
|
'serverNick' => DB::prefix(),
|
||||||
|
'mapName' => GameConst::$mapName,
|
||||||
|
'unitSet' => GameConst::$unitSet,
|
||||||
|
]
|
||||||
|
], false) ?>
|
||||||
|
<?= WebUtil::printJS('../d_shared/common_path.js') ?>
|
||||||
|
<?= WebUtil::printCSS('../d_shared/common.css') ?>
|
||||||
|
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/static/pretendard.css" />
|
||||||
|
<?= WebUtil::printDist('vue', 'v_troop', true) ?>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user