Merge branch 'g38_ui' into devel

This commit is contained in:
2021-12-23 03:06:57 +09:00
192 changed files with 6918 additions and 26811 deletions
+1
View File
@@ -9,6 +9,7 @@ body {
color: white;
line-height: 1.3;
font-size: 14px;
background-position: center;
}
input {
+467 -341
View File
@@ -1,165 +1,228 @@
@charset "UTF-8";
.world_map{
width:700px;
background:black;
font-size:14px;
color:white;
.world_map {
width: 700px;
background: black;
font-size: 14px;
color: white;
position: relative;
}
.map_title{
width:700px;
height:20px;
text-align:center;
.map_title {
width: 700px;
height: 20px;
text-align: center;
}
.map_title_tooltiptext .tooltip-inner{
max-width:220px;
width:220px;
.map_title_tooltiptext .tooltip-inner {
max-width: 220px;
width: 220px;
text-align: left;
}
.map_title_text{
margin:auto;
text-align:center;
width:160px;
display:block;
line-height:20px;
font-size:14px;
font-weight:bold;
.map_title_text {
margin: auto;
text-align: center;
width: 160px;
display: block;
line-height: 20px;
font-size: 14px;
font-weight: bold;
}
.map_body{
width:700px;
height:500px;
position:relative;
.map_body {
width: 700px;
height: 500px;
position: relative;
overflow: hidden;
}
.map_body .map_bglayer1{
width:100%;
height:100%;
position:absolute;
left:0;
top:0;
}
.map_body .map_bglayer2{
width:100%;
height:100%;
position:absolute;
left:0;
top:0;
}
.map_body .map_bgroad{
width:100%;
height:100%;
position:absolute;
left:0;
top:0;
}
.map_body .map_button_stack{
position:absolute;
right:0;
bottom:0;
text-align:right;
}
.map_body .map_toggle_cityname::after{
content: "끄기"
}
.map_body .map_toggle_cityname.active::after{
content: "켜기"
}
.map_body .map_toggle_single_tap{
display:none;
}
.map_body .map_toggle_single_tap::after{
content: "끄기"
}
.map_body .map_toggle_single_tap.active::after{
content: "켜기"
}
.world_map .city_tooltip{
.map_body .map_bglayer1 {
width: 100%;
height: 100%;
position: absolute;
z-index:16;
display:none;
min-width:120px;
left: 0;
top: 0;
}
.map_body .map_bglayer2 {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
}
.map_body .map_bgroad {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
}
.map_body .map_button_stack {
position: absolute;
right: 0;
bottom: 0;
text-align: right;
}
.map_body .map_toggle_cityname::after {
content: "끄기"
}
.map_body .map_toggle_cityname.active::after {
content: "켜기"
}
.map_body .map_toggle_single_tap {
display: none;
}
.map_body .map_toggle_single_tap::after {
content: "끄기"
}
.map_body .map_toggle_single_tap.active::after {
content: "켜기"
}
.world_map .city_tooltip {
position: absolute;
z-index: 16;
display: none;
min-width: 120px;
border: 0.02em gray solid;
white-space: nowrap;
}
.world_map .city_name{
.world_map .city_name {
background-color: rgb(30, 164, 255);
z-index: 6;
line-height:15px;
height:15px;
line-height: 15px;
height: 15px;
}
.world_map .nation_name{
.world_map .nation_name {
background-color: rgb(30, 164, 255);
z-index: 6;
line-height:15px;
height:15px;
line-height: 15px;
height: 15px;
border-top: 0.02em gray solid;
text-align:right;
text-align: right;
}
.world_map .map_flag{
width:12px;
height:12px;
.world_map .map_flag {
width: 12px;
height: 12px;
}
.world_map.map_detail .my_city{
border:solid 1px red;
outline:dotted 4px red;
border-radius: 33%;
}
.world_map.map_detail .my_city:before {
content: "";
top: -2px;
left: -2px;
right: -2px;
bottom: -2px;
position: absolute;
border-radius: 33%;
overflow: hidden;
animation-duration: 3.9s;
animation-name: blink-my-city-d1;
animation-iteration-count: infinite;
}
.world_map.map_detail .city_filler{
position:absolute;
width:calc(100% + 2px);
height:calc(100% + 2px);
left:-1px;
top:-1px;
background:transparent;
.world_map.map_detail .my_city:after {
content: "";
top: -2px;
left: -2px;
right: -2px;
bottom: -2px;
position: absolute;
border-radius: 33%;
overflow: hidden;
animation-duration: 3.9s;
animation-name: blink-my-city-d2;
animation-iteration-count: infinite;
}
.world_map .city_detail_name{
background-color:rgba(0,0,0,0.5);
font-size:10px;
position:absolute;
white-space:nowrap;
left:70%;
bottom:-10px;
color:white;
@keyframes blink-my-city-d1 {
0% {
outline: solid 4px transparent;
}
50% {
outline: solid 4px rgba(255, 0, 0, 1);
}
100% {
outline: solid 4px transparent;
}
}
.world_map.hide_cityname .city_detail_name{
display:none;
@keyframes blink-my-city-d2 {
0% {
outline: double 4px transparent;
}
50% {
outline: double 4px rgba(192, 192, 192, 1);
}
100% {
outline: double 4px transparent;
}
}
.city_base{
position:absolute;
width:40px;
height:30px;
.world_map.map_detail .city_filler {
position: absolute;
width: calc(100% + 2px);
height: calc(100% + 2px);
left: -1px;
top: -1px;
background: transparent;
}
.city_base .city_bg{
z-index:1;
position:absolute;
.world_map .city_detail_name {
background-color: rgba(0, 0, 0, 0.5);
font-size: 10px;
position: absolute;
white-space: nowrap;
left: 70%;
bottom: -10px;
color: white;
}
.world_map.hide_cityname .city_detail_name {
display: none;
}
.city_base {
position: absolute;
width: 40px;
height: 30px;
}
.city_base .city_bg {
z-index: 1;
position: absolute;
background-position: center;
}
.city_base div{
z-index:2;
.city_base div {
z-index: 2;
}
/*
@@ -173,80 +236,80 @@
}
*/
.map_detail .city_base.city_level_1 .city_bg{
.map_detail .city_base.city_level_1 .city_bg {
/*수*/
width:48px;
height:45px;
background-size:48px 45px;
left:-4px;
top:-7.5px;
width: 48px;
height: 45px;
background-size: 48px 45px;
left: -4px;
top: -7.5px;
}
.map_detail .city_base.city_level_2 .city_bg{
.map_detail .city_base.city_level_2 .city_bg {
/*진*/
width:60px;
height:42px;
background-size:60px 42px;
left:-10px;
top:-6px;
width: 60px;
height: 42px;
background-size: 60px 42px;
left: -10px;
top: -6px;
}
.map_detail .city_base.city_level_3 .city_bg{
.map_detail .city_base.city_level_3 .city_bg {
/*관*/
width:42px;
height:42px;
background-size:42px 42px;
left:-1px;
top:-6px;
width: 42px;
height: 42px;
background-size: 42px 42px;
left: -1px;
top: -6px;
}
.map_detail .city_base.city_level_4 .city_bg{
.map_detail .city_base.city_level_4 .city_bg {
/*이*/
width:60px;
height:45px;
background-size:60px 45px;
left:-10px;
top:-7.5px;
width: 60px;
height: 45px;
background-size: 60px 45px;
left: -10px;
top: -7.5px;
}
.map_detail .city_base.city_level_5 .city_bg{
.map_detail .city_base.city_level_5 .city_bg {
/*소*/
width:72px;
height:48px;
background-size:72px 48px;
left:-16px;
top:-9px;
width: 72px;
height: 48px;
background-size: 72px 48px;
left: -16px;
top: -9px;
}
.map_detail .city_base.city_level_6 .city_bg{
.map_detail .city_base.city_level_6 .city_bg {
/*중*/
width:78px;
height:54px;
background-size:78px 54px;
left:-19px;
top:-12px;
width: 78px;
height: 54px;
background-size: 78px 54px;
left: -19px;
top: -12px;
}
.map_detail .city_base.city_level_7 .city_bg{
.map_detail .city_base.city_level_7 .city_bg {
/*대*/
width:84px;
height:60px;
background-size:84px 60px;
left:-22px;
top:-15px;
width: 84px;
height: 60px;
background-size: 84px 60px;
left: -22px;
top: -15px;
}
.map_detail .city_base.city_level_8 .city_bg{
.map_detail .city_base.city_level_8 .city_bg {
/*특*/
width:96px;
height:72px;
background-size:96px 72px;
left:-28px;
top:-21px;
width: 96px;
height: 72px;
background-size: 96px 72px;
left: -28px;
top: -21px;
}
.city_img{
position:absolute;
.city_img {
position: absolute;
}
/*
@@ -261,241 +324,304 @@
*/
.map_detail .city_base.city_level_1 .city_img{
width:16px;
height:15px;
left:12px;
top:7.5px;
.map_detail .city_base.city_level_1 .city_img {
width: 16px;
height: 15px;
left: 12px;
top: 7.5px;
}
.map_detail .city_base.city_level_2 .city_img{
width:20px;
height:14px;
left:10px;
top:8px;
.map_detail .city_base.city_level_2 .city_img {
width: 20px;
height: 14px;
left: 10px;
top: 8px;
}
.map_detail .city_base.city_level_3 .city_img{
width:14px;
height:14px;
left:13px;
top:8px;
.map_detail .city_base.city_level_3 .city_img {
width: 14px;
height: 14px;
left: 13px;
top: 8px;
}
.map_detail .city_base.city_level_4 .city_img{
width:20px;
height:15px;
left:10px;
top:7.5px;
.map_detail .city_base.city_level_4 .city_img {
width: 20px;
height: 15px;
left: 10px;
top: 7.5px;
}
.map_detail .city_base.city_level_5 .city_img{
width:24px;
height:16px;
left:8px;
top:7px;
.map_detail .city_base.city_level_5 .city_img {
width: 24px;
height: 16px;
left: 8px;
top: 7px;
}
.map_detail .city_base.city_level_6 .city_img{
width:26px;
height:18px;
left:7px;
top:6px;
.map_detail .city_base.city_level_6 .city_img {
width: 26px;
height: 18px;
left: 7px;
top: 6px;
}
.map_detail .city_base.city_level_7 .city_img{
width:28px;
height:20px;
left:6px;
top:5px;
.map_detail .city_base.city_level_7 .city_img {
width: 28px;
height: 20px;
left: 6px;
top: 5px;
}
.map_detail .city_base.city_level_8 .city_img{
width:32px;
height:24px;
left:4px;
top:3px;
.map_detail .city_base.city_level_8 .city_img {
width: 32px;
height: 24px;
left: 4px;
top: 3px;
}
.map_detail .city_base .city_state{
.map_detail .city_base .city_state {
position: absolute;
top:5px; /*TODO:도시 레벨 별로 다르게 위치 설정해볼 것*/
left:0;
top: 5px;
/*TODO:도시 레벨 별로 다르게 위치 설정해볼 것*/
left: 0;
}
.map_detail .city_base .city_flag{
.map_detail .city_base .city_flag {
position: absolute;
width:12px;
height:12px;
width: 12px;
height: 12px;
}
.map_detail .city_base .city_flag > img{
width:12px;
height:12px;
.map_detail .city_base .city_flag>img {
width: 12px;
height: 12px;
}
.map_detail .city_base.city_level_1 .city_flag{
right:-8px;
top:-4px;
.map_detail .city_base.city_level_1 .city_flag {
right: -8px;
top: -4px;
}
.map_detail .city_base.city_level_2 .city_flag{
right:-8px;
top:-4px;
.map_detail .city_base.city_level_2 .city_flag {
right: -8px;
top: -4px;
}
.map_detail .city_base.city_level_3 .city_flag{
right:-8px;
top:-4px;
.map_detail .city_base.city_level_3 .city_flag {
right: -8px;
top: -4px;
}
.map_detail .city_base.city_level_4 .city_flag{
right:-6px;
top:-3px;
.map_detail .city_base.city_level_4 .city_flag {
right: -6px;
top: -3px;
}
.map_detail .city_base.city_level_5 .city_flag{
right:-6px;
top:-4px;
.map_detail .city_base.city_level_5 .city_flag {
right: -6px;
top: -4px;
}
.map_detail .city_base.city_level_6 .city_flag{
right:-6px;
top:-4px;
.map_detail .city_base.city_level_6 .city_flag {
right: -6px;
top: -4px;
}
.map_detail .city_base.city_level_7 .city_flag{
right:-6px;
top:-4px;
.map_detail .city_base.city_level_7 .city_flag {
right: -6px;
top: -4px;
}
.map_detail .city_base.city_level_8 .city_flag{
right:-6px;
top:-3px;
.map_detail .city_base.city_level_8 .city_flag {
right: -6px;
top: -3px;
}
.map_detail .city_base .city_flag .city_capital{
.map_detail .city_base .city_flag .city_capital {
position: absolute;
width:10px;
height:10px;
top:0;
right:-1px;
width: 10px;
height: 10px;
top: 0;
right: -1px;
}
.map_detail .city_base .city_flag .city_capital > img{
width:10px;
height:10px;
.map_detail .city_base .city_flag .city_capital>img {
width: 10px;
height: 10px;
}
/* Basic */
.world_map.map_basic .city_filler{
position:absolute;
width:100%;
height:100%;
left:0;
top:0;
background:transparent;
}
.world_map.map_basic .my_city{
outline:dotted 4px red;
}
.map_basic .city_base .city_img{
background-color:white;
}
.map_basic .city_base.city_level_1 .city_img{
width:12px;
height:12px;
left:14px;
top:9px;
}
.map_basic .city_base.city_level_2 .city_img{
width:12px;
height:12px;
left:14px;
top:9px;
}
.map_basic .city_base.city_level_3 .city_img{
width:14px;
height:14px;
left:13px;
top:8px;
}
.map_basic .city_base.city_level_4 .city_img{
width:16px;
height:14px;
left:12px;
top:8px;
}
.map_basic .city_base.city_level_5 .city_img{
width:18px;
height:16px;
left:11px;
top:7px;
}
.map_basic .city_base.city_level_6 .city_img{
width:20px;
height:16px;
left:10px;
top:7px;
}
.map_basic .city_base.city_level_7 .city_img{
width:22px;
height:18px;
left:9px;
top:6px;
}
.map_basic .city_base.city_level_8 .city_img{
width:24px;
height:18px;
left:8px;
top:6px;
}
.map_basic .city_base .city_capital{
.world_map.map_basic .city_filler {
position: absolute;
width:5px;
height:5px;
top:-2px;
right:-2px;
background-color:yellow;
width: 100%;
height: 100%;
left: 0;
top: 0;
background: transparent;
}
.map_basic .city_base .city_state{
/*
.world_map.map_basic .my_city {
}*/
.world_map.map_basic .my_city:before {
content: "";
top: -2px;
left: -2px;
right: -2px;
bottom: -2px;
position: absolute;
width:10px;
height:10px;
top:-2px;
left:-4px;
background-color:white;
animation-duration: 2s;
animation-name: blink-my-city;
animation-iteration-count: infinite;
}
.map_basic .city_base .city_state.city_state_war{
background-color:red;
.world_map.map_basic .my_city:after {
content: "";
top: -2px;
left: -2px;
right: -2px;
bottom: -2px;
position: absolute;
animation-duration: 2s;
animation-name: blink-my-city2;
animation-iteration-count: infinite;
}
.map_basic .city_base .city_state.city_state_bad{
background-color:orange;
@keyframes blink-my-city {
0% {
outline: dashed 5px transparent;
}
50% {
outline: dashed 5px white;
}
100% {
outline: dashed 5px transparent;
}
}
.map_basic .city_base .city_state.city_state_good{
background-color:blue;
@keyframes blink-my-city2 {
0% {
outline: dashed 4px transparent;
}
50% {
outline: dashed 4px red;
}
100% {
outline: dashed 4px transparent;
}
}
.btn.btn-minimum{
.map_basic .city_base .city_img {
background-color: white;
}
.map_basic .city_base.city_level_1 .city_img {
width: 12px;
height: 12px;
left: 14px;
top: 9px;
}
.map_basic .city_base.city_level_2 .city_img {
width: 12px;
height: 12px;
left: 14px;
top: 9px;
}
.map_basic .city_base.city_level_3 .city_img {
width: 14px;
height: 14px;
left: 13px;
top: 8px;
}
.map_basic .city_base.city_level_4 .city_img {
width: 16px;
height: 14px;
left: 12px;
top: 8px;
}
.map_basic .city_base.city_level_5 .city_img {
width: 18px;
height: 16px;
left: 11px;
top: 7px;
}
.map_basic .city_base.city_level_6 .city_img {
width: 20px;
height: 16px;
left: 10px;
top: 7px;
}
.map_basic .city_base.city_level_7 .city_img {
width: 22px;
height: 18px;
left: 9px;
top: 6px;
}
.map_basic .city_base.city_level_8 .city_img {
width: 24px;
height: 18px;
left: 8px;
top: 6px;
}
.map_basic .city_base .city_capital {
position: absolute;
width: 5px;
height: 5px;
top: -2px;
right: -2px;
background-color: yellow;
}
.map_basic .city_base .city_state {
position: absolute;
width: 10px;
height: 10px;
top: -2px;
left: -4px;
background-color: white;
}
.map_basic .city_base .city_state.city_state_war {
background-color: red;
}
.map_basic .city_base .city_state.city_state_bad {
background-color: orange;
}
.map_basic .city_base .city_state.city_state_good {
background-color: blue;
}
.btn.btn-minimum {
padding-top: 1px;
padding-bottom: 1px;
}
-11
View File
@@ -1,11 +0,0 @@
#amount {
width: 100px;
}
#colorType {
width: 150px;
}
.no-padding .select2-results__option {
padding: 0;
}
+21
View File
@@ -52,6 +52,18 @@ function bar($per, $h = 7)
return $str;
}
function JSOptionsForCities(callable $infoCall = null){
$result = [];
foreach (CityConst::all() as $city) {
$item = [$city->id, $city->name];
if($infoCall){
$item[2] = $infoCall($city);
}
$result[] = $item;
}
return $result;
}
function optionsForCities(callable $infoCall = null)
{
@@ -116,6 +128,15 @@ function closeButton()
return "<button type='button' class='btn btn-primary back_btn' onclick=window.close()>창 닫기</button><br>";
}
function JSCitiesBasedOnDistance(int $cityNo, int $maxDistance = 1): array{
$distanceList = searchDistance($cityNo, $maxDistance, true);
$result = [];
for ($dist = 1; $dist <= $maxDistance; $dist++) {
$result[$dist] = Util::array_get($distanceList[$dist], []);
}
return $result;
}
function printCitiesBasedOnDistance(int $cityNo, int $maxDistance = 1): string
{
+8
View File
@@ -225,6 +225,14 @@ function formatName(string $name, int $npc): string
return $name;
}
function getMapTheme(): string
{
$db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env');
$mapTheme = $gameStor->map_theme ?? 'che';
return $mapTheme;
}
function getMapHtml(?string $mapTheme = null)
{
$templates = new \League\Plates\Engine(__DIR__ . '/templates');
+1 -1
View File
@@ -337,7 +337,7 @@ if (!$otherTextInfo) {
?>
</div>
<nav class="navbar navbar-expand fixed-bottom navbar-dark bg-dark d-sm-block d-md-none p-0" id="navbar500">
<div class="container-fluid">
<div class="container-fluid px-0">
<div class="collapse navbar-collapse">
<ul class="navbar-nav me-auto mx-auto">
<li class="nav-item dropup">
+1 -7
View File
@@ -475,15 +475,9 @@ abstract class BaseCommand{
abstract public function run():bool;
public function getJSPlugins():array {
public function exportJSVars():array {
return [];
}
public function getCSSFiles():array {
return [];
}
public function getForm():string{
throw new \sammo\MustNotBeReachedException();
}
public function getLastTurn():LastTurn{
return $this->generalObj->getLastTurn();
+8 -24
View File
@@ -12,6 +12,7 @@ use \sammo\GameUnitConst;
use \sammo\LastTurn;
use \sammo\Command;
use function sammo\getMapTheme;
use function \sammo\printCitiesBasedOnDistance;
use function sammo\tryUniqueItemLottery;
@@ -169,31 +170,14 @@ class che_강행 extends Command\GeneralCommand
return true;
}
public function getJSPlugins(): array
public function exportJSVars(): array
{
return [
'defaultSelectCityByMap'
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'cities' => \sammo\JSOptionsForCities(),
'distanceList' => \sammo\JSCitiesBasedOnDistance($this->generalObj->getCityID(), 3),
],
];
}
public function getForm(): string
{
$currentCityID = $this->generalObj->getCityID();
$currentCityName = CityConst::byID($currentCityID)->name;
ob_start();
?>
<?= \sammo\getMapHtml() ?><br>
선택된 도시로 강행합니다.<br>
최대 3칸내 도시로만 강행이 가능합니다.<br>
목록을 선택하거나 도시를 클릭하세요.<br>
<?= $currentCityName ?> => <select class='formInput' name="destCityID" id="destCityID" size='1' style='color:white;background-color:black;'><br>
<?= \sammo\optionsForCities() ?><br>
</select> <input type=button id="commonSubmit" value="<?= $this->getName() ?>"><br>
<br>
<br>
<?= printCitiesBasedOnDistance($currentCityID, 3) ?>
<?php
return ob_get_clean();
}
}
}
+18 -80
View File
@@ -169,7 +169,7 @@ class che_건국 extends Command\GeneralCommand
$general->addExperience($exp);
$general->addDedication($ded);
$aux = Json::decode($this->nation['aux'])??[];
$aux = Json::decode($this->nation['aux']) ?? [];
$aux['can_국기변경'] = 1;
$db->update('city', [
@@ -197,87 +197,25 @@ class che_건국 extends Command\GeneralCommand
return true;
}
public function getJSPlugins(): array
public function exportJSVars(): array
{
$nationTypes = [];
foreach (GameConst::$availableNationType as $nationType) {
$nationClass = buildNationTypeClass($nationType);
$nationTypes[$nationType] = [
'type' => $nationType,
'name' => $nationClass->getName(),
'pros' => $nationClass::$pros,
'cons' => $nationClass::$cons
];
}
return [
'colorSelect'
'procRes' => [
'available건국' => count(getAllNationStaticInfo()) < $this->env['maxnation'],
'nationTypes' => $nationTypes,
'colors' => GetNationColors(),
]
];
}
public function getForm(): string
{
if (count(getAllNationStaticInfo()) >= $this->env['maxnation']) {
return '더 이상 건국은 불가능합니다.';
}
//NOTE: 새로운 방법이 생기기 전까진 아무색이나 선택 가능하도록 하자.
/*
foreach(GetNationColors() as $color){
$colorUsed[$color] = 0;
}
foreach(getAllNationStaticInfo() as $nation){
if($nation['level'] <= 0){
continue;
}
$colorUsed[$nation['color']]++;
}
$colorUsedCnt = 0;
foreach($colorUsed as $color=>$used){
if($used){
continue;
}
$colorUsedCnt += 1;
}
//색깔이 다 쓰였으면 그냥 모두 허용
if($colorUsedCnt === count($colorUsed)){
foreach(array_keys($colorUsed) as $color){
$colorUsed[$color] = 0;
}
}
*/
ob_start();
?>
현재 도시에서 나라를 세웁니다. 중, 소도시에서만 가능합니다.<br>
<?php foreach (GameConst::$availableNationType as $nationType) :
$nationClass = buildNationTypeClass($nationType);
[$name, $pros, $cons] = [$nationClass->getName(), $nationClass::$pros, $nationClass::$cons];
?>
- <?= $name ?> : <span style='color:cyan;'><?= $pros ?></span> <span style='color:magenta;'><?= $cons ?></span><br>
<?php endforeach; ?>
<br>
국명 : <input type='text' class='formInput' name="nationName" id="nationName" size='18' maxlength='18' style='color:white;background-color:black;'>
색상 : <select class='formInput' name='colorType' id='colorType' size='1'>
<?php foreach (GetNationColors() as $idx => $color) :
/*
if($colorUsed[$color] > 0){
continue;
}
*/
?>
<option value="<?= $idx ?>" data-color="<?= $color ?>" data-font-color="<?=newColor($color)?>" style='background-color:<?= $color ?>;color:<?= newColor($color) ?>;'>국가명(<?=$color?>)</option>
<?php endforeach; ?>
</select>
성향 : <select class='formInput' name='nationType' id='nationType' size='1'>
<?php foreach (GameConst::$availableNationType as $nationType) :
$nationTypeName = buildNationTypeClass($nationType)->getName();
?>
<option value='<?= $nationType ?>' style=background-color:black;color:white;><?= $nationTypeName ?></option>
<?php endforeach; ?>
</select>
<input type=button id="commonSubmit" value="<?= $this->getName() ?>">
<?php
return ob_get_clean();
}
}
+8 -17
View File
@@ -185,23 +185,14 @@ class che_군량매매 extends Command\GeneralCommand{
return true;
}
public function getForm(): string
public function exportJSVars(): array
{
ob_start();
?>
자신의 군량을 사거나 팝니다.<br>
<select id='buyRice' name="buyRice" size=1 style=color:white;background-color:black>
<option value='false'>팜</option>
<option value='true'>삼</option>
</select>
<select name=amount id='amount' size=1 style=text-align:right;color:white;background-color:black>
<?php foreach(GameConst::$resourceActionAmountGuide as $amount): ?>
<option value='<?=$amount?>'><?=$amount?></option>
<?php endforeach; ?>
</select> <input type=button id="commonSubmit" value="<?=$this->getName()?>"><br>
<?php
return ob_get_clean();
return [
'procRes' => [
'minAmount' => 100,
'maxAmount' => GameConst::$maxResourceActionAmount,
'amountGuide' => GameConst::$resourceActionAmountGuide,
]
];
}
}
+54 -60
View File
@@ -1,9 +1,13 @@
<?php
namespace sammo\Command\General;
use \sammo\{
DB, Util, JosaUtil,
General, DummyGeneral,
DB,
Util,
JosaUtil,
General,
DummyGeneral,
ActionLogger,
GameConst,
LastTurn,
@@ -21,30 +25,32 @@ use \sammo\Constraint\Constraint;
use \sammo\Constraint\ConstraintHelper;
class che_등용 extends Command\GeneralCommand{
class che_등용 extends Command\GeneralCommand
{
static protected $actionName = '등용';
static public $reqArg = true;
protected function argTest():bool{
if($this->arg === null){
protected function argTest(): bool
{
if ($this->arg === null) {
return false;
}
//NOTE: 사망 직전에 '등용' 턴을 넣을 수 있으므로, 존재하지 않는 장수여도 argTest에서 바로 탈락시키지 않음
if(!key_exists('destGeneralID', $this->arg)){
if (!key_exists('destGeneralID', $this->arg)) {
return false;
}
$destGeneralID = $this->arg['destGeneralID'];
if(!is_int($destGeneralID)){
if (!is_int($destGeneralID)) {
return false;
}
if($destGeneralID <= 0){
if ($destGeneralID <= 0) {
return false;
}
if($destGeneralID == $this->generalObj->getID()){
if ($destGeneralID == $this->generalObj->getID()) {
return false;
}
$this->arg = [
'destGeneralID'=>$destGeneralID
'destGeneralID' => $destGeneralID
];
return true;
}
@@ -59,11 +65,11 @@ class che_등용 extends Command\GeneralCommand{
$relYear = $this->env['year'] - $this->env['startyear'];
$this->permissionConstraints=[
$this->permissionConstraints = [
ConstraintHelper::ReqEnvValue('join_mode', '!=', 'onlyRandom', '랜덤 임관만 가능합니다'),
];
$this->minConditionConstraints=[
$this->minConditionConstraints = [
ConstraintHelper::ReqEnvValue('join_mode', '!=', 'onlyRandom', '랜덤 임관만 가능합니다'),
ConstraintHelper::NotBeNeutral(),
ConstraintHelper::OccupiedCity(),
@@ -79,7 +85,7 @@ class che_등용 extends Command\GeneralCommand{
[$reqGold, $reqRice] = $this->getCost();
$relYear = $this->env['year'] - $this->env['startyear'];
$this->fullConditionConstraints=[
$this->fullConditionConstraints = [
ConstraintHelper::ReqEnvValue('join_mode', '!=', 'onlyRandom', '랜덤 임관만 가능합니다'),
ConstraintHelper::NotBeNeutral(),
ConstraintHelper::OccupiedCity(),
@@ -90,33 +96,37 @@ class che_등용 extends Command\GeneralCommand{
ConstraintHelper::ReqGeneralRice($reqRice),
];
if($this->destGeneralObj->getVar('officer_level') == 12){
if ($this->destGeneralObj->getVar('officer_level') == 12) {
$this->fullConditionConstraints[] = ConstraintHelper::AlwaysFail('군주에게는 등용장을 보낼 수 없습니다.');
}
}
public function canDisplay():bool{
public function canDisplay(): bool
{
return $this->env['join_mode'] !== 'onlyRandom';
}
public function getCost():array{
public function getCost(): array
{
$env = $this->env;
if(!$this->isArgValid){
if (!$this->isArgValid) {
return [$env['develcost'], 0];
}
$destGeneral = $this->destGeneralObj;
$reqGold = Util::round(
$env['develcost'] +
($destGeneral->getVar('experience') + $destGeneral->getVar('dedication')) / 1000
($destGeneral->getVar('experience') + $destGeneral->getVar('dedication')) / 1000
) * 10;
return [$reqGold, 0];
}
public function getPreReqTurn():int{
public function getPreReqTurn(): int
{
return 0;
}
public function getPostReqTurn():int{
public function getPostReqTurn(): int
{
return 0;
}
@@ -124,12 +134,13 @@ class che_등용 extends Command\GeneralCommand{
{
$destGeneralName = $this->destGeneralObj->getName();
$name = $this->getName();
$josaUl = JosaUtil::pick($name, '을');
$josaUl = JosaUtil::pick($destGeneralName, '을');
return "{$destGeneralName}{$josaUl} {$name}";
}
public function run():bool{
if(!$this->hasFullConditionMet()){
public function run(): bool
{
if (!$this->hasFullConditionMet()) {
throw new \RuntimeException('불가능한 커맨드를 강제로 실행 시도');
}
@@ -145,11 +156,10 @@ class che_등용 extends Command\GeneralCommand{
$msg = ScoutMessage::buildScoutMessage($general->getID(), $destGeneralID, $reason, new \DateTime($general->getTurnTime()));
if($msg){
if ($msg) {
$logger->pushGeneralActionLog("<Y>{$destGeneralName}</>에게 등용 권유 서신을 보냈습니다. <1>$date</>");
$msg->send(true);
}
else{
} else {
$logger->pushGeneralActionLog("<Y>{$destGeneralName}</>에게 등용 권유 서신을 보내지 못했습니다. {$reason} <1>$date</>");
}
@@ -172,43 +182,27 @@ class che_등용 extends Command\GeneralCommand{
return true;
}
public function getForm(): string
public function exportJSVars(): array
{
$db = DB::db();
$destRawGenerals = $db->queryAllLists('SELECT no,name,nation,officer_level,npc,leadership,strength,intel FROM general WHERE npc < 2 AND officer_level != 12 AND no != %i ORDER BY npc,binary(name)', $this->generalObj->getID());
$nationList = [];
$destGenerals = [];
$destRawGenerals = $db->query('SELECT no,name,npc,nation FROM general WHERE npc < 2 AND no != %i AND officer_level != 12 ORDER BY npc,binary(name)',$this->generalObj->getID());
foreach($destRawGenerals as $destGeneral){
$destNationID = $destGeneral['nation'];
if(!key_exists($destNationID, $destGenerals)){
$destGenerals[$destNationID] = [];
}
$destGenerals[$destNationID][] = $destGeneral;
foreach ([getNationStaticInfo(0), ...getAllNationStaticInfo()] as $destNation) {
$nationList[] = [
'id' => $destNation['nation'],
'name' => $destNation['name'],
'color' => $destNation['color'],
'power' => $destNation['power'],
];
}
$nationList = array_merge([0=>getNationStaticInfo(0)], getAllNationStaticInfo());
ob_start();
?>
재야나 타국의 장수를 등용합니다.<br>
서신은 개인 메세지로 전달됩니다.<br>
등용할 장수를 목록에서 선택하세요.<br>
<select class='formInput' name="destGeneralID" id="destGeneralID" size='1' style='color:white;background-color:black;'>
<?php foreach($nationList as $destNation): ?>
<optgroup style='background-color:<?=$destNation['color']?>;color:<?=newColor($destNation['color'])?>;' label="【<?=$destNation['name']?>】" >
<?php foreach($destGenerals[$destNation['nation']]??[] as $destGeneral):
$nameColor = \sammo\getNameColor($destGeneral['npc']);
if($nameColor){
$nameColor = " style='color:{$nameColor}'";
}
?>
<option style="background-color:black;color:white" value='<?=$destGeneral['no']?>' <?=$nameColor?>><?=$destGeneral['name']?></option>
<?php endforeach; ?>
</optgroup>
<?php endforeach; ?>
</select> <input type=button id="commonSubmit" value="<?=$this->getName()?>"><br>
<?php
return ob_get_clean();
return [
'procRes' => [
'nationList' => $nationList,
'generals' => $destRawGenerals,
'generalsKey' => ['no', 'name', 'nationID', 'officerLevel', 'npc', 'leadership', 'strength', 'intel']
]
];
}
}
}
@@ -218,44 +218,4 @@ class che_등용수락 extends Command\GeneralCommand{
return true;
}
public function getForm(): string
{
$db = DB::db();
$destGenerals = [];
$destRawGenerals = $db->query('SELECT no,name,npc,nation FROM general WHERE npc < 2 AND no != %i AND officer_level != 12 ORDER BY npc,binary(name)',$this->generalObj->getID());
foreach($destRawGenerals as $destGeneral){
$destNationID = $destGeneral['nation'];
if(!key_exists($destNationID, $destGenerals)){
$destGenerals[$destNationID] = [];
}
$destGenerals[$destNationID] = [$destGeneral];
}
$nationList = array_merge([0=>getNationStaticInfo(0)], getAllNationStaticInfo());
ob_start();
?>
재야나 타국의 장수를 등용합니다.<br>
서신은 개인 메세지로 전달됩니다.<br>
등용할 장수를 목록에서 선택하세요.<br>
<select class='formInput' name="destGeneralID" id="destGeneralID" size='1' style='color:white;background-color:black;'>
<?php foreach($nationList as $destNation): ?>
<optgroup style='color:<?=$destNation['color']?>'>【<?=$destNation['name']?>】
<?php foreach($destGenerals[$destNation['nation']]??[] as $destGeneral):
$nameColor = \sammo\getNameColor($destGeneral['npc']);
if($nameColor){
$nameColor = " style='color:{$nameColor}'";
}
?>
<option value='<?=$destGeneral['no']?>' <?=$nameColor?>><?=$destGeneral['name']?></option>
<?php endforeach; ?>
</optgroup>
<?php endforeach; ?>
</select> <input type=button id="commonSubmit" value="<?=$this->getName()?>"><br>
<?php
return ob_get_clean();
}
}
+11 -25
View File
@@ -145,31 +145,17 @@ class che_선양 extends Command\GeneralCommand
return true;
}
public function getForm(): string
{
//TODO: 암행부처럼 보여야...
$db = DB::db();
$destRawGenerals = $db->query('SELECT no,name,officer_level,npc FROM general WHERE nation != 0 AND nation = %i AND no != %i ORDER BY npc,binary(name)', $this->generalObj->getNationID(), $this->generalObj->getID());
ob_start();
?>
군주의 자리를 다른 장수에게 물려줍니다.<br>
장수를 선택하세요.<br>
<select class='formInput' name="destGeneralID" id="destGeneralID" size='1' style='color:white;background-color:black;'>
<?php foreach ($destRawGenerals as $destGeneral) :
$color = \sammo\getNameColor($destGeneral['npc']);
if ($color) {
$color = " style='color:{$color}'";
}
$name = $destGeneral['name'];
if ($destGeneral['officer_level'] >= 5) {
$name = "*{$name}*";
}
?>
<option value='<?= $destGeneral['no'] ?>' <?= $color ?>><?= $name ?></option>
<?php endforeach; ?>
</select> <input type=button id="commonSubmit" value="<?= $this->getName() ?>"><br>
<?php
return ob_get_clean();
public function exportJSVars(): array
{
$db = DB::db();
$destRawGenerals = $db->queryAllLists('SELECT no,name,officer_level,npc,leadership,strength,intel FROM general WHERE nation != 0 AND nation = %i AND no != %i ORDER BY npc,binary(name)', $this->generalObj->getNationID(), $this->generalObj->getID());
return [
'procRes' => [
'generals' => $destRawGenerals,
'generalsKey' => ['no', 'name', 'officerLevel', 'npc', 'leadership', 'strength', 'intel']
]
];
}
}
+29 -39
View File
@@ -16,6 +16,7 @@ use \sammo\Command;
use \sammo\ServConfig;
use function \sammo\getDexCall;
use function sammo\getDexLevelList;
use function \sammo\tryUniqueItemLottery;
use \sammo\Constraint\Constraint;
@@ -37,6 +38,9 @@ class che_숙련전환 extends Command\GeneralCommand
/** @var string */
protected $destArmTypeName;
static $decreaseCoeff = 0.4;
static $convertCoeff = 0.9;
protected function argTest(): bool
{
if ($this->arg === null) {
@@ -162,9 +166,9 @@ class che_숙련전환 extends Command\GeneralCommand
$logger = $general->getLogger();
$srcDex = $general->getVar('dex' . $this->srcArmType);
$cutDex = Util::toInt($srcDex * 0.4);
$cutDex = Util::toInt($srcDex * static::$decreaseCoeff);
$cutDexText = number_format($cutDex);
$addDex = Util::toInt($cutDex * 9 / 10);
$addDex = Util::toInt($cutDex * static::$convertCoeff);
$addDexText = number_format($addDex);
$general->increaseVar('dex' . $this->srcArmType, -$cutDex);
@@ -189,47 +193,33 @@ class che_숙련전환 extends Command\GeneralCommand
return true;
}
public function getForm(): string
public function exportJSVars(): array
{
$db = DB::db();
$general = $this->generalObj;
$dexSrcTexts = [];
$dexDestTexts = [];
$ownDexList = [];
foreach (GameUnitConst::allType() as $armType => $armName) {
$dexVal = $general->getVar('dex' . $armType);
$dexValText = number_format($dexVal);
$cutDex = Util::toInt($dexVal * 0.4);
$addDex = Util::toInt($cutDex * 9 / 10);
$addDexText = number_format($addDex);
$newDex = $dexVal - $cutDex;
$beforeDexLevel = getDexCall($dexVal);
$afterDexLevel = getDexCall($newDex);
$dexSrcTexts[$armType] = "{$armName} ({$beforeDexLevel}{$afterDexLevel}, {$addDexText} 전환)";
$dexDestTexts[$armType] = "{$armName} ({$dexValText})";
$ownDexList[] = [
'armType' => $armType,
'name' => $armName,
'amount' => $general->getVar('dex' . $armType),
];
}
ob_start();
?>
본인의 특정 병종 숙련을 40% 줄이고, 줄어든 숙련 중 9/10(90%p)를 다른 병종 숙련으로 전환합니다.<br>
<select class='formInput' name="srcArmType" id="srcArmType" size='1' style='color:white;background-color:black;'>
<?php foreach ($dexSrcTexts as $armType => $infoText) : ?>
<option value="<?= $armType ?>"><?= $infoText ?></option>
<?php endforeach; ?>
</select>
숙련을
<select class='formInput' name="destArmType" id="destArmType" size='1' style='color:white;background-color:black;'>
<?php foreach ($dexDestTexts as $armType => $infoText) : ?>
<option value="<?= $armType ?>"><?= $infoText ?></option>
<?php endforeach; ?>
</select>
숙련으로 <input type=button id="commonSubmit" value="<?= $this->getName() ?>"><br>
<?php
return ob_get_clean();
$dexLevelList = [];
foreach(getDexLevelList() as [$dexKey, $color, $name]){
$dexLevelList[] = [
'amount'=>$dexKey,
'color'=>$color,
'name'=>$name
];
}
return [
'procRes' => [
'ownDexList' => $ownDexList,
'dexLevelList' => $dexLevelList,
'decreaseCoeff' => static::$decreaseCoeff,
'convertCoeff' => static::$convertCoeff,
]
];
}
}
+7 -23
View File
@@ -12,6 +12,7 @@ use \sammo\GameUnitConst;
use \sammo\LastTurn;
use \sammo\Command;
use function sammo\getMapTheme;
use function \sammo\printCitiesBasedOnDistance;
use function sammo\tryUniqueItemLottery;
@@ -177,31 +178,14 @@ class che_이동 extends Command\GeneralCommand
return true;
}
public function getJSPlugins(): array
public function exportJSVars(): array
{
return [
'defaultSelectCityByMap'
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'cities' => \sammo\JSOptionsForCities(),
'distanceList' => \sammo\JSCitiesBasedOnDistance($this->generalObj->getCityID(), 1),
],
];
}
public function getForm(): string
{
$currentCityID = $this->generalObj->getCityID();
$currentCityName = CityConst::byID($currentCityID)->name;
ob_start();
?>
<?= \sammo\getMapHtml() ?><br>
선택된 도시로 이동합니다.<br>
인접 도시로만 이동이 가능합니다.<br>
목록을 선택하거나 도시를 클릭하세요.<br>
<?= $currentCityName ?> => <select class='formInput' name="destCityID" id="destCityID" size='1' style='color:white;background-color:black;'><br>
<?= \sammo\optionsForCities() ?><br>
</select> <input type=button id="commonSubmit" value="<?= $this->getName() ?>"><br>
<br>
<br>
<?= printCitiesBasedOnDistance($currentCityID, 1) ?>
<?php
return ob_get_clean();
}
}
+70 -81
View File
@@ -1,11 +1,15 @@
<?php
namespace sammo\Command\General;
use \sammo\{
DB, Util, JosaUtil,
DB,
Util,
JosaUtil,
General,
ActionLogger,
GameConst, GameUnitConst,
GameConst,
GameUnitConst,
LastTurn,
Command,
Json,
@@ -22,24 +26,26 @@ use sammo\CityConst;
class che_임관 extends Command\GeneralCommand{
class che_임관 extends Command\GeneralCommand
{
static protected $actionName = '임관';
static public $reqArg = true;
protected function argTest():bool{
if($this->arg === null){
protected function argTest(): bool
{
if ($this->arg === null) {
return false;
}
$destNationID = $this->arg['destNationID']??null;
$destNationID = $this->arg['destNationID'] ?? null;
if($destNationID === null){
if ($destNationID === null) {
return false;
}
if(!is_int($destNationID)){
if (!is_int($destNationID)) {
return false;
}
if($destNationID < 1){
if ($destNationID < 1) {
return false;
}
@@ -50,7 +56,8 @@ class che_임관 extends Command\GeneralCommand{
return true;
}
protected function init(){
protected function init()
{
$general = $this->generalObj;
$env = $this->env;
@@ -60,23 +67,24 @@ class che_임관 extends Command\GeneralCommand{
$relYear = $env['year'] - $env['startyear'];
$this->permissionConstraints=[
$this->permissionConstraints = [
ConstraintHelper::ReqEnvValue('join_mode', '!=', 'onlyRandom', '랜덤 임관만 가능합니다')
];
$this->minConditionConstraints=[
$this->minConditionConstraints = [
ConstraintHelper::ReqEnvValue('join_mode', '!=', 'onlyRandom', '랜덤 임관만 가능합니다'),
ConstraintHelper::BeNeutral(),
ConstraintHelper::AllowJoinAction()
];
}
public function getCommandDetailTitle():string{
public function getCommandDetailTitle(): string
{
return '지정한 국가로 임관';
}
public function canDisplay():bool{
public function canDisplay(): bool
{
return $this->env['join_mode'] !== 'onlyRandom';
}
@@ -87,7 +95,7 @@ class che_임관 extends Command\GeneralCommand{
$env = $this->env;
$relYear = $env['year'] - $env['startyear'];
$this->fullConditionConstraints=[
$this->fullConditionConstraints = [
ConstraintHelper::ReqEnvValue('join_mode', '!=', 'onlyRandom', '랜덤 임관만 가능합니다'),
ConstraintHelper::BeNeutral(),
ConstraintHelper::ExistsDestNation(),
@@ -96,27 +104,32 @@ class che_임관 extends Command\GeneralCommand{
];
}
public function getCost():array{
public function getCost(): array
{
return [0, 0];
}
public function getPreReqTurn():int{
public function getPreReqTurn(): int
{
return 0;
}
public function getPostReqTurn():int{
public function getPostReqTurn(): int
{
return 0;
}
public function getBrief():string{
public function getBrief(): string
{
$commandName = $this->getName();
$destNationName = getNationStaticInfo($this->arg['destNationID'])['name'];
$josaRo = JosaUtil::pick($destNationName, '로');
return "{$destNationName}{$josaRo} {$commandName}";
}
public function run():bool{
if(!$this->hasFullConditionMet()){
public function run(): bool
{
if (!$this->hasFullConditionMet()) {
throw new \RuntimeException('불가능한 커맨드를 강제로 실행 시도');
}
@@ -139,10 +152,9 @@ class che_임관 extends Command\GeneralCommand{
$logger->pushGeneralHistoryLog("<D><b>{$destNationName}</b></>에 임관");
$logger->pushGlobalActionLog("<Y>{$generalName}</>{$josaYi} <D><b>{$destNationName}</b></>에 <S>임관</>했습니다.");
if($gennum < GameConst::$initialNationGenLimit) {
if ($gennum < GameConst::$initialNationGenLimit) {
$exp = 700;
}
else {
} else {
$exp = 100;
}
@@ -151,22 +163,21 @@ class che_임관 extends Command\GeneralCommand{
$general->setVar('officer_city', 0);
$general->setVar('belong', 1);
if($this->destGeneralObj !== null){
if ($this->destGeneralObj !== null) {
$general->setVar('city', $this->destGeneralObj->getCityID());
}
else{
} else {
$targetCityID = $db->queryFirstField('SELECT city FROM general WHERE nation = %i AND officer_level=12', $destNationID);
$general->setVar('city', $targetCityID);
}
$db->update('nation', [
'gennum'=>$db->sqleval('gennum + 1')
'gennum' => $db->sqleval('gennum + 1')
], 'nation=%i', $destNationID);
\sammo\refreshNationStaticInfo();
$relYear = $env['year'] - $env['startyear'];
if($general->getNPCType() == 1 || $relYear >= 3){
$joinedNations = $general->getAuxVar('joinedNations')??[];
if ($general->getNPCType() == 1 || $relYear >= 3) {
$joinedNations = $general->getAuxVar('joinedNations') ?? [];
$joinedNations[] = $destNationID;
$general->setAuxVar('joinedNations', $joinedNations);
}
@@ -181,64 +192,42 @@ class che_임관 extends Command\GeneralCommand{
return true;
}
public function getForm(): string
public function exportJSVars(): array
{
$generalObj = $this->generalObj;
$nationID = $generalObj->getNationID();
$db = DB::db();
$generalObj = $this->generalObj;
$env = $this->env;
$joinedNations = $generalObj->getAuxVar('joinedNations')??[];
$nationList = $db->query('SELECT nation,`name`,color,scout,gennum FROM nation');
shuffle($nationList);
$nationList = Util::convertArrayToDict($nationList, 'nation');
//NOTE: join 안할것임
$rawNationList = Util::convertArrayToDict($db->query('SELECT nation,`name`,color,gennum,`power` FROM nation'), 'nation');
$scoutMsgs = KVStorage::getValuesFromInterNamespace($db, 'nation_env', 'scout_msg');
foreach($scoutMsgs as $nationID=>$scoutMsg){
$nationList[$nationID]['scoutmsg'] = $scoutMsg;
foreach ($scoutMsgs as $nationID => $scoutMsg) {
$rawNationList[$nationID]['scoutmsg'] = $scoutMsg;
}
foreach ($rawNationList as $destNation) {
$testCommand = new static($generalObj, $this->env, ['destNationID' => $destNation['nation']]);
$hiddenItems = [];
foreach($nationList as &$nation){
if($env['year'] < $env['startyear']+3 && $nation['gennum'] >= GameConst::$initialNationGenLimit){
$nation['availableJoin'] = false;
$nationTarget = [
'id' => $destNation['nation'],
'name' => $destNation['name'],
'color' => $destNation['color'],
'power' => $destNation['power'],
'scoutMsg' => $destNation['scoutmsg'] ?? ' ',
];
if (!$testCommand->hasFullConditionMet()) {
$nationTarget['notAvailable'] = true;
}
else if($nation['scout'] == 1) {
$nation['availableJoin'] = false;
}
else{
$nation['availableJoin'] = true;
if ($destNation['id'] == $nationID) {
$nationTarget['notAvailable'] = true;
}
if(in_array($nation['nation'], $joinedNations)){
$nation['availableJoin'] = false;
}
if(Util::starts_with($nation['name'], 'ⓤ')){
$hiddenItems[$nation['nation']] = $nation['nation'];
}
$nationList[] = $nationTarget;
}
unset($nation);
ob_start();
?>
국가에 임관합니다.<br>
이미 임관/등용되었던 국가는 다시 임관할 수 없습니다.<br>
바로 군주의 위치로 이동합니다.<br>
임관할 국가를 목록에서 선택하세요.<br>
<select class='formInput' name="destNationID" id="destNationID" size='1' style='color:white;background-color:black;'>
<?php foreach($nationList as $nation): ?>
<?php if(key_exists($nation['nation'], $hiddenItems)){ continue; } ?>
<option
value='<?=$nation['nation']?>'
style='<?=$nation['availableJoin']?'':'background-color:red;'?>'
>【<?=$nation['name']?> 】</option>
<?php endforeach; ?>
<input type=button id="commonSubmit" value="<?=$this->getName()?>">
<?=getInvitationList($nationList)?>
<?php
return ob_get_clean();
return [
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'nationList' => $nationList,
'startYear' => $this->env['startyear'],
],
];
}
}
}
+142 -80
View File
@@ -1,13 +1,17 @@
<?php
namespace sammo\Command\General;
//TODO: 아이템 클래스 재확정 후 재 구현!
use \sammo\{
DB, Util, JosaUtil,
DB,
Util,
JosaUtil,
General,
ActionLogger,
GameConst, GameUnitConst,
GameConst,
GameUnitConst,
LastTurn,
Command
};
@@ -18,49 +22,52 @@ use function sammo\tryUniqueItemLottery;
use \sammo\Constraint\Constraint;
use \sammo\Constraint\ConstraintHelper;
class che_장비매매 extends Command\GeneralCommand{
class che_장비매매 extends Command\GeneralCommand
{
static protected $actionName = '장비매매';
static public $reqArg = true;
static $itemMap = [
'horse'=>'명마',
'weapon'=>'무기',
'book'=>'서적',
'item'=>'도구',
'horse' => '명마',
'weapon' => '무기',
'book' => '서적',
'item' => '도구',
];
protected function argTest():bool{
if($this->arg === null){
protected function argTest(): bool
{
if ($this->arg === null) {
return false;
}
$itemType = $this->arg['itemType']??null;
if(!in_array($itemType, array_keys(static::$itemMap))){
$itemType = $this->arg['itemType'] ?? null;
if (!in_array($itemType, array_keys(static::$itemMap))) {
return false;
}
$itemCode = $this->arg['itemCode']??null;
if(!key_exists($itemCode, GameConst::$allItems[$itemType]) && $itemCode !== 'None'){
$itemCode = $this->arg['itemCode'] ?? null;
if (!key_exists($itemCode, GameConst::$allItems[$itemType]) && $itemCode !== 'None') {
return false;
}
$itemClass = buildItemClass($itemCode);
if(!$itemClass->isBuyable()){
if (!$itemClass->isBuyable()) {
return false;
}
$this->arg = [
'itemType'=>$itemType,
'itemCode'=>$itemCode
'itemType' => $itemType,
'itemCode' => $itemCode
];
return true;
}
protected function init(){
protected function init()
{
$general = $this->generalObj;
$this->setCity();
$this->setNation();
$this->minConditionConstraints=[
$this->minConditionConstraints = [
ConstraintHelper::ReqCityTrader($general->getNPCType()),
];
}
@@ -76,26 +83,25 @@ class che_장비매매 extends Command\GeneralCommand{
[$reqGold, $reqRice] = $this->getCost();
$this->fullConditionConstraints=[
$this->fullConditionConstraints = [
ConstraintHelper::ReqCityTrader($general->getNPCType()),
ConstraintHelper::ReqCityCapacity('secu', '치안 수치', $itemClass->getReqSecu()),
ConstraintHelper::ReqGeneralGold($reqGold),
ConstraintHelper::ReqGeneralRice($reqRice),
];
if($itemCode === 'None'){
if ($itemCode === 'None') {
$this->fullConditionConstraints[] = ConstraintHelper::ReqGeneralValue($itemType, $itemTypeName, '!=', 'None');
}
else if($itemCode == $general->getVar($itemType)){
} else if ($itemCode == $general->getVar($itemType)) {
$this->fullConditionConstraints[] = ConstraintHelper::AlwaysFail('이미 가지고 있습니다.');
}
else if(!buildItemClass($general->getVar($itemType))->isBuyable()){
} else if (!buildItemClass($general->getVar($itemType))->isBuyable()) {
$this->fullConditionConstraints[] = ConstraintHelper::AlwaysFail('이미 진귀한 것을 가지고 있습니다.');
}
}
public function getCost():array{
if(!$this->isArgValid){
public function getCost(): array
{
if (!$this->isArgValid) {
return [0, 0];
}
@@ -106,19 +112,22 @@ class che_장비매매 extends Command\GeneralCommand{
return [$reqGold, 0];
}
public function getPreReqTurn():int{
public function getPreReqTurn(): int
{
return 0;
}
public function getPostReqTurn():int{
public function getPostReqTurn(): int
{
return 0;
}
public function getBrief():string{
public function getBrief(): string
{
$itemType = $this->arg['itemType'];
$itemCode = $this->arg['itemCode'];
if($itemCode === 'None'){
if ($itemCode === 'None') {
$itemTypeName = static::$itemMap[$itemType];
$josaUl = JosaUtil::pick($itemTypeName, '을');
return "{$itemTypeName}{$josaUl} 판매";
@@ -132,8 +141,9 @@ class che_장비매매 extends Command\GeneralCommand{
return "{$itemName}{$josaUl} 구입";
}
public function run():bool{
if(!$this->hasFullConditionMet()){
public function run(): bool
{
if (!$this->hasFullConditionMet()) {
throw new \RuntimeException('불가능한 커맨드를 강제로 실행 시도');
}
@@ -145,11 +155,10 @@ class che_장비매매 extends Command\GeneralCommand{
$itemType = $this->arg['itemType'];
$itemCode = $this->arg['itemCode'];
if($itemCode === 'None'){
if ($itemCode === 'None') {
$buying = false;
$itemCode = $general->getVar($itemType);
}
else{
} else {
$buying = true;
}
$itemObj = buildItemClass($itemCode);
@@ -161,12 +170,11 @@ class che_장비매매 extends Command\GeneralCommand{
$logger = $general->getLogger();
if($buying){
if ($buying) {
$logger->pushGeneralActionLog("<C>{$itemName}</>{$josaUl} 구입했습니다. <1>$date</>");
$general->increaseVarWithLimit('gold', -$cost, 0);
$general->setVar($itemType, $itemCode);
}
else{
} else {
$logger->pushGeneralActionLog("<C>{$itemName}</>{$josaUl} 판매했습니다. <1>$date</>");
$general->increaseVarWithLimit('gold', $cost / 2);
$general->deleteItem($itemType);
@@ -183,6 +191,60 @@ class che_장비매매 extends Command\GeneralCommand{
return true;
}
public function exportJSVars(): array
{
$general = $this->generalObj;
$db = DB::db();
$citySecu = $db->queryFirstField('SELECT secu FROM city WHERE city = %i', $this->generalObj->getCityID());
$itemList = [];
foreach (GameConst::$allItems as $itemType => $itemCategories) {
$typeName = static::$itemMap[$itemType];
$values = [];
foreach ($itemCategories as $itemCode => $cnt) {
if ($cnt > 0) {
continue;
}
$item = buildItemClass($itemCode);
if (!$item->isBuyable()) {
continue;
}
$values[] = [
'id'=>$itemCode,
'name'=>$item->getName(),
'reqSecu'=>$item->getReqSecu(),
'cost'=>$item->getCost(),
'info'=>$item->getInfo(),
'isBuyable'=>$item->isBuyable(),//항상 true지만, 일관성을 위해
];
}
$itemList[$itemType] = [
'typeName'=>$typeName,
'values'=>$values
];
}
$ownItem = [];
foreach($general->getItems() as $itemType => $item){
$ownItem[$itemType] = [
'id'=>$item->getRawClassName(),
'name'=>$item->getName(),
'reqSecu'=>$item->getReqSecu(),
'cost'=>$item->getCost(),
'info'=>$item->getInfo(),
'isBuyable'=>$item->isBuyable(),
];
}
return [
'procRes' => [
'citySecu'=>$citySecu,
'gold'=>$general->getVar('gold'),
'itemList'=>$itemList,
'ownItem'=>$ownItem,
]
];
}
public function getForm(): string
{
$form = [];
@@ -194,49 +256,49 @@ class che_장비매매 extends Command\GeneralCommand{
ob_start();
?>
<script>
function updateItemType(){
$('#itemType').val($('#itemCode option:selected').data('item_type'));
}
$(function(){
$('#customSubmit').click(function(){
updateItemType();
submitAction();
});
});
</script>
<input type="hidden" class="formInput" name="itemType" id="itemType" value="item">
장비를 구입하거나 매각합니다.<br>
현재 구입 불가능한 것은 <font color=red>붉은색</font>으로 표시됩니다.<br>
현재 도시 치안 : <?=$citySecu?> &nbsp;&nbsp;&nbsp;현재 자금 : <?=$gold?><br>
장비 : <select class='formInput' name="itemCode" id="itemCode" onchange='updateItemType();' size='1' style='color:white;background-color:black;'>
<?php foreach(GameConst::$allItems as $itemType=>$itemCategories):
//매각
$typeName = static::$itemMap[$itemType];
?>
<option value='None' data-item_type='<?=$itemType?>' style='color:skyblue'>_____<?=$typeName?>매각(반값)____</option>
<?php foreach($itemCategories as $itemCode=>$cnt) :
if($cnt > 0){
continue;
<script>
function updateItemType() {
$('#itemType').val($('#itemCode option:selected').data('item_type'));
}
$itemClass = buildItemClass($itemCode);
if(!$itemClass->isBuyable()){
continue;
}
$itemName = $itemClass->getName();
$reqSecu = $itemClass->getReqSecu();
$reqGold = $itemClass->getCost();
$css = '';
if($reqSecu > $citySecu){
$css = 'color:red;';
}
?>
<option value='<?=$itemCode?>' data-item_type='<?=$itemType?>' style='<?=$css?>'><?=$itemName?> 가격: <?=$reqGold?></option>
<?php endforeach; ?>
<?php endforeach; ?>
</select>
<input type=button id="customSubmit" value="<?=$this->getName()?>"><br>
$(function() {
$('#customSubmit').click(function() {
updateItemType();
submitAction();
});
});
</script>
<input type="hidden" class="formInput" name="itemType" id="itemType" value="item">
장비를 구입하거나 매각합니다.<br>
현재 구입 불가능한 것은 <font color=red>붉은색</font>으로 표시됩니다.<br>
현재 도시 치안 : <?= $citySecu ?> &nbsp;&nbsp;&nbsp;현재 자금 : <?= $gold ?><br>
장비 : <select class='formInput' name="itemCode" id="itemCode" onchange='updateItemType();' size='1' style='color:white;background-color:black;'>
<?php foreach (GameConst::$allItems as $itemType => $itemCategories) :
//매각
$typeName = static::$itemMap[$itemType];
?>
<option value='None' data-item_type='<?= $itemType ?>' style='color:skyblue'>_____<?= $typeName ?>매각(반값)____</option>
<?php foreach ($itemCategories as $itemCode => $cnt) :
if ($cnt > 0) {
continue;
}
$itemClass = buildItemClass($itemCode);
if (!$itemClass->isBuyable()) {
continue;
}
$itemName = $itemClass->getName();
$reqSecu = $itemClass->getReqSecu();
$reqGold = $itemClass->getCost();
$css = '';
if ($reqSecu > $citySecu) {
$css = 'color:red;';
}
?>
<option value='<?= $itemCode ?>' data-item_type='<?= $itemType ?>' style='<?= $css ?>'><?= $itemName ?> 가격: <?= $reqGold ?></option>
<?php endforeach; ?>
<?php endforeach; ?>
</select>
<input type=button id="customSubmit" value="<?= $this->getName() ?>"><br>
<?php
return ob_get_clean();
}
}
}
@@ -12,6 +12,7 @@ use \sammo\{
KVStorage
};
use function sammo\getAllNationStaticInfo;
use function \sammo\getColoredName;
use function \sammo\tryUniqueItemLottery;
use function \sammo\getInvitationList;
@@ -21,8 +22,6 @@ use \sammo\Constraint\Constraint;
use \sammo\Constraint\ConstraintHelper;
use sammo\CityConst;
class che_장수대상임관 extends Command\GeneralCommand{
static protected $actionName = '장수를 따라 임관';
static public $reqArg = true;
@@ -186,65 +185,29 @@ class che_장수대상임관 extends Command\GeneralCommand{
return true;
}
public function getForm(): string
public function exportJSVars(): array
{
$db = DB::db();
$generalObj = $this->generalObj;
$env = $this->env;
$joinedNations = $generalObj->getAuxVar('joinedNations')??[];
$generalList = $db->query('SELECT no,name,nation,npc FROM general WHERE no!=%i ORDER BY name ASC', $generalObj->getID());
$nationList = $db->query('SELECT nation,`name`,color,scout,gennum FROM nation');
shuffle($nationList);
$nationList = Util::convertArrayToDict($nationList, 'nation');
//NOTE: join 안할것임
$destRawGenerals = $db->queryAllLists('SELECT no,name,nation,officer_level,npc,leadership,strength,intel FROM general WHERE no != %i ORDER BY npc,binary(name)', $this->generalObj->getID());
$nationList = [];
$scoutMsgs = KVStorage::getValuesFromInterNamespace($db, 'nation_env', 'scout_msg');
foreach($scoutMsgs as $nationID=>$scoutMsg){
$nationList[$nationID]['scoutmsg'] = $scoutMsg;
foreach ([getNationStaticInfo(0), ...getAllNationStaticInfo()] as $destNation) {
$nationList[] = [
'id' => $destNation['nation'],
'name' => $destNation['name'],
'color' => $destNation['color'],
'power' => $destNation['power'],
'scoutMsg' => $scoutMsgs[$destNation['nation']]??' '
];
}
$hiddenItems = [];
foreach($nationList as &$nation){
$nation['hideen'] = false;
if($env['year'] < $env['startyear']+3 && $nation['gennum'] >= GameConst::$initialNationGenLimit){
$nation['availableJoin'] = false;
}
else if($nation['scout'] == 1) {
$nation['availableJoin'] = false;
}
else{
$nation['availableJoin'] = true;
}
if(in_array($nation['nation'], $joinedNations)){
$nation['availableJoin'] = false;
}
if(Util::starts_with($nation['name'], 'ⓤ')){
$hiddenItems[$nation['nation']] = $nation['nation'];
}
}
unset($nation);
ob_start();
?>
장수를 따라 임관합니다.<br>
이미 임관/등용되었던 국가는 다시 임관할 수 없습니다.<br>
바로 군주의 위치로 이동합니다.<br>
임관할 국가를 목록에서 선택하세요.<br>
<select class='formInput' name="destGeneralID" id="destGeneralID" size='1' style='color:white;background-color:black;'>
<?php foreach($generalList as $targetGeneral): ?>
<?php if(key_exists($targetGeneral['nation'], $hiddenItems)){ continue; } ?>
<option value='<?=$targetGeneral['no']?>'><?=getColoredName($targetGeneral['name'],$targetGeneral['npc'])?>【<?=getNationStaticInfo($targetGeneral['nation'])['name']??'재야'?>】</option>
<?php endforeach; ?>
</select>
<input type=button id="commonSubmit" value="<?=$this->getName()?>">
<?=getInvitationList($nationList)?>
<?php
return ob_get_clean();
return [
'procRes' => [
'nationList' => $nationList,
'generals' => $destRawGenerals,
'generalsKey' => ['no', 'name', 'nationID', 'officerLevel', 'npc', 'leadership', 'strength', 'intel']
]
];
}
}
+15 -33
View File
@@ -177,40 +177,22 @@ class che_증여 extends Command\GeneralCommand
return true;
}
public function getForm(): string
public function exportJSVars(): array
{
//TODO: 암행부처럼 보여야...
$db = DB::db();
$destRawGenerals = $db->query('SELECT no,name,officer_level,npc,gold,rice FROM general WHERE nation != 0 AND nation = %i AND no != %i ORDER BY npc,binary(name)', $this->generalObj->getNationID(), $this->generalObj->getID());
ob_start();
?>
자신의 자금이나 군량을 다른 장수에게 증여합니다.<br>
장수를 선택하세요.<br>
<select class='formInput' name="destGeneralID" id="destGeneralID" size='1' style='color:white;background-color:black;'>
<?php foreach ($destRawGenerals as $destGeneral) :
$color = \sammo\getNameColor($destGeneral['npc']);
if ($color) {
$color = " style='color:{$color}'";
}
$name = $destGeneral['name'];
if ($destGeneral['officer_level'] >= 5) {
$name = "*{$name}*";
}
?>
<option value='<?= $destGeneral['no'] ?>' <?= $color ?>><?= $name ?>(금:<?= $destGeneral['gold'] ?>, 쌀:<?= $destGeneral['rice'] ?>)</option>
<?php endforeach; ?>
</select>
<select class='formInput' name="isGold" id="isGold" size='1' style='color:white;background-color:black;'>
<option value="true">금</option>
<option value="false">쌀</option>
</select>
<select class='formInput' name="amount" id="amount" size='1' style='color:white;background-color:black;'>
<?php foreach (GameConst::$resourceActionAmountGuide as $amount) : ?>
<option value='<?= $amount ?>'><?= $amount ?></option>
<?php endforeach; ?>
</select> <input type=button id="commonSubmit" value="<?= $this->getName() ?>"><br>
<?php
return ob_get_clean();
$nationID = $this->getNationID();
$troops = Util::convertArrayToDict($db->query('SELECT * FROM troop WHERE nation=%i', $nationID), 'troop_leader');
$destRawGenerals = $db->queryAllLists('SELECT no,name,officer_level,npc,gold,rice,leadership,strength,intel,city,crew,train,atmos,troop FROM general WHERE nation = %i ORDER BY npc,binary(name)', $nationID);
return [
'procRes' => [
'troops' => $troops,
'generals' => $destRawGenerals,
'generalsKey' => ['no', 'name', 'officerLevel', 'npc', 'gold', 'rice', 'leadership', 'strength', 'intel', 'cityID', 'crew', 'train', 'atmos', 'troopID'],
'cities' => \sammo\JSOptionsForCities(),
'minAmount' => 100,
'maxAmount' => GameConst::$maxResourceActionAmount,
'amountGuide' => GameConst::$resourceActionAmountGuide,
]
];
}
}
+39 -119
View File
@@ -18,6 +18,7 @@ use \sammo\ServConfig;
use function \sammo\getTechCall;
use function \sammo\tryUniqueItemLottery;
use function \sammo\getTechAbil;
use function sammo\getTechLevel;
use \sammo\Constraint\Constraint;
use \sammo\Constraint\ConstraintHelper;
@@ -231,18 +232,10 @@ class che_징병 extends Command\GeneralCommand
return true;
}
public function getJSPlugins(): array
public function exportJSVars(): array
{
return [
'recruitCrewForm'
];
}
public function getForm(): string
{
$db = DB::db();
$general = $this->generalObj;
$db = DB::db();
[$nationLevel, $tech] = $db->queryFirstList('SELECT level,tech FROM nation WHERE nation=%i', $general->getNationID());
if (!$nationLevel) {
@@ -253,13 +246,13 @@ class che_징병 extends Command\GeneralCommand
$tech = 0;
}
$ownCities = [];
$ownRegions = [];
$year = $this->env['year'];
$startyear = $this->env['startyear'];
$relativeYear = $year - $startyear;
$ownCities = [];
$ownRegions = [];
foreach (DB::db()->query('SELECT city, region from city where nation = %i', $general->getNationID()) as $city) {
$ownCities[$city['city']] = 1;
$ownRegions[$city['region']] = 1;
@@ -269,27 +262,31 @@ class che_징병 extends Command\GeneralCommand
$fullLeadership = $general->getLeadership(false);
$abil = getTechAbil($tech);
$armTypes = [];
$armCrewTypes = [];
foreach (GameUnitConst::allType() as $armType => $armName) {
$armTypeCrews = [];
$armCrewType = [
'armType' => $armType,
'armName' => $armName,
'values' => [],
];
$crewTypes = [];
foreach (GameUnitConst::byType($armType) as $unit) {
$crewObj = new \stdClass;
$crewObj->showDefault = 'true';
$crewObj->id = $unit->id;
$crewObj->reqTech = $unit->reqTech;
$crewObj->reqYear = $unit->reqYear;
/*
if ($unit->reqTech == 0) {
$crewObj->bgcolor = 'green';
} else {
$crewObj->bgcolor = 'limegreen';
}
*/
if (!$unit->isValid($ownCities, $ownRegions, $relativeYear, $tech)) {
$crewObj->showDefault = 'false';
$crewObj->bgcolor = 'red';
}
$crewObj->notAvailable = !$unit->isValid($ownCities, $ownRegions, $relativeYear, $tech);
$crewObj->baseRice = $general->onCalcDomestic($this->getName(), 'rice', $unit->riceWithTech($tech), ['armType' => $unit->armType]);
$crewObj->baseCost = $general->onCalcDomestic($this->getName(), 'cost', $unit->costWithTech($tech), ['armType' => $unit->armType]);
@@ -305,111 +302,34 @@ class che_징병 extends Command\GeneralCommand
$crewObj->img = ServConfig::$gameImagePath . "/crewtype" . $unit->id . ".png";
}
$crewObj->baseRiceShort = round($crewObj->baseRice, 1);
$crewObj->baseCostShort = round($crewObj->baseCost, 1);
$crewObj->info = $unit->info;
$crewObj->info = join('<br>', $unit->info);
$armTypeCrews[] = $crewObj;
$armCrewType['values'][] = $crewObj;
}
$armTypes[] = [$armName, $armTypeCrews];
}
$commandName = $this->getName();
$techLevelText = getTechCall($tech);
$armCrewTypes[] = $armCrewType;
}
$crew = $general->getVar('crew');
$gold = $general->getVar('gold');
$crewTypeObj = $general->getCrewTypeObj();
ob_start();
?>
<font size=2>병사를 모집합니다.
<?php if ($commandName == '징병') : ?>
훈련과 사기치는 낮지만 가격이 저렴합니다.<br>
<?php else : ?>
훈련과 사기치는 높지만 자금이 많이 듭니다.
<?php endif; ?>
가능한 수보다 많게 입력하면 가능한 최대 병사를 모집합니다.<br>
이미 병사가 있는 경우 추가<?= $commandName ?>되며, 병종이 다를경우는 기존의 병사는 소집해제됩니다.<br>
현재 <?= $commandName ?> 가능한 병종은 <font color=green>녹색</font>으로 표시되며,<br>
현재 <?= $commandName ?> 가능한 특수병종은 <font color=limegreen>초록색</font>으로 표시됩니다.<br>
<table class='tb_layout' style='margin:auto;'>
<thead>
<tr>
<td colspan=11>
<div style='float:right'><input type='checkbox' id="show_unavailable_troops">불가능한 병종 표시</input></div>
<?php if ($commandName == '모병') : ?>
<div style='text-align:center;'>모병은 가격 2배의 자금이 소요됩니다.</div>
<?php endif; ?>
</td>
</tr>
<tr>
<td colspan=11 align=center class='bg2'>
현재 기술력 : <?= $techLevelText ?>
현재 통솔 : <?= $leadership ?><?= ($leadership != $fullLeadership) ? "({$fullLeadership})" : '' ?>
현재 병종 : <?= $crewTypeObj->name ?>
현재 병사 : <?= $crew ?>
현재 자금 : <?= $gold ?>
</td>
</tr>
<tr>
<td width=64 align=center class='bg1'>사진</td>
<td width=64 align=center class='bg1'>병종</td>
<td width=40 align=center class='bg1'>공격</td>
<td width=40 align=center class='bg1'>방어</td>
<td width=40 align=center class='bg1'>기동</td>
<td width=40 align=center class='bg1'>회피</td>
<td width=40 align=center class='bg1'>가격</td>
<td width=40 align=center class='bg1'>군량</td>
<td width=180 align=center class='bg1'>병사수</td>
<td width=50 align=center class='bg1'>행동</td>
<td width=300 align=center class='bg1'>특징</td>
</tr>
</thead>
<tbody>
<?php foreach ($armTypes as [$armName, $armTypeCrews]) : ?>
<tr>
<td colspan=11><?= $armName ?> 계열</td>
</tr>
<?php foreach ($armTypeCrews as $crewObj) : ?>
<tr id="crewType<?= $crewObj->id ?>" class="show_default_<?= $crewObj->showDefault ?>" style='height:64px;background-color:<?= $crewObj->bgcolor ?>' data-rice="<?= $crewObj->baseRice ?>" data-cost="<?= $crewObj->baseCost ?>">
<td style='background:#222222 no-repeat center url("<?= $crewObj->img ?>");background-size:64px'></td>
<td style='text-align:center;vertical-align:middle;'><?= $crewObj->name ?></td>
<td style='text-align:center;vertical-align:middle;'><?= $crewObj->attack ?></td>
<td style='text-align:center;vertical-align:middle;'><?= $crewObj->defence ?></td>
<td style='text-align:center;vertical-align:middle;'><?= $crewObj->speed ?></td>
<td style='text-align:center;vertical-align:middle;'><?= $crewObj->avoid ?></td>
<td style='text-align:center;vertical-align:middle;'><?= $crewObj->baseCostShort ?></td>
<td style='text-align:center;vertical-align:middle;'><?= $crewObj->baseRiceShort ?></td>
<td style='text-align:center;vertical-align:middle;' class='input_form' data-crewtype='<?= $crewObj->id ?>'>
<input type=button value='절반' class='btn_half'><input type=button value='채우기' class='btn_fill'><input type=button value='가득' class='btn_full'><br>
<input type=text data-crewtype='<?= $crewObj->id ?>' class=form_double name=double maxlength=3 size=3 style=text-align:right;color:white;background-color:black>00명
<input type=text class=form_cost name=cost maxlength=5 size=5 readonly style=text-align:right;color:white;background-color:black>원
</td>
<td style='position:relative;height:64px;'><input type=submit value='<?= $commandName ?>' class='submit_btn' style='width:100%;height:44px;margin:10px 0;display:block;position: absolute;left:0;top:0;'></td>
<td><?= $crewObj->info ?></td>
</tr>
<?php endforeach; ?>
<?php endforeach; ?>
</tbody>
</table>
<input type='hidden' id='amount' value='1'>
<input type='hidden' id='crewType' value='<?= $crewTypeObj->id ?>'>
<script>
window.currentTech = <?= $tech ?>;
window.leadership = <?= $leadership ?>;
window.fullLeadership = <?= $fullLeadership ?>;
window.currentCrewType = <?= $crewTypeObj->id ?>;
window.currentCrew = <?= $crew ?>;
window.currentGold = <?= $gold ?>;
window.is모병 = <?= ($this->getName() == '모병') ? 'true' : 'false' ?>;
</script>
<?php
return ob_get_clean();
return [
'procRes' => [
'relYear' => $relativeYear,
'year' => $year,
'tech' => $tech,
'techLevel' => getTechLevel($tech),
'startYear' => $startyear,
'goldCoeff' => static::$costOffset,
'leadership' => $leadership,
'fullLeadership' => $fullLeadership,
'armCrewTypes' => $armCrewTypes,
'currentCrewType' => $crewTypeObj->id,
'crew' => $crew,
'gold' => $gold,
]
];
}
}
+6 -21
View File
@@ -225,29 +225,14 @@ class che_첩보 extends Command\GeneralCommand
return true;
}
public function getJSPlugins(): array
public function exportJSVars(): array
{
return [
'defaultSelectCityByMap'
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'cities' => \sammo\JSOptionsForCities(),
'distanceList' => \sammo\JSCitiesBasedOnDistance($this->generalObj->getCityID(), 3),
],
];
}
public function getForm(): string
{
$currentCityID = $this->generalObj->getCityID();
ob_start();
?>
<?= \sammo\getMapHtml() ?><br>
선택된 도시에 첩보를 실행합니다.<br>
인접도시일 경우 많은 정보를 얻을 수 있습니다.<br>
목록을 선택하거나 도시를 클릭하세요.<br>
<select class='formInput' name="destCityID" id="destCityID" size='1' style='color:white;background-color:black;'><br>
<?= \sammo\optionsForCities() ?><br>
</select> <input type=button id="commonSubmit" value="<?= $this->getName() ?>"><br>
<br>
<br>
<?= printCitiesBasedOnDistance($currentCityID, 3) ?>
<?php
return ob_get_clean();
}
}
+8 -19
View File
@@ -2,6 +2,7 @@
namespace sammo\Command\General;
use phpDocumentor\Reflection\Types\Object_;
use \sammo\DB;
use \sammo\Util;
use \sammo\JosaUtil;
@@ -240,27 +241,15 @@ class che_출병 extends Command\GeneralCommand
return true;
}
public function getJSPlugins(): array
public function exportJSVars(): array
{
return [
'defaultSelectCityByMap'
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'cities' => \sammo\JSOptionsForCities(),
'distanceList' => \sammo\JSCitiesBasedOnDistance($this->generalObj->getCityID(), 1),
],
];
}
public function getForm(): string
{
$srcCityName = \sammo\CityConst::byID($this->generalObj->getCityID())->name;
ob_start();
?>
<?= \sammo\getMapHtml() ?><br>
선택된 도시를 향해 침공을 합니다.<br>
침공 경로에 적군의 도시가 있다면 전투를 벌입니다.<br>
목록을 선택하거나 도시를 클릭하세요.<br>
<?= $srcCityName ?> =><select class='formInput' name="destCityID" id="destCityID" size='1' style='color:white;background-color:black;'>
<?= \sammo\optionsForCities() ?><br>
</select> <input type=button id="commonSubmit" value="<?= $this->getName() ?>"><br>
<br>
<?php
return ob_get_clean();
}
}
+8 -15
View File
@@ -152,21 +152,14 @@ class che_헌납 extends Command\GeneralCommand
return true;
}
public function getForm(): string
public function exportJSVars(): array
{
ob_start();
?>
자신의 자금이나 군량을 국가 재산으로 헌납합니다.<br>
<select id='isGold' name="isGold" size=1 style=color:white;background-color:black>
<option value='true'>금</option>
<option value='false'>쌀</option>
</select>
<select name=amount id='amount' size=1 style=text-align:right;color:white;background-color:black>
<?php foreach (GameConst::$resourceActionAmountGuide as $amount) : ?>
<option value='<?= $amount ?>'><?= $amount ?></option>
<?php endforeach; ?>
</select> <input type=button id="commonSubmit" value="<?= $this->getName() ?>"><br>
<?php
return ob_get_clean();
return [
'procRes' => [
'minAmount' => 100,
'maxAmount' => GameConst::$maxResourceActionAmount,
'amountGuide' => GameConst::$resourceActionAmountGuide,
]
];
}
}
+6 -23
View File
@@ -335,31 +335,14 @@ class che_화계 extends Command\GeneralCommand
return true;
}
public function getJSPlugins(): array
public function exportJSVars(): array
{
return [
'defaultSelectCityByMap'
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'cities' => \sammo\JSOptionsForCities(),
'distanceList' => \sammo\JSCitiesBasedOnDistance($this->generalObj->getCityID(), 3),
],
];
}
public function getForm(): string
{
$currentCityID = $this->generalObj->getCityID();
$commandName = $this->getName();
$josaUl = JosaUtil::pick($commandName, '을');
ob_start();
?>
<?= \sammo\getMapHtml() ?><br>
선택된 도시에 <?= $commandName ?><?= $josaUl ?> 실행합니다.<br>
목록을 선택하거나 도시를 클릭하세요.<br>
<select class='formInput' name="destCityID" id="destCityID" size='1' style='color:white;background-color:black;'><br>
<?= \sammo\optionsForCities() ?><br>
</select> <input type=button id="commonSubmit" value="<?= $this->getName() ?>"><br>
<br>
<br>
<?= printCitiesBasedOnDistance($currentCityID, 2) ?>
<?php
return ob_get_clean();
}
}
+4 -24
View File
@@ -152,32 +152,12 @@ class che_국기변경 extends Command\NationCommand
return true;
}
public function getJSPlugins(): array
public function exportJSVars(): array
{
return [
'colorSelect'
'procRes' => [
'colors' => GetNationColors(),
],
];
}
public function getForm(): string
{
ob_start();
?>
국기를 변경합니다. 단 1회 가능합니다.<br>
색상 : <select class='formInput' name='colorType' id='colorType' size='1'>
<?php foreach (GetNationColors() as $idx => $color) :
/*
if($colorUsed[$color] > 0){
continue;
}
*/
?>
<option value="<?= $idx ?>" data-color=<?=$color?> data-font-color="<?=newColor($color)?>" style='background-color:<?= $color ?>;color:<?= newColor($color) ?>;'>국가명(<?=$color?>)</option>
<?php endforeach; ?> <input type=button id="commonSubmit" value="<?= $this->getName() ?>"><br>
<br>
<?php
return ob_get_clean();
}
}
@@ -157,23 +157,4 @@ class che_국호변경 extends Command\NationCommand
$general->applyDB($db);
return true;
}
public function getJSPlugins(): array
{
return [
'colorSelect'
];
}
public function getForm(): string
{
ob_start();
?>
나라의 이름을 바꿉니다. 황제가 된 후 1회 가능합니다.<br>
국명 : <input type='text' class='formInput' name="nationName" id="nationName" size='18' maxlength='18' style='color:white;background-color:black;'> <input type=button id="commonSubmit" value="<?= $this->getName() ?>"><br>
<br>
<?php
return ob_get_clean();
}
}
+24 -33
View File
@@ -196,48 +196,39 @@ class che_급습 extends Command\NationCommand
return true;
}
public function getJSPlugins(): array
{
return [
'defaultSelectNationByMap'
];
}
public function getForm(): string
public function exportJSVars(): array
{
$generalObj = $this->generalObj;
$nationID = $generalObj->getNationID();
$nationList = [];
$testTurn = new LastTurn($this->getName(), null, $this->getPreReqTurn());
foreach (getAllNationStaticInfo() as $destNation) {
if ($destNation['nation'] == $nationID) {
continue;
}
$testTurn->setArg(['destNationID' => $destNation['nation']]);
$testCommand = new static($generalObj, $this->env, $testTurn, ['destNationID' => $destNation['nation']]);
if ($testCommand->hasFullConditionMet()) {
$destNation['availableCommand'] = true;
} else {
$destNation['availableCommand'] = false;
$nationTarget = [
'id' => $destNation['nation'],
'name' => $destNation['name'],
'color' => $destNation['color'],
'power' => $destNation['power'],
];
if (!$testCommand->hasFullConditionMet()) {
$nationTarget['notAvailable'] = true;
}
if ($destNation['id'] == $nationID) {
$nationTarget['notAvailable'] = true;
}
$nationList[] = $destNation;
$nationList[] = $nationTarget;
}
ob_start();
?>
<?= \sammo\getMapHtml() ?><br>
선택된 국가에 급습을 발동합니다.<br>
선포, 전쟁중인 상대국에만 가능합니다.<br>
상대 국가를 목록에서 선택하세요.<br>
배경색은 현재 급습 불가능 국가는 <font color=red>붉은색</font>으로 표시됩니다.<br>
<select class='formInput' name="destNationID" id="destNationID" size='1' style='color:white;background-color:black;'>
<?php foreach ($nationList as $nation) : ?>
<option value='<?= $nation['nation'] ?>' style='color:<?= $nation['color'] ?>;<?= $nation['availableCommand'] ? '' : 'background-color:red;' ?>'>【<?= $nation['name'] ?> 】</option>
<?php endforeach; ?>
<input type=button id="commonSubmit" value="<?= $this->getName() ?>">
<?php
return ob_get_clean();
return [
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'nationList' => $nationList,
'startYear' => $this->env['startyear'],
],
];
}
}
+15 -46
View File
@@ -214,53 +214,22 @@ class che_몰수 extends Command\NationCommand
return true;
}
public function getForm(): string
public function exportJSVars(): array
{
//TODO: 암행부처럼 보여야...
$db = DB::db();
$destRawGenerals = $db->query('SELECT no,name,officer_level,npc,gold,rice FROM general WHERE nation = %i ORDER BY npc,binary(name)', $this->generalObj->getNationID());
$destGeneralList = [];
foreach ($destRawGenerals as $destGeneral) {
$nameColor = \sammo\getNameColor($destGeneral['npc']);
if ($nameColor) {
$nameColor = " style='color:{$nameColor}'";
}
$name = $destGeneral['name'];
if ($destGeneral['officer_level'] >= 5) {
$name = "*{$name}*";
}
$destGeneralList[] = [
'no' => $destGeneral['no'],
'color' => $nameColor,
'name' => $name,
'gold' => $destGeneral['gold'],
'rice' => $destGeneral['rice']
];
}
ob_start();
?>
장수의 자금이나 군량을 몰수합니다.<br>
몰수한것은 국가재산으로 귀속됩니다.<br>
<select class='formInput' name="destGeneralID" id="destGeneralID" size='1' style='color:white;background-color:black;'>
<?php foreach ($destGeneralList as $destGeneral) : ?>
<option value='<?= $destGeneral['no'] ?>' <?= $destGeneral['color'] ?>><?= $destGeneral['name'] ?>(금:<?= $destGeneral['gold'] ?>, 쌀:<?= $destGeneral['rice'] ?>)</option>
<?php endforeach; ?>
</select>
<select class='formInput' name="isGold" id="isGold" size='1' style='color:white;background-color:black;'>
<option value="true">금</option>
<option value="false">쌀</option>
</select>
</select>
<select class='formInput' name="amount" id="amount" size='1' style='color:white;background-color:black;'>
<?php foreach (GameConst::$resourceActionAmountGuide as $amount) : ?>
<option value='<?= $amount ?>'><?= $amount ?></option>
<?php endforeach; ?>
</select> <input type=button id="commonSubmit" value="<?= $this->getName() ?>"><br>
<br>
<?php
return ob_get_clean();
$nationID = $this->getNationID();
$troops = Util::convertArrayToDict($db->query('SELECT * FROM troop WHERE nation=%i', $nationID), 'troop_leader');
$destRawGenerals = $db->queryAllLists('SELECT no,name,officer_level,npc,gold,rice,leadership,strength,intel,city,crew,train,atmos,troop FROM general WHERE nation = %i ORDER BY npc,binary(name)', $nationID);
return [
'procRes' => [
'troops' => $troops,
'generals' => $destRawGenerals,
'generalsKey' => ['no', 'name', 'officerLevel', 'npc', 'gold', 'rice', 'leadership', 'strength', 'intel', 'cityID', 'crew', 'train', 'atmos', 'troopID'],
'cities' => \sammo\JSOptionsForCities(),
'minAmount' => 100,
'maxAmount' => GameConst::$maxResourceActionAmount,
'amountGuide' => GameConst::$resourceActionAmountGuide,
]
];
}
}
+95 -82
View File
@@ -1,9 +1,13 @@
<?php
namespace sammo\Command\Nation;
use \sammo\{
DB, Util, JosaUtil,
General, DummyGeneral,
DB,
Util,
JosaUtil,
General,
DummyGeneral,
ActionLogger,
GameConst,
LastTurn,
@@ -21,64 +25,67 @@ use function \sammo\getNationStaticInfo;
use \sammo\Constraint\Constraint;
use \sammo\Constraint\ConstraintHelper;
class che_물자원조 extends Command\NationCommand{
class che_물자원조 extends Command\NationCommand
{
static protected $actionName = '원조';
static public $reqArg = true;
protected function argTest():bool{
if($this->arg === null){
protected function argTest(): bool
{
if ($this->arg === null) {
return false;
}
if(!key_exists('destNationID', $this->arg)){
if (!key_exists('destNationID', $this->arg)) {
return false;
}
$destNationID = $this->arg['destNationID'];
if(!is_int($destNationID)){
if (!is_int($destNationID)) {
return false;
}
if($destNationID < 1){
if ($destNationID < 1) {
return false;
}
if(!key_exists('amountList', $this->arg)){
if (!key_exists('amountList', $this->arg)) {
return false;
}
$amountList = $this->arg['amountList'];
if(!is_array($amountList)){
if (!is_array($amountList)) {
return false;
}
if(count($amountList) != 2){
if (count($amountList) != 2) {
return false;
}
[$goldAmount, $riceAmount] = $amountList;
if(!is_int($goldAmount) || !is_int($riceAmount)){
if (!is_int($goldAmount) || !is_int($riceAmount)) {
return false;
}
if($goldAmount < 0 || $riceAmount < 0){
if ($goldAmount < 0 || $riceAmount < 0) {
return false;
}
if($goldAmount == 0 && $riceAmount == 0){
if ($goldAmount == 0 && $riceAmount == 0) {
return false;
}
$this->arg = [
'destNationID'=>$destNationID,
'amountList'=>[$goldAmount, $riceAmount]
'destNationID' => $destNationID,
'amountList' => [$goldAmount, $riceAmount]
];
return true;
}
protected function init(){
protected function init()
{
$general = $this->generalObj;
$this->setCity();
$this->setNation(['gold', 'rice', 'surlimit']);
$this->minConditionConstraints=[
$this->minConditionConstraints = [
ConstraintHelper::OccupiedCity(),
ConstraintHelper::BeChief(),
ConstraintHelper::SuppliedCity(),
@@ -94,47 +101,51 @@ class che_물자원조 extends Command\NationCommand{
[$goldAmount, $riceAmount] = $this->arg['amountList'];
$limit = $this->nation['level'] * GameConst::$coefAidAmount;
if($goldAmount > $limit || $riceAmount > $limit){
$this->fullConditionConstraints[
ConstraintHelper::AlwaysFail('작위 제한량 이상은 보낼 수 없습니다.')
];
if ($goldAmount > $limit || $riceAmount > $limit) {
$this->fullConditionConstraints[ConstraintHelper::AlwaysFail('작위 제한량 이상은 보낼 수 없습니다.')];
return;
}
$this->fullConditionConstraints=[
$this->fullConditionConstraints = [
ConstraintHelper::ExistsDestNation(),
ConstraintHelper::OccupiedCity(),
ConstraintHelper::BeChief(),
ConstraintHelper::SuppliedCity(),
ConstraintHelper::ReqNationGold(GameConst::$basegold+(($goldAmount>0)?1:0)),
ConstraintHelper::ReqNationRice(GameConst::$baserice+(($riceAmount>0)?1:0)),
ConstraintHelper::ReqNationGold(GameConst::$basegold + (($goldAmount > 0) ? 1 : 0)),
ConstraintHelper::ReqNationRice(GameConst::$baserice + (($riceAmount > 0) ? 1 : 0)),
ConstraintHelper::ReqNationValue('surlimit', '외교제한', '==', 0, '외교제한중입니다.'),
ConstraintHelper::ReqDestNationValue('surlimit', '외교제한', '==', 0, '상대국이 외교제한중입니다.'),
];
}
public function getCost():array{
public function getCost(): array
{
return [0, 0];
}
public function getPreReqTurn():int{
public function getPreReqTurn(): int
{
return 0;
}
public function getPostReqTurn():int{
public function getPostReqTurn(): int
{
//NOTE: 자체 postReqTurn 사용
return 12;
}
public function getNextAvailableTurn():?int{
public function getNextAvailableTurn(): ?int
{
return null;
}
public function setNextAvailable(?int $yearMonth=null){
public function setNextAvailable(?int $yearMonth = null)
{
return;
}
public function getBrief():string{
public function getBrief(): string
{
[$goldAmount, $riceAmount] = $this->arg['amountList'];
$goldAmountText = number_format($goldAmount);
$riceAmountText = number_format($riceAmount);
@@ -144,8 +155,9 @@ class che_물자원조 extends Command\NationCommand{
}
public function run():bool{
if(!$this->hasFullConditionMet()){
public function run(): bool
{
if (!$this->hasFullConditionMet()) {
throw new \RuntimeException('불가능한 커맨드를 강제로 실행 시도');
}
@@ -196,7 +208,7 @@ class che_물자원조 extends Command\NationCommand{
$broadcastMessage = "<D><b>{$destNationName}</b></>{$josaRo} 금<C>{$goldAmountText}</> 쌀<C>{$riceAmountText}</>을 지원했습니다.";
$chiefList = $db->queryFirstColumn('SELECT no FROM general WHERE officer_level >= 5 AND no != %i AND nation = %i', $generalID, $nationID);
foreach($chiefList as $chiefID){
foreach ($chiefList as $chiefID) {
$chiefLogger = new ActionLogger($chiefID, $nationID, $year, $month);
$chiefLogger->pushGeneralActionLog($broadcastMessage, ActionLogger::PLAIN);
$chiefLogger->flush();
@@ -211,7 +223,7 @@ class che_물자원조 extends Command\NationCommand{
$destBroadcastMessage = $broadcastMessage = "<D><b>{$nationName}</b></>에서 금<C>{$goldAmountText}</> 쌀<C>{$riceAmountText}</>을 원조했습니다.";
$destChiefList = $db->queryFirstColumn('SELECT no FROM general WHERE officer_level >= 5 AND nation = %i', $destNationID);
foreach($destChiefList as $destChiefID){
foreach ($destChiefList as $destChiefID) {
$destChiefLogger = new ActionLogger($destChiefID, $nationID, $year, $month);
$destChiefLogger->pushGeneralActionLog($destBroadcastMessage, ActionLogger::PLAIN);
$destChiefLogger->flush();
@@ -222,19 +234,19 @@ class che_물자원조 extends Command\NationCommand{
$destNationLogger->pushNationalHistoryLog("<D><b>{$nationName}</b></>{$josaRoSrc}부터 금<C>{$goldAmountText}</> 쌀<C>{$riceAmountText}</>을 지원 받음");
$destNationStor = KVStorage::getStorage(DB::db(), $destNationID, 'nation_env');
$destRecvAssist = $destNationStor->getValue('recv_assist')??[];
$destRecvAssist["n{$nationID}"] = [$nationID, ($destRecvAssist["n{$nationID}"][1]??0) + $goldAmount + $riceAmount];
$destRecvAssist = $destNationStor->getValue('recv_assist') ?? [];
$destRecvAssist["n{$nationID}"] = [$nationID, ($destRecvAssist["n{$nationID}"][1] ?? 0) + $goldAmount + $riceAmount];
$destNationStor->setValue('recv_assist', $destRecvAssist);
$db->update('nation', [
'gold'=>$db->sqleval('gold - %i', $goldAmount),
'rice'=>$db->sqleval('rice - %i', $riceAmount),
'surlimit'=>$db->sqleval('surlimit + %i', $this->getPostReqTurn())
'gold' => $db->sqleval('gold - %i', $goldAmount),
'rice' => $db->sqleval('rice - %i', $riceAmount),
'surlimit' => $db->sqleval('surlimit + %i', $this->getPostReqTurn())
], 'nation = %i', $nationID);
$db->update('nation', [
'gold'=>$db->sqleval('gold + %i', $goldAmount),
'rice'=>$db->sqleval('rice + %i', $riceAmount),
'gold' => $db->sqleval('gold + %i', $goldAmount),
'rice' => $db->sqleval('rice + %i', $riceAmount),
], 'nation = %i', $destNationID);
$general->addExperience(5);
@@ -246,47 +258,48 @@ class che_물자원조 extends Command\NationCommand{
return true;
}
public function getJSPlugins(): array
public function exportJSVars(): array
{
$generalObj = $this->generalObj;
$nationList = [];
foreach (getAllNationStaticInfo() as $destNation) {
$nationTarget = [
'id' => $destNation['nation'],
'name' => $destNation['name'],
'color' => $destNation['color'],
'power' => $destNation['power'],
];
//TODO: 물자원조 자체가 가능한지도 검사?
if ($nationTarget['id'] == $generalObj->getNationID()) {
$nationTarget['notAvailable'] = true;
}
$nationList[] = $nationTarget;
}
$currentNationLevel = getNationStaticInfo($this->generalObj->getNationID())['level'];
$levelInfo = [];
foreach (\sammo\getNationLevelList() as $level => [$levelText,,]) {
$levelInfo[$level] = ['text' => $levelText, 'amount' => $level * GameConst::$coefAidAmount];
}
$amountGuide = [];
foreach (Util::range(1, $currentNationLevel + 1) as $nationLevel) {
$amountGuide[] = $nationLevel * GameConst::$coefAidAmount;
}
return [
'defaultSelectNationByMap'
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'nationList' => $nationList,
'currentNationLevel' => $currentNationLevel,
'levelInfo' => $levelInfo,
'minAmount' => 1000,
'maxAmount' => Util::array_last($amountGuide),
'amountGuide' => $amountGuide,
],
];
}
public function getForm(): string
{
$currentNationLevel = getNationStaticInfo($this->generalObj->getNationID())['level'];
ob_start();
?>
<?=\sammo\getMapHtml()?><br>
타국에게 원조합니다.<br>
작위별로 금액 제한이 있습니다.<br>
<?php foreach(\sammo\getNationLevelList() as $level => [$levelText,,]): ?>
<?=StringUtil::padStringAlignRight($levelText, 10)?>: <?=number_format($level*GameConst::$coefAidAmount)?><br>
<?php endforeach; ?>
원조할 국가를 목록에서 선택하세요.<br>
<select class='formInput' name="destNationID" id="destNationID" size='1' style='color:white;background-color:black;'>
<?php foreach(getAllNationStaticInfo() as $nation): ?>
<option
value='<?=$nation['nation']?>'
style='color:<?=$nation['color']?>;'
>【<?=$nation['name']?> 】</option>
<?php endforeach; ?>
</select>
국고 <select class='formInput amountList' name="amountList[]" size='1' style='color:white;background-color:black;'>
<?php foreach(Util::range($currentNationLevel+1) as $nationLevel): ?>
<option value='<?=$nationLevel*GameConst::$coefAidAmount?>'><?=$nationLevel*GameConst::$coefAidAmount?></option>
<?php endforeach; ?>
</select>
병량 <select class='formInput amountList' name="amountList[]" size='1' style='color:white;background-color:black;'>
<?php foreach(Util::range($currentNationLevel+1) as $nationLevel): ?>
<option value='<?=$nationLevel*GameConst::$coefAidAmount?>'><?=$nationLevel*GameConst::$coefAidAmount?></option>
<?php endforeach; ?>
</select>
<input type=button id="commonSubmit" value="<?=$this->getName()?>"><br>
<br>
<?php
return ob_get_clean();
}
}
}
+52 -74
View File
@@ -1,9 +1,13 @@
<?php
namespace sammo\Command\Nation;
use \sammo\{
DB, Util, JosaUtil,
General, DummyGeneral,
DB,
Util,
JosaUtil,
General,
DummyGeneral,
ActionLogger,
GameConst,
LastTurn,
@@ -18,41 +22,44 @@ use function \sammo\cutTurn;
use \sammo\Constraint\Constraint;
use \sammo\Constraint\ConstraintHelper;
class che_발령 extends Command\NationCommand{
class che_발령 extends Command\NationCommand
{
static protected $actionName = '발령';
static public $reqArg = true;
protected function argTest():bool{
if($this->arg === null){
protected function argTest(): bool
{
if ($this->arg === null) {
return false;
}
//NOTE: 사망 직전에 턴을 넣을 수 있으므로, 존재하지 않는 장수여도 argTest에서 바로 탈락시키지 않음
if(!key_exists('destGeneralID', $this->arg)){
if (!key_exists('destGeneralID', $this->arg)) {
return false;
}
if(!key_exists('destCityID', $this->arg)){
if (!key_exists('destCityID', $this->arg)) {
return false;
}
if(CityConst::byID($this->arg['destCityID']) === null){
if (CityConst::byID($this->arg['destCityID']) === null) {
return false;
}
$destGeneralID = $this->arg['destGeneralID'];
$destCityID = $this->arg['destCityID'];
$this->arg = [
'destGeneralID'=>$destGeneralID,
'destCityID'=>$destCityID,
'destGeneralID' => $destGeneralID,
'destCityID' => $destCityID,
];
return true;
}
protected function init(){
protected function init()
{
$general = $this->generalObj;
$this->setCity();
$this->setNation();
$this->minConditionConstraints=[
$this->minConditionConstraints = [
ConstraintHelper::BeChief(),
ConstraintHelper::NotBeNeutral(),
ConstraintHelper::OccupiedCity(),
@@ -67,14 +74,14 @@ class che_발령 extends Command\NationCommand{
$destGeneral = General::createGeneralObjFromDB($this->arg['destGeneralID'], null, 1);
$this->setDestGeneral($destGeneral);
if($this->arg['destGeneralID'] == $this->getGeneral()->getID()){
$this->fullConditionConstraints=[
if ($this->arg['destGeneralID'] == $this->getGeneral()->getID()) {
$this->fullConditionConstraints = [
ConstraintHelper::AlwaysFail('본인입니다')
];
return;
}
$this->fullConditionConstraints=[
$this->fullConditionConstraints = [
ConstraintHelper::BeChief(),
ConstraintHelper::NotBeNeutral(),
ConstraintHelper::OccupiedCity(),
@@ -86,29 +93,34 @@ class che_발령 extends Command\NationCommand{
];
}
public function getFailString():string{
public function getFailString(): string
{
$commandName = $this->getName();
$failReason = $this->testFullConditionMet();
if($failReason === null){
if ($failReason === null) {
throw new \RuntimeException('실행 가능한 커맨드에 대해 실패 이유를 수집');
}
$destGeneralName = $this->destGeneralObj->getName();
return "{$failReason} <Y>{$destGeneralName}</> {$commandName} 실패.";
}
public function getCost():array{
public function getCost(): array
{
return [0, 0];
}
public function getPreReqTurn():int{
public function getPreReqTurn(): int
{
return 0;
}
public function getPostReqTurn():int{
public function getPostReqTurn(): int
{
return 0;
}
public function getBrief():string{
public function getBrief(): string
{
$commandName = $this->getName();
$destGeneralName = $this->destGeneralObj->getName();
$destCityName = CityConst::byID($this->arg['destCityID'])->name;
@@ -117,8 +129,9 @@ class che_발령 extends Command\NationCommand{
}
public function run():bool{
if(!$this->hasFullConditionMet()){
public function run(): bool
{
if (!$this->hasFullConditionMet()) {
throw new \RuntimeException('불가능한 커맨드를 강제로 실행 시도');
}
@@ -144,8 +157,8 @@ class che_발령 extends Command\NationCommand{
$destGeneral->getLogger()->pushGeneralActionLog("<Y>{$generalName}</>에 의해 <G><b>{$destCityName}</b></>{$josaRo} 발령됐습니다. <1>$date</>");
$yearMonth = Util::joinYearMonth($this->env['year'], $this->env['month']);
if(cutTurn($general->getTurnTime(), $this->env['turnterm']) != cutTurn($destGeneral->getTurnTime(), $this->env['turnterm'])){
$yearMonth+=1;
if (cutTurn($general->getTurnTime(), $this->env['turnterm']) != cutTurn($destGeneral->getTurnTime(), $this->env['turnterm'])) {
$yearMonth += 1;
}
$destGeneral->setAuxVar('last발령', $yearMonth);
$logger->pushGeneralActionLog("<Y>{$destGeneralName}</>{$josaUl} <G><b>{$destCityName}</b></>{$josaRo} 발령했습니다. <1>$date</>");
@@ -157,56 +170,21 @@ class che_발령 extends Command\NationCommand{
return true;
}
public function getJSPlugins(): array
{
return [
'defaultSelectCityByMap'
];
}
public function getForm(): string
public function exportJSVars(): array
{
$db = DB::db();
$nationID = $this->generalObj->getNationID();
$nationID = $this->getNationID();
$troops = Util::convertArrayToDict($db->query('SELECT * FROM troop WHERE nation=%i', $nationID), 'troop_leader');
$destRawGenerals = $db->query('SELECT no,name,officer_level,npc,gold,rice,leadership,strength,intel,city,crew,train,atmos,troop FROM general WHERE nation = %i ORDER BY npc,binary(name)',$nationID);
$destGeneralList = [];
foreach($destRawGenerals as $destGeneral){
$nameColor = \sammo\getNameColor($destGeneral['npc']);
if($nameColor){
$nameColor = " style='color:{$nameColor}'";
}
$name = $destGeneral['name'];
if($destGeneral['officer_level'] >= 5){
$name = "*{$name}*";
}
$destGeneral['name'] = $name;
$destGeneral['color'] = $nameColor;
$destGeneralList[] = $destGeneral;
}
ob_start();
?>
<?=\sammo\getMapHtml()?><br>
선택된 도시로 아국 장수를 발령합니다.<br>
아국 도시로만 발령이 가능합니다.<br>
목록을 선택하거나 도시를 클릭하세요.<br>
<select class='formInput' name="destGeneralID" id="destGeneralID" size='1' style='color:white;background-color:black;'>
<?php foreach($destGeneralList as $destGeneral): ?>
<option value='<?=$destGeneral['no']?>' <?=$destGeneral['color']?>><?=$destGeneral['name']?>[<?=CityConst::byID($destGeneral['city'])->name?>]
(<?=$destGeneral['leadership']?>/<?=$destGeneral['strength']?>/<?=$destGeneral['intel']?>)
&lt;병<?=number_format($destGeneral['crew'])?>/훈<?=$destGeneral['train']?>/사<?=$destGeneral['atmos']?>/<?=$destGeneral['troop']?$troops[$destGeneral['troop']]['name']:'-'?>&gt;
</option>
<?php endforeach; ?>
</select>
<select class='formInput' name="destCityID" id="destCityID" size='1' style='color:white;background-color:black;'>
<?=\sammo\optionsForCities()?><br>
</select> <input type=button id="commonSubmit" value="<?=$this->getName()?>"><br>
<br>
<?php
return ob_get_clean();
$destRawGenerals = $db->queryAllLists('SELECT no,name,officer_level,npc,gold,rice,leadership,strength,intel,city,crew,train,atmos,troop FROM general WHERE nation = %i ORDER BY npc,binary(name)', $nationID);
return [
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'distanceList' => \sammo\JSCitiesBasedOnDistance($this->generalObj->getCityID(), 1),
'cities' => \sammo\JSOptionsForCities(),
'troops' => $troops,
'generals' => $destRawGenerals,
'generalsKey' => ['no', 'name', 'officerLevel', 'npc', 'gold', 'rice', 'leadership', 'strength', 'intel', 'cityID', 'crew', 'train', 'atmos', 'troopID']
]
];
}
}
}
+41 -41
View File
@@ -1,9 +1,13 @@
<?php
namespace sammo\Command\Nation;
use \sammo\{
DB, Util, JosaUtil,
General, DummyGeneral,
DB,
Util,
JosaUtil,
General,
DummyGeneral,
ActionLogger,
GameConst,
LastTurn,
@@ -18,30 +22,33 @@ use \sammo\Constraint\Constraint;
use \sammo\Constraint\ConstraintHelper;
use sammo\Event\Action;
class che_백성동원 extends Command\NationCommand{
class che_백성동원 extends Command\NationCommand
{
static protected $actionName = '백성동원';
static public $reqArg = true;
protected function argTest():bool{
if($this->arg === null){
protected function argTest(): bool
{
if ($this->arg === null) {
return false;
}
if(!key_exists('destCityID', $this->arg)){
if (!key_exists('destCityID', $this->arg)) {
return false;
}
if(CityConst::byID($this->arg['destCityID']) === null){
if (CityConst::byID($this->arg['destCityID']) === null) {
return false;
}
$destCityID = $this->arg['destCityID'];
$this->arg = [
'destCityID'=>$destCityID,
'destCityID' => $destCityID,
];
return true;
}
protected function init(){
protected function init()
{
$general = $this->generalObj;
$env = $this->env;
@@ -49,7 +56,7 @@ class che_백성동원 extends Command\NationCommand{
$this->setCity();
$this->setNation(['strategic_cmd_limit']);
$this->minConditionConstraints=[
$this->minConditionConstraints = [
ConstraintHelper::OccupiedCity(),
ConstraintHelper::BeChief(),
ConstraintHelper::AvailableStrategicCommand()
@@ -61,7 +68,7 @@ class che_백성동원 extends Command\NationCommand{
$this->setDestCity($this->arg['destCityID']);
$this->setDestNation($this->destCity['nation']);
$this->fullConditionConstraints=[
$this->fullConditionConstraints = [
ConstraintHelper::OccupiedCity(),
ConstraintHelper::BeChief(),
ConstraintHelper::OccupiedDestCity(),
@@ -69,38 +76,44 @@ class che_백성동원 extends Command\NationCommand{
];
}
public function getCommandDetailTitle():string{
public function getCommandDetailTitle(): string
{
$name = $this->getName();
$reqTurn = $this->getPreReqTurn()+1;
$reqTurn = $this->getPreReqTurn() + 1;
$postReqTurn = $this->getPostReqTurn();
return "{$name}/{$reqTurn}턴(재사용 대기 $postReqTurn)";
}
public function getCost():array{
public function getCost(): array
{
return [0, 0];
}
public function getPreReqTurn():int{
public function getPreReqTurn(): int
{
return 0;
}
public function getPostReqTurn():int{
public function getPostReqTurn(): int
{
$genCount = Util::valueFit($this->nation['gennum'], GameConst::$initialNationGenLimit);
$nextTerm = Util::round(sqrt($genCount*4)*10);
$nextTerm = Util::round(sqrt($genCount * 4) * 10);
$nextTerm = $this->generalObj->onCalcStrategic($this->getName(), 'delay', $nextTerm);
return $nextTerm;
}
public function getBrief():string{
public function getBrief(): string
{
$commandName = $this->getName();
$destCityName = CityConst::byID($this->arg['destCityID'])->name;
return "{$destCityName}】에 {$commandName}";
}
public function run():bool{
if(!$this->hasFullConditionMet()){
public function run(): bool
{
if (!$this->hasFullConditionMet()) {
throw new \RuntimeException('불가능한 커맨드를 강제로 실행 시도');
}
@@ -132,7 +145,7 @@ class che_백성동원 extends Command\NationCommand{
$broadcastMessage = "<Y>{$generalName}</>{$josaYi} <G><b>{$destCityName}</b></>에 <M>백성동원</>을 하였습니다.";
$targetGeneralList = $db->queryFirstColumn('SELECT no FROM general WHERE nation=%i AND no != %i', $nationID, $generalID);
foreach($targetGeneralList as $targetGeneralID){
foreach ($targetGeneralList as $targetGeneralID) {
$targetLogger = new ActionLogger($targetGeneralID, $nationID, $year, $month);
$targetLogger->pushGeneralActionLog($broadcastMessage, ActionLogger::PLAIN);
$targetLogger->flush();
@@ -159,27 +172,14 @@ class che_백성동원 extends Command\NationCommand{
return true;
}
public function getJSPlugins(): array
public function exportJSVars(): array
{
return [
'defaultSelectCityByMap'
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'cities' => \sammo\JSOptionsForCities(),
'distanceList' => new \stdClass(),
],
];
}
public function getForm(): string
{
ob_start();
?>
<?=\sammo\getMapHtml()?><br>
선택된 도시에 백성을 동원해 성벽을 쌓습니다.<br>
아국 도시만 가능합니다.<br>
목록을 선택하거나 도시를 클릭하세요.<br>
<select class='formInput' name="destCityID" id="destCityID" size='1' style='color:white;background-color:black;'>
<?=\sammo\optionsForCities()?><br>
</select> <input type=button id="commonSubmit" value="<?=$this->getName()?>"><br>
<br>
<?php
return ob_get_clean();
}
}
}
@@ -223,74 +223,46 @@ class che_불가침제의 extends Command\NationCommand
return true;
}
public function getJSPlugins(): array
{
return [
'defaultSelectNationByMap'
];
}
public function getForm(): string
public function exportJSVars(): array
{
$generalObj = $this->generalObj;
$nationID = $generalObj->getNationID();
$db = DB::db();
$nationList = [];
$testTurn = new LastTurn($this->getName(), null, $this->getPreReqTurn());
$currYear = $this->env['year'];
$diplomacyStatus = Util::convertArrayToDict(
$db->query('SELECT * FROM diplomacy WHERE me = %i', $nationID),
'you'
);
$nationList = [];
foreach (getAllNationStaticInfo() as $destNation) {
if ($destNation['nation'] == $nationID) {
continue;
}
$testCommand = new static($generalObj, $this->env, $this->getLastTurn(), [
$testCommand = new static($generalObj, $this->env, $testTurn, [
'destNationID' => $destNation['nation'],
'year' => $currYear + 1,
'month' => 12
'year' => $currYear + 2,
'month' => 1
]);
$nationTarget = [
'id' => $destNation['nation'],
'name' => $destNation['name'],
'color' => $destNation['color'],
'power' => $destNation['power'],
];
if (!$testCommand->hasFullConditionMet()) {
$destNation['cssBgColor'] = 'background-color:red;';
} else if ($diplomacyStatus[$destNation['nation']]['state'] == 7) {
$destNation['cssBgColor'] = 'background-color:blue;';
} else {
$destNation['cssBgColor'] = '';
$nationTarget['notAvailable'] = true;
}
if ($destNation['id'] == $nationID) {
$nationTarget['notAvailable'] = true;
}
$nationList[] = $destNation;
$nationList[] = $nationTarget;
}
ob_start();
?>
<?= \sammo\getMapHtml() ?><br>
타국에게 불가침을 제의합니다.<br>
제의할 국가를 목록에서 선택하세요.<br>
불가침 기한 다음 달부터 선포 가능합니다.<br>
배경색은 현재 제의가 불가능한 국가는 <font color=red>붉은색</font>, 현재 불가침중인 국가는 <font color=blue>푸른색</font>으로 표시됩니다.<br>
<br>
<select class='formInput' name="destNationID" id="destNationID" size='1' style='color:white;background-color:black;'>
<?php foreach ($nationList as $nation) : ?>
<option value='<?= $nation['nation'] ?>' style='color:<?= $nation['color'] ?>;<?= $nation['cssBgColor'] ?>'>【<?= $nation['name'] ?> 】</option>
<?php endforeach; ?>
</select>에게
<select class='formInput' name="year" id="year" size='1' style='color:white;background-color:black;'>
<?php foreach (Util::range($currYear + 1, $currYear + 20 + 1) as $formYear) : ?>
<option value='<?= $formYear ?>'><?= $formYear ?></option>
<?php endforeach; ?>
</select>년
<select class='formInput' name="month" id="month" size='1' style='color:white;background-color:black;'>
<?php foreach (Util::range(1, 12 + 1) as $formMonth) : ?>
<option value='<?= $formMonth ?>'><?= $formMonth ?></option>
<?php endforeach; ?>
</select>월까지
<input type=button id="commonSubmit" value="<?= $this->getName() ?>">
<?php
return ob_get_clean();
return [
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'nationList' => $nationList,
'startYear' => $this->env['startyear'],
'minYear' => $this->env['year'] + 1,
'maxYear' => $this->env['year'] + 20,
'month' => $this->env['month'],
],
];
}
}
@@ -170,17 +170,11 @@ class che_불가침파기제의 extends Command\NationCommand{
return true;
}
public function getJSPlugins(): array
{
return [
'defaultSelectNationByMap'
];
}
public function getForm(): string
public function exportJSVars(): array
{
$generalObj = $this->generalObj;
$nationID = $generalObj->getNationID();
$nationList = [];
$db = DB::db();
$diplomacyStatus = Util::convertArrayToDict(
@@ -188,39 +182,29 @@ class che_불가침파기제의 extends Command\NationCommand{
'you'
);
$nationList = [];
foreach(getAllNationStaticInfo() as $destNation){
if($destNation['nation'] == $nationID){
continue;
}
foreach (getAllNationStaticInfo() as $destNation) {
$nationTarget = [
'id' => $destNation['nation'],
'name' => $destNation['name'],
'color' => $destNation['color'],
'power' => $destNation['power'],
];
if($diplomacyStatus[$destNation['nation']]['state'] != 7){
$destNation['cssBgColor'] = 'background-color:red;';
$nationTarget['notAvailable'] = true;
}
else{
$destNation['cssBgColor'] = '';
if ($destNation['id'] == $nationID) {
$nationTarget['notAvailable'] = true;
}
$nationList[] = $destNation;
$nationList[] = $nationTarget;
}
ob_start();
?>
<?=\sammo\getMapHtml()?><br>
불가침중인 국가에 조약 파기를 제의합니다.<br>
제의할 국가를 목록에서 선택하세요.<br>
배경색은 현재 제의가 불가능한 국가는 <font color=red>붉은색</font>으로 표시됩니다.<br>
<br>
<select class='formInput' name="destNationID" id="destNationID" size='1' style='color:white;background-color:black;'>
<?php foreach($nationList as $nation): ?>
<option
value='<?=$nation['nation']?>'
style='color:<?=$nation['color']?>;<?=$nation['cssBgColor']?>'
>【<?=$nation['name']?> 】</option>
<?php endforeach; ?>
</select>에게
<input type=button id="commonSubmit" value="<?=$this->getName()?>">
<?php
return ob_get_clean();
return [
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'nationList' => $nationList,
'startYear' => $this->env['startyear'],
],
];
}
}
+24 -36
View File
@@ -192,50 +192,38 @@ class che_선전포고 extends Command\NationCommand
return true;
}
public function getJSPlugins(): array
{
return [
'defaultSelectNationByMap'
];
}
public function getForm(): string
public function exportJSVars(): array
{
$generalObj = $this->generalObj;
$nationID = $generalObj->getNationID();
$startYear = $this->env['startyear'];
$availableYear = $startYear + 1;
$nationList = [];
$testTurn = new LastTurn($this->getName(), null, $this->getPreReqTurn());
foreach (getAllNationStaticInfo() as $destNation) {
if ($destNation['nation'] == $nationID) {
continue;
$testTurn->setArg(['destNationID' => $destNation['nation']]);
$testCommand = new static($generalObj, $this->env, $testTurn, ['destNationID' => $destNation['nation']]);
$nationTarget = [
'id' => $destNation['nation'],
'name' => $destNation['name'],
'color' => $destNation['color'],
'power' => $destNation['power'],
];
if (!$testCommand->hasFullConditionMet()) {
$nationTarget['notAvailable'] = true;
}
if ($destNation['id'] == $nationID) {
$nationTarget['notAvailable'] = true;
}
$testCommand = new static($generalObj, $this->env, $this->getLastTurn(), ['destNationID' => $destNation['nation']]);
if ($testCommand->hasFullConditionMet()) {
$destNation['availableWar'] = true;
} else {
$destNation['availableWar'] = false;
}
$nationList[] = $destNation;
$nationList[] = $nationTarget;
}
ob_start();
?>
<?= \sammo\getMapHtml() ?><br>
타국에게 선전 포고합니다.<br>
선전 포고할 국가를 목록에서 선택하세요.<br>
고립되지 않은 아국 도시에서 인접한 국가에 선포 가능합니다.<br>
초반제한 해제 2년전부터 선포가 가능합니다. (<?= $availableYear ?>년 1월부터 가능)<br>
현재 선포가 불가능한 국가는 배경색이 <font color=red>붉은색</font>으로 표시됩니다.<br>
<select class='formInput' name="destNationID" id="destNationID" size='1' style='color:white;background-color:black;'>
<?php foreach ($nationList as $nation) : ?>
<option value='<?= $nation['nation'] ?>' style='color:<?= $nation['color'] ?>;<?= $nation['availableWar'] ? '' : 'background-color:red;' ?>'>【<?= $nation['name'] ?> 】</option>
<?php endforeach; ?>
</select>
<input type=button id="commonSubmit" value="<?= $this->getName() ?>">
<?php
return ob_get_clean();
return [
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'nationList' => $nationList,
'startYear' => $this->env['startyear'],
],
];
}
}
+45 -44
View File
@@ -1,9 +1,13 @@
<?php
namespace sammo\Command\Nation;
use \sammo\{
DB, Util, JosaUtil,
General, DummyGeneral,
DB,
Util,
JosaUtil,
General,
DummyGeneral,
ActionLogger,
GameConst,
LastTurn,
@@ -20,30 +24,33 @@ use \sammo\Constraint\Constraint;
use \sammo\Constraint\ConstraintHelper;
use sammo\Event\Action;
class che_수몰 extends Command\NationCommand{
class che_수몰 extends Command\NationCommand
{
static protected $actionName = '수몰';
static public $reqArg = true;
protected function argTest():bool{
if($this->arg === null){
protected function argTest(): bool
{
if ($this->arg === null) {
return false;
}
if(!key_exists('destCityID', $this->arg)){
if (!key_exists('destCityID', $this->arg)) {
return false;
}
if(CityConst::byID($this->arg['destCityID']) === null){
if (CityConst::byID($this->arg['destCityID']) === null) {
return false;
}
$destCityID = $this->arg['destCityID'];
$this->arg = [
'destCityID'=>$destCityID,
'destCityID' => $destCityID,
];
return true;
}
protected function init(){
protected function init()
{
$general = $this->generalObj;
$env = $this->env;
@@ -51,7 +58,7 @@ class che_수몰 extends Command\NationCommand{
$this->setCity();
$this->setNation(['strategic_cmd_limit']);
$this->minConditionConstraints=[
$this->minConditionConstraints = [
ConstraintHelper::OccupiedCity(),
ConstraintHelper::BeChief(),
ConstraintHelper::AvailableStrategicCommand(),
@@ -63,7 +70,7 @@ class che_수몰 extends Command\NationCommand{
$this->setDestCity($this->arg['destCityID']);
$this->setDestNation($this->destCity['nation']);
$this->fullConditionConstraints=[
$this->fullConditionConstraints = [
ConstraintHelper::OccupiedCity(),
ConstraintHelper::BeChief(),
ConstraintHelper::NotNeutralDestCity(),
@@ -73,39 +80,45 @@ class che_수몰 extends Command\NationCommand{
];
}
public function getCommandDetailTitle():string{
public function getCommandDetailTitle(): string
{
$name = $this->getName();
$reqTurn = $this->getPreReqTurn()+1;
$reqTurn = $this->getPreReqTurn() + 1;
$postReqTurn = $this->getPostReqTurn();
return "{$name}/{$reqTurn}턴(재사용 대기 $postReqTurn)";
}
public function getCost():array{
public function getCost(): array
{
return [0, 0];
}
public function getPreReqTurn():int{
public function getPreReqTurn(): int
{
return 2;
}
public function getPostReqTurn():int{
public function getPostReqTurn(): int
{
$genCount = Util::valueFit($this->nation['gennum'], GameConst::$initialNationGenLimit);
$nextTerm = Util::round(sqrt($genCount*4)*10);
$nextTerm = Util::round(sqrt($genCount * 4) * 10);
$nextTerm = $this->generalObj->onCalcStrategic($this->getName(), 'delay', $nextTerm);
return $nextTerm;
}
public function getBrief():string{
public function getBrief(): string
{
$commandName = $this->getName();
$destCityName = CityConst::byID($this->arg['destCityID'])->name;
$josaUl = JosaUtil::pick($destCityName, '을');
return "{$destCityName}{$josaUl} {$commandName}";
}
public function run():bool{
if(!$this->hasFullConditionMet()){
public function run(): bool
{
if (!$this->hasFullConditionMet()) {
throw new \RuntimeException('불가능한 커맨드를 강제로 실행 시도');
}
@@ -140,7 +153,7 @@ class che_수몰 extends Command\NationCommand{
$broadcastMessage = "<Y>{$generalName}</>{$josaYi} <G><b>{$destCityName}</b></>에 <M>수몰</>을 발동하였습니다.";
$targetGeneralList = $db->queryFirstColumn('SELECT no FROM general WHERE nation=%i AND no != %i', $nationID, $generalID);
foreach($targetGeneralList as $targetGeneralID){
foreach ($targetGeneralList as $targetGeneralID) {
$targetLogger = new ActionLogger($targetGeneralID, $nationID, $year, $month);
$targetLogger->pushGeneralActionLog($broadcastMessage, ActionLogger::PLAIN);
$targetLogger->flush();
@@ -150,12 +163,13 @@ class che_수몰 extends Command\NationCommand{
$targetGeneralList = $db->queryFirstColumn('SELECT no FROM general WHERE nation=%i', $destNationID);
$destNationLogged = false;
foreach($targetGeneralList as $targetGeneralID){
foreach ($targetGeneralList as $targetGeneralID) {
$targetLogger = new ActionLogger($targetGeneralID, $destNationID, $year, $month);
$targetLogger->pushGeneralActionLog($destBroadcastMessage, ActionLogger::PLAIN);
if(!$destNationLogged){
if (!$destNationLogged) {
$targetLogger->pushNationalHistoryLog(
"<D><b>{$nationName}</b></>의 <Y>{$generalName}</>{$josaYi} 아국의 <G><b>{$destCityName}</b></>에 <M>수몰</>을 발동", ActionLogger::PLAIN
"<D><b>{$nationName}</b></>의 <Y>{$generalName}</>{$josaYi} 아국의 <G><b>{$destCityName}</b></>에 <M>수몰</>을 발동",
ActionLogger::PLAIN
);
$destNationLogged = true;
}
@@ -182,27 +196,14 @@ class che_수몰 extends Command\NationCommand{
return true;
}
public function getJSPlugins(): array
public function exportJSVars(): array
{
return [
'defaultSelectCityByMap'
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'cities' => \sammo\JSOptionsForCities(),
'distanceList' => new \stdClass(),
],
];
}
public function getForm(): string
{
ob_start();
?>
<?=\sammo\getMapHtml()?><br>
선택된 도시에 수몰을 발동합니다.<br>
전쟁중인 상대국 도시만 가능합니다.<br>
목록을 선택하거나 도시를 클릭하세요.<br>
<select class='formInput' name="destCityID" id="destCityID" size='1' style='color:white;background-color:black;'>
<?=\sammo\optionsForCities()?><br>
</select> <input type=button id="commonSubmit" value="<?=$this->getName()?>"><br>
<br>
<?php
return ob_get_clean();
}
}
}
+21 -33
View File
@@ -199,48 +199,36 @@ class che_이호경식 extends Command\NationCommand
return true;
}
public function getJSPlugins(): array
{
return [
'defaultSelectNationByMap'
];
}
public function getForm(): string
public function exportJSVars(): array
{
$generalObj = $this->generalObj;
$nationID = $generalObj->getNationID();
$nationList = [];
$testTurn = new LastTurn($this->getName(), null, $this->getPreReqTurn());
foreach (getAllNationStaticInfo() as $destNation) {
if ($destNation['nation'] == $nationID) {
continue;
}
$testTurn->setArg(['destNationID' => $destNation['nation']]);
$testCommand = new static($generalObj, $this->env, $testTurn, ['destNationID' => $destNation['nation']]);
if ($testCommand->hasFullConditionMet()) {
$destNation['availableCommand'] = true;
} else {
$destNation['availableCommand'] = false;
$nationTarget = [
'id' => $destNation['nation'],
'name' => $destNation['name'],
'color' => $destNation['color'],
'power' => $destNation['power'],
];
if (!$testCommand->hasFullConditionMet()) {
$nationTarget['notAvailable'] = true;
}
if ($destNation['id'] == $nationID) {
$nationTarget['notAvailable'] = true;
}
$nationList[] = $destNation;
$nationList[] = $nationTarget;
}
ob_start();
?>
<?= \sammo\getMapHtml() ?><br>
선택된 국가에 이호경식을 발동합니다.<br>
선포, 전쟁중인 상대국에만 가능합니다.<br>
상대 국가를 목록에서 선택하세요.<br>
배경색은 현재 이호경식 불가능 국가는 <font color=red>붉은색</font>으로 표시됩니다.<br>
<select class='formInput' name="destNationID" id="destNationID" size='1' style='color:white;background-color:black;'>
<?php foreach ($nationList as $nation) : ?>
<option value='<?= $nation['nation'] ?>' style='color:<?= $nation['color'] ?>;<?= $nation['availableCommand'] ? '' : 'background-color:red;' ?>'>【<?= $nation['name'] ?> 】</option>
<?php endforeach; ?>
<input type=button id="commonSubmit" value="<?= $this->getName() ?>">
<?php
return ob_get_clean();
return [
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'nationList' => $nationList,
'startYear' => $this->env['startyear'],
],
];
}
}
+24 -45
View File
@@ -168,57 +168,36 @@ class che_종전제의 extends Command\NationCommand{
return true;
}
public function getJSPlugins(): array
{
return [
'defaultSelectNationByMap'
];
}
public function getForm(): string
public function exportJSVars(): array
{
$generalObj = $this->generalObj;
$nationID = $generalObj->getNationID();
$db = DB::db();
$diplomacyStatus = Util::convertArrayToDict(
$db->query('SELECT * FROM diplomacy WHERE me = %i', $nationID),
'you'
);
$nationList = [];
foreach(getAllNationStaticInfo() as $destNation){
if($destNation['nation'] == $nationID){
continue;
$testTurn = new LastTurn($this->getName(), null, $this->getPreReqTurn());
foreach (getAllNationStaticInfo() as $destNation) {
$testCommand = new static($generalObj, $this->env, $testTurn, ['destNationID' => $destNation['nation']]);
$nationTarget = [
'id' => $destNation['nation'],
'name' => $destNation['name'],
'color' => $destNation['color'],
'power' => $destNation['power'],
];
if (!$testCommand->hasFullConditionMet()) {
$nationTarget['notAvailable'] = true;
}
if ($destNation['id'] == $nationID) {
$nationTarget['notAvailable'] = true;
}
if(!in_array($diplomacyStatus[$destNation['nation']]['state'], [0, 1])){
$destNation['cssBgColor'] = 'background-color:red;';
}
else{
$destNation['cssBgColor'] = '';
}
$nationList[] = $destNation;
$nationList[] = $nationTarget;
}
ob_start();
?>
<?=\sammo\getMapHtml()?><br>
전쟁중인 국가에 종전을 제의합니다.<br>
제의할 국가를 목록에서 선택하세요.<br>
배경색은 현재 제의가 불가능한 국가는 <font color=red>붉은색</font>으로 표시됩니다.<br>
<br>
<select class='formInput' name="destNationID" id="destNationID" size='1' style='color:white;background-color:black;'>
<?php foreach($nationList as $nation): ?>
<option
value='<?=$nation['nation']?>'
style='color:<?=$nation['color']?>;<?=$nation['cssBgColor']?>'
>【<?=$nation['name']?> 】</option>
<?php endforeach; ?>
</select>에게
<input type=button id="commonSubmit" value="<?=$this->getName()?>">
<?php
return ob_get_clean();
return [
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'nationList' => $nationList,
'startYear' => $this->env['startyear'],
],
];
}
}
+6 -19
View File
@@ -230,27 +230,14 @@ class che_천도 extends Command\NationCommand
return true;
}
public function getJSPlugins(): array
public function exportJSVars(): array
{
return [
'defaultSelectCityByMap'
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'cities' => \sammo\JSOptionsForCities(),
'distanceList' => new \stdClass(),
],
];
}
public function getForm(): string
{
ob_start();
?>
<?= \sammo\getMapHtml() ?><br>
선택된 도시로 천도합니다.<br>
현재 수도에서 연결된 도시만 가능하며, 1+2×거리만큼의 턴이 필요합니다.<br>
목록을 선택하거나 도시를 클릭하세요.<br>
<select class='formInput' name="destCityID" id="destCityID" size='1' style='color:white;background-color:black;'>
<?= \sammo\optionsForCities() ?><br>
</select> <input type=button id="commonSubmit" value="<?= $this->getName() ?>"><br>
<br>
<?php
return ob_get_clean();
}
}
+6 -30
View File
@@ -196,38 +196,14 @@ class che_초토화 extends Command\NationCommand{
return true;
}
public function getJSPlugins(): array
public function exportJSVars(): array
{
return [
'defaultSelectCityByMap'
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'cities' => \sammo\JSOptionsForCities(),
'distanceList' => new \stdClass(),
],
];
}
public function getForm(): string
{
$cities = Util::convertArrayToDict(DB::db()->query('SELECT * FROM city WHERE nation =%i', $this->generalObj->getNationID()), 'city');
$calc = function(CityInitialDetail $constCity)use($cities){
if(!key_exists($constCity->id, $cities)){
return null;
}
$amount = $this->calcReturnAmount($cities[$constCity->id]);
$amountText = number_format($amount);
return "금쌀 각 {$amountText} 회수";
};
ob_start();
?>
<?=\sammo\getMapHtml()?><br>
선택된 도시를 초토화 시킵니다.<br>
도시가 공백지가 되며, 도시의 인구, 내정 상태에 따라 상당량의 국고가 확보됩니다.<br>
국가의 수뇌들은 명성을 잃고, 모든 장수들은 배신 수치가 1 증가합니다.<br>
목록을 선택하거나 도시를 클릭하세요.<br>
<select class='formInput' name="destCityID" id="destCityID" size='1' style='color:white;background-color:black;'>
<?=\sammo\optionsForCities($calc)?><br>
</select> <input type=button id="commonSubmit" value="<?=$this->getName()?>"><br>
<br>
<?php
return ob_get_clean();
}
}
+15 -46
View File
@@ -177,53 +177,22 @@ class che_포상 extends Command\NationCommand
return true;
}
public function getForm(): string
public function exportJSVars(): array
{
//TODO: 암행부처럼 보여야...
$db = DB::db();
$destRawGenerals = $db->query('SELECT no,name,officer_level,npc,gold,rice FROM general WHERE nation = %i ORDER BY npc,binary(name)', $this->generalObj->getNationID());
$destGeneralList = [];
foreach ($destRawGenerals as $destGeneral) {
$nameColor = \sammo\getNameColor($destGeneral['npc']);
if ($nameColor) {
$nameColor = " style='color:{$nameColor}'";
}
$name = $destGeneral['name'];
if ($destGeneral['officer_level'] >= 5) {
$name = "*{$name}*";
}
$destGeneralList[] = [
'no' => $destGeneral['no'],
'color' => $nameColor,
'name' => $name,
'gold' => $destGeneral['gold'],
'rice' => $destGeneral['rice']
];
}
ob_start();
?>
국고로 장수에게 자금이나 군량을 지급합니다.<br>
<select class='formInput' name="destGeneralID" id="destGeneralID" size='1' style='color:white;background-color:black;'>
<?php foreach ($destGeneralList as $destGeneral) : ?>
<option value='<?= $destGeneral['no'] ?>' <?= $destGeneral['color'] ?>><?= $destGeneral['name'] ?>(금:<?= $destGeneral['gold'] ?>, 쌀:<?= $destGeneral['rice'] ?>)</option>
<?php endforeach; ?>
</select>
<select class='formInput' name="isGold" id="isGold" size='1' style='color:white;background-color:black;'>
<option value="true">금</option>
<option value="false">쌀</option>
</select>
</select>
<select class='formInput' name="amount" id="amount" size='1' style='color:white;background-color:black;'>
<?php foreach (GameConst::$resourceActionAmountGuide as $amount) : ?>
<option value='<?= $amount ?>'><?= $amount ?></option>
<?php endforeach; ?>
</select> <input type=button id="commonSubmit" value="<?= $this->getName() ?>"><br>
<br>
<?php
return ob_get_clean();
$nationID = $this->getNationID();
$troops = Util::convertArrayToDict($db->query('SELECT * FROM troop WHERE nation=%i', $nationID), 'troop_leader');
$destRawGenerals = $db->queryAllLists('SELECT no,name,officer_level,npc,gold,rice,leadership,strength,intel,city,crew,train,atmos,troop FROM general WHERE nation = %i ORDER BY npc,binary(name)', $nationID);
return [
'procRes' => [
'troops' => $troops,
'generals' => $destRawGenerals,
'generalsKey' => ['no', 'name', 'officerLevel', 'npc', 'gold', 'rice', 'leadership', 'strength', 'intel', 'cityID', 'crew', 'train', 'atmos', 'troopID'],
'cities' => \sammo\JSOptionsForCities(),
'minAmount' => 100,
'maxAmount' => GameConst::$maxResourceActionAmount,
'amountGuide' => GameConst::$resourceActionAmountGuide,
]
];
}
}
+79 -88
View File
@@ -1,16 +1,21 @@
<?php
namespace sammo\Command\Nation;
use \sammo\{
DB, Util, JosaUtil,
General, DummyGeneral,
DB,
Util,
JosaUtil,
General,
DummyGeneral,
ActionLogger,
GameConst,
LastTurn,
GameUnitConst,
Command,
KVStorage,
Message, MessageTarget
Message,
MessageTarget
};
use function \sammo\buildNationCommandClass;
@@ -20,48 +25,51 @@ use function \sammo\getNationStaticInfo;
use \sammo\Constraint\Constraint;
use \sammo\Constraint\ConstraintHelper;
class che_피장파장 extends Command\NationCommand{
class che_피장파장 extends Command\NationCommand
{
static protected $actionName = '피장파장';
static public $reqArg = true;
static public $delayCnt = 60;
protected function argTest():bool{
if($this->arg === null){
protected function argTest(): bool
{
if ($this->arg === null) {
return false;
}
//NOTE: 멸망 직전에 턴을 넣을 수 있으므로, 존재하지 않는 국가여도 argTest에서 바로 탈락시키지 않음
if(!key_exists('destNationID', $this->arg)){
if (!key_exists('destNationID', $this->arg)) {
return false;
}
if(!key_exists('commandType', $this->arg)){
if (!key_exists('commandType', $this->arg)) {
return false;
}
$destNationID = $this->arg['destNationID'];
$commandType = $this->arg['commandType'];
if(!is_int($destNationID)){
if (!is_int($destNationID)) {
return false;
}
if($destNationID < 1){
if ($destNationID < 1) {
return false;
}
if(!is_string($commandType)){
if (!is_string($commandType)) {
return false;
}
if(!in_array($commandType, GameConst::$availableChiefCommand['전략'])){
if (!in_array($commandType, GameConst::$availableChiefCommand['전략'])) {
return false;
}
$this->arg = [
'destNationID'=>$destNationID,
'commandType'=>$commandType
'destNationID' => $destNationID,
'commandType' => $commandType
];
return true;
}
protected function init(){
protected function init()
{
$general = $this->generalObj;
$env = $this->env;
@@ -69,7 +77,7 @@ class che_피장파장 extends Command\NationCommand{
$this->setCity();
$this->setNation();
$this->minConditionConstraints=[
$this->minConditionConstraints = [
ConstraintHelper::OccupiedCity(),
ConstraintHelper::BeChief(),
];
@@ -79,8 +87,8 @@ class che_피장파장 extends Command\NationCommand{
{
$this->setDestNation($this->arg['destNationID'], null);
if($this->getNationID() == 0){
$this->fullConditionConstraints=[
if ($this->getNationID() == 0) {
$this->fullConditionConstraints = [
ConstraintHelper::OccupiedCity()
];
return;
@@ -90,14 +98,14 @@ class che_피장파장 extends Command\NationCommand{
$currYearMonth = Util::joinYearMonth($this->env['year'], $this->env['month']);
$nextAvailableTurn = $cmd->getNextAvailableTurn();
if($currYearMonth < $nextAvailableTurn){
$this->fullConditionConstraints=[
if ($currYearMonth < $nextAvailableTurn) {
$this->fullConditionConstraints = [
ConstraintHelper::AlwaysFail('해당 전략을 아직 사용할 수 없습니다')
];
return;
}
$this->fullConditionConstraints=[
$this->fullConditionConstraints = [
ConstraintHelper::OccupiedCity(),
ConstraintHelper::BeChief(),
ConstraintHelper::ExistsDestNation(),
@@ -108,35 +116,41 @@ class che_피장파장 extends Command\NationCommand{
];
}
public function getCommandDetailTitle():string{
public function getCommandDetailTitle(): string
{
$name = $this->getName();
$reqTurn = $this->getPreReqTurn()+1;
$reqTurn = $this->getPreReqTurn() + 1;
return "{$name}/{$reqTurn}턴(대상 재사용 대기 {$this->getTargetPostReqTurn()})";
}
public function getCost():array{
public function getCost(): array
{
return [0, 0];
}
public function getPreReqTurn():int{
public function getPreReqTurn(): int
{
return 1;
}
public function getPostReqTurn():int{
public function getPostReqTurn(): int
{
return 0;
}
public function getTargetPostReqTurn():int{
public function getTargetPostReqTurn(): int
{
$genCount = Util::valueFit($this->nation['gennum'], GameConst::$initialNationGenLimit);
$nextTerm = Util::round(sqrt($genCount*2)*10);
$nextTerm = Util::round(sqrt($genCount * 2) * 10);
$nextTerm = $this->generalObj->onCalcStrategic($this->getName(), 'delay', $nextTerm);
$nextTerm = Util::valueFit($nextTerm, Util::round(static::$delayCnt * 1.2));
return $nextTerm;
}
public function getBrief():string{
public function getBrief(): string
{
$commandName = $this->getName();
$cmd = buildNationCommandClass($this->arg['commandType'], $this->generalObj, $this->env, new LastTurn());
$targetCommandName = $cmd->getName();
@@ -145,8 +159,9 @@ class che_피장파장 extends Command\NationCommand{
}
public function run():bool{
if(!$this->hasFullConditionMet()){
public function run(): bool
{
if (!$this->hasFullConditionMet()) {
throw new \RuntimeException('불가능한 커맨드를 강제로 실행 시도');
}
@@ -187,7 +202,7 @@ class che_피장파장 extends Command\NationCommand{
$broadcastMessage = "<Y>{$generalName}</>{$josaYi} <G><b>{$destNationName}</b></>에 <G><b>{$cmd->getName()}</b></> 전략의 <M>{$commandName}</>{$josaUl} 발동하였습니다.";
$nationGeneralList = $db->queryFirstColumn('SELECT no FROM general WHERE nation=%i AND no != %i', $nationID, $generalID);
foreach($nationGeneralList as $nationGeneralID){
foreach ($nationGeneralList as $nationGeneralID) {
$nationGeneralLogger = new ActionLogger($nationGeneralID, $nationID, $year, $month);
$nationGeneralLogger->pushGeneralActionLog($broadcastMessage, ActionLogger::PLAIN);
$nationGeneralLogger->flush();
@@ -198,7 +213,7 @@ class che_피장파장 extends Command\NationCommand{
$broadcastMessage = "아국에 <G><b>{$cmd->getName()}</b></> 전략의 <M>{$commandName}</>{$josaYiCommand} 발동되었습니다.";
$destNationGeneralList = $db->queryFirstColumn('SELECT no FROM general WHERE nation=%i AND no != %i', $destNationID, $generalID);
foreach($destNationGeneralList as $destNationGeneralID){
foreach ($destNationGeneralList as $destNationGeneralID) {
$destNationGeneralLogger = new ActionLogger($destNationGeneralID, $destNationID, $year, $month);
$destNationGeneralLogger->pushGeneralActionLog($broadcastMessage, ActionLogger::PLAIN);
$destNationGeneralLogger->flush();
@@ -222,79 +237,55 @@ class che_피장파장 extends Command\NationCommand{
return true;
}
public function getJSPlugins(): array
{
return [
'defaultSelectNationByMap'
];
}
public function getForm(): string
public function exportJSVars(): array
{
$generalObj = $this->generalObj;
$nationID = $generalObj->getNationID();
$nationList = [];
$testTurn = new LastTurn($this->getName(), null, $this->getPreReqTurn());
foreach(getAllNationStaticInfo() as $destNation){
if($destNation['nation'] == $nationID){
continue;
foreach (getAllNationStaticInfo() as $destNation) {
$testCommand = new static($generalObj, $this->env, $testTurn, ['destNationID' => $destNation['nation']]);
$nationTarget = [
'id' => $destNation['nation'],
'name' => $destNation['name'],
'color' => $destNation['color'],
'power' => $destNation['power'],
];
if (!$testCommand->hasFullConditionMet()) {
$nationTarget['notAvailable'] = true;
}
if ($destNation['id'] == $nationID) {
$nationTarget['notAvailable'] = true;
}
$testTurn->setArg(['destNationID'=>$destNation['nation']]);
$testCommand = new static($generalObj, $this->env, $testTurn, ['destNationID'=>$destNation['nation']]);
if($testCommand->hasFullConditionMet()){
$destNation['availableCommand'] = true;
}
else{
$destNation['availableCommand'] = false;
}
$nationList[] = $destNation;
$nationList[] = $nationTarget;
}
$availableCommandTypeList = [];
$currYearMonth = Util::joinYearMonth($this->env['year'], $this->env['month']);
foreach(GameConst::$availableChiefCommand['전략'] as $commandType){
foreach (GameConst::$availableChiefCommand['전략'] as $commandType) {
$cmd = buildNationCommandClass($commandType, $generalObj, $this->env, new LastTurn());
$cmdName = $cmd->getName();
$remainTurn = 0;
$nextAvailableTurn = $cmd->getNextAvailableTurn();
if($nextAvailableTurn !== null && $currYearMonth < $nextAvailableTurn){
if ($nextAvailableTurn !== null && $currYearMonth < $nextAvailableTurn) {
$remainTurn = $nextAvailableTurn - $currYearMonth;
}
$availableCommandTypeList[$commandType] = [$cmdName, $remainTurn];
$availableCommandTypeList[$commandType] = ['name' => $cmdName, 'remainTurn' => $remainTurn];
}
ob_start();
?>
<?=\sammo\getMapHtml()?><br>
선택된 국가에 피장파장을 발동합니다.<br>
지정한 전략을 상대국이 <?=static::$delayCnt?>턴 동안 사용할 수 없게됩니다.<br>
대신 아국은 지정한 전략을<?=$this->getTargetPostReqTurn()?>턴 동안 사용할 수 없습니다.<br>
선포, 전쟁중인 상대국에만 가능합니다.<br>
상대 국가를 목록에서 선택하세요.<br>
배경색은 현재 피장파장 불가능 국가는 <font color=red>붉은색</font>으로 표시됩니다.<br>
<select class='formInput' name="destNationID" id="destNationID" size='1' style='color:white;background-color:black;'>
<?php foreach($nationList as $nation): ?>
<option
value='<?=$nation['nation']?>'
style='color:<?=$nation['color']?>;<?=$nation['availableCommand']?'':'background-color:red;'?>'
>【<?=$nation['name']?> 】</option>
<?php endforeach; ?>
</select>에
<select class='formInput' name="commandType" id="commandType" size='1' style='color:white;background-color:black;'>
<?php foreach($availableCommandTypeList as $commandType=>[$cmdName, $cmdRemainTurn]):
/** @var \sammo\Command\NationCommand $cmdObj */
?>
<option
value='<?=$commandType?>'
style='color:white;<?=$cmdRemainTurn?'background-color:red;':''?>'
><?=$cmdName?><?=$cmdRemainTurn?"({$cmdRemainTurn}턴 뒤)":''?></option>
<?php endforeach; ?>
</select> 전략을
<input type=button id="commonSubmit" value="<?=$this->getName()?>">
<?php
return ob_get_clean();
return [
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'nationList' => $nationList,
'startYear' => $this->env['startyear'],
'delayCnt' => static::$delayCnt,
'postReqTurn' => $this->getTargetPostReqTurn(),
'availableCommandTypeList' => $availableCommandTypeList
],
];
}
}
}
+6 -19
View File
@@ -199,27 +199,14 @@ class che_허보 extends Command\NationCommand
return true;
}
public function getJSPlugins(): array
public function exportJSVars(): array
{
return [
'defaultSelectCityByMap'
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'cities' => \sammo\JSOptionsForCities(),
'distanceList' => new \stdClass(),
],
];
}
public function getForm(): string
{
ob_start();
?>
<?= \sammo\getMapHtml() ?><br>
선택된 도시에 허보를 발동합니다.<br>
전쟁중인 상대국 도시만 가능합니다.<br>
목록을 선택하거나 도시를 클릭하세요.<br>
<select class='formInput' name="destCityID" id="destCityID" size='1' style='color:white;background-color:black;'>
<?= \sammo\optionsForCities() ?><br>
</select> <input type=button id="commonSubmit" value="<?= $this->getName() ?>"><br>
<br>
<?php
return ob_get_clean();
}
}
+1 -1
View File
@@ -50,7 +50,7 @@ class GameUnitConstBase{
0, null, null, 0,
[self::T_ARCHER=>1.2, self::T_CAVALRY=>0.8, self::T_SIEGE=>1.2],
[self::T_ARCHER=>0.8, self::T_CAVALRY=>1.2, self::T_SIEGE=>0.8],
['표준적인 보병입니다.','보병은 방어특화이며, 상대가 회피하기 어렵습니다.'],
['표준적인 보병입니다.','보병은 방어특화이며,','상대가 회피하기 어렵습니다.'],
null, ['che_방어력증가5p']
],
[
+23 -23
View File
@@ -8,8 +8,8 @@ class GameUnitConst extends GameUnitConstBase
protected static $_buildData = [
[
1000, self::T_CASTLE, '성벽',
100, 100, 7, 0, 0, 99, 9,
999999, null, null, 999999,
100, 100, 7, 0, 0, 99, 9,
999999, null, null, 999999,
[],//성벽은 공격할 수 없다.
[self::T_FOOTMAN=>1.2],
['성벽입니다.','생성할 수 없습니다.'],
@@ -18,18 +18,18 @@ class GameUnitConst extends GameUnitConstBase
[
1100, self::T_FOOTMAN, '보병',
100, 150, 7, 10, 0, 9, 9,
0, null, null, 0,
100, 150, 7, 10, 0, 9, 9,
0, null, null, 0,
[self::T_ARCHER=>1.2, self::T_CAVALRY=>0.8, self::T_SIEGE=>1.2],
[self::T_ARCHER=>0.8, self::T_CAVALRY=>1.2, self::T_SIEGE=>0.8],
['표준적인 보병입니다.','보병은 방어특화이며, 상대가 회피하기 어렵습니다.'],
['표준적인 보병입니다.','보병은 방어특화이며,','상대가 회피하기 어렵습니다.'],
null, null
],
[
1200, self::T_ARCHER, '궁병',
100, 100, 7, 20, 0, 10, 10,
0, null, null, 0,
1200, self::T_ARCHER, '궁병',
100, 100, 7, 20, 0, 10, 10,
0, null, null, 0,
[self::T_CAVALRY=>1.2, self::T_FOOTMAN=>0.8, self::T_SIEGE=>1.2],
[self::T_CAVALRY=>0.8, self::T_FOOTMAN=>1.2, self::T_SIEGE=>0.8],
['표준적인 궁병입니다.','궁병은 회피특화입니다.'],
@@ -37,9 +37,9 @@ class GameUnitConst extends GameUnitConstBase
],
[
1300, self::T_CAVALRY, '기병',
150, 100, 7, 5, 0, 11, 11,
0, null, null, 0,
1300, self::T_CAVALRY, '기병',
150, 100, 7, 5, 0, 11, 11,
0, null, null, 0,
[self::T_FOOTMAN=>1.2, self::T_ARCHER=>0.8, self::T_SIEGE=>1.2],
[self::T_FOOTMAN=>0.8, self::T_ARCHER=>1.2, self::T_SIEGE=>0.8],
['표준적인 기병입니다.','기병은 공격특화입니다.'],
@@ -47,18 +47,18 @@ class GameUnitConst extends GameUnitConstBase
],
[
1400, self::T_WIZARD, '귀병',
80, 80, 7, 5, 0.5, 9, 9,
0, null, null, 0,
1400, self::T_WIZARD, '귀병',
80, 80, 7, 5, 0.5, 9, 9,
0, null, null, 0,
[self::T_SIEGE=>1.2],
[self::T_SIEGE=>0.8],
['계략을 사용하는 병종입니다.'],
null, null
],
[
1405, self::T_WIZARD, '남귀병',
60, 60, 7, 10, 0.8, 8, 8,
1000, null, null, 0,
1405, self::T_WIZARD, '남귀병',
60, 60, 7, 10, 0.8, 8, 8,
1000, null, null, 0,
[self::T_SIEGE=>1.2],
[self::T_SIEGE=>0.8],
['전투를 포기하고 계략에 몰두합니다.'],
@@ -66,18 +66,18 @@ class GameUnitConst extends GameUnitConstBase
],
[
1500, self::T_SIEGE, '정란',
100, 100, 6, 0, 0, 15, 5,
0, null, null, 3,
1500, self::T_SIEGE, '정란',
100, 100, 6, 0, 0, 15, 5,
0, null, null, 3,
[self::T_FOOTMAN=>0.8, self::T_ARCHER=>0.8, self::T_CAVALRY=>0.8, self::T_WIZARD=>0.8, self::T_CASTLE=>1.8],
[self::T_FOOTMAN=>1.2, self::T_ARCHER=>1.2, self::T_CAVALRY=>1.2, self::T_WIZARD=>1.2],
['높은 구조물 위에서 공격합니다.'],
['che_성벽부상무효'], null
],
[
1501, self::T_SIEGE, '충차',
150, 100, 6, 0, 0, 20, 5,
1000, null, null, 3,
1501, self::T_SIEGE, '충차',
150, 100, 6, 0, 0, 20, 5,
1000, null, null, 3,
[self::T_FOOTMAN=>0.8, self::T_ARCHER=>0.8, self::T_CAVALRY=>0.8, self::T_WIZARD=>0.8, self::T_CASTLE=>2.4],
[self::T_FOOTMAN=>1.2, self::T_ARCHER=>1.2, self::T_CAVALRY=>1.2, self::T_WIZARD=>1.2],
['엄청난 위력으로 성벽을 부수어버립니다.'],
+29 -29
View File
@@ -8,8 +8,8 @@ class GameUnitConst extends GameUnitConstBase
protected static $_buildData = [
[
1000, self::T_CASTLE, '성벽',
100, 100, 7, 0, 0, 99, 9,
999999, null, null, 999999,
100, 100, 7, 0, 0, 99, 9,
999999, null, null, 999999,
[],//성벽은 공격할 수 없다.
[self::T_FOOTMAN=>1.2],
['성벽입니다.','생성할 수 없습니다.'],
@@ -17,18 +17,18 @@ class GameUnitConst extends GameUnitConstBase
],
[
1100, self::T_FOOTMAN, '보병',
100, 150, 7, 10, 0, 18, 18,
0, null, null, 0,
100, 150, 7, 10, 0, 18, 18,
0, null, null, 0,
[self::T_ARCHER=>1.2, self::T_CAVALRY=>0.8],
[self::T_ARCHER=>0.8, self::T_CAVALRY=>1.2],
['표준적인 보병입니다.','보병은 방어특화이며, 상대가 회피하기 어렵습니다.'],
['표준적인 보병입니다.','보병은 방어특화이며,','상대가 회피하기 어렵습니다.'],
null, null
],
[
1200, self::T_ARCHER, '궁병',
100, 100, 7, 20, 0, 20, 20,
0, null, null, 0,
1200, self::T_ARCHER, '궁병',
100, 100, 7, 20, 0, 20, 20,
0, null, null, 0,
[self::T_CAVALRY=>1.2, self::T_FOOTMAN=>0.8],
[self::T_CAVALRY=>0.8, self::T_FOOTMAN=>1.2],
['표준적인 궁병입니다.','궁병은 회피특화입니다.'],
@@ -36,9 +36,9 @@ class GameUnitConst extends GameUnitConstBase
],
[
1300, self::T_CAVALRY, '기병',
150, 100, 7, 5, 0, 22, 22,
0, null, null, 0,
1300, self::T_CAVALRY, '기병',
150, 100, 7, 5, 0, 22, 22,
0, null, null, 0,
[self::T_FOOTMAN=>1.2, self::T_ARCHER=>0.8],
[self::T_FOOTMAN=>0.8, self::T_ARCHER=>1.2],
['표준적인 기병입니다.','기병은 공격특화입니다.'],
@@ -46,18 +46,18 @@ class GameUnitConst extends GameUnitConstBase
],
[
1400, self::T_WIZARD, '귀병',
80, 80, 7, 5, 0.5, 18, 18,
0, null, null, 0,
1400, self::T_WIZARD, '귀병',
80, 80, 7, 5, 0.5, 18, 18,
0, null, null, 0,
[],
[],
['계략을 사용하는 병종입니다.'],
null, null
],
[
1405, self::T_WIZARD, '남귀병',
60, 60, 7, 10, 0.8, 16, 16,
1000, null, null, 0,
1405, self::T_WIZARD, '남귀병',
60, 60, 7, 10, 0.8, 16, 16,
1000, null, null, 0,
[],
[],
['전투를 포기하고 계략에 몰두합니다.'],
@@ -65,36 +65,36 @@ class GameUnitConst extends GameUnitConstBase
],
[
1500, self::T_SIEGE, '정란',
100, 100, 6, 0, 0, 7, 3,
0, null, null, 0,
1500, self::T_SIEGE, '정란',
100, 100, 6, 0, 0, 7, 3,
0, null, null, 0,
[self::T_CASTLE=>1.8],
[],
['높은 구조물 위에서 공격합니다.'],
['che_성벽부상무효'], null
],
[
1501, self::T_SIEGE, '충차',
150, 100, 6, 0, 0, 10, 3,
1000, null, null, 3,
1501, self::T_SIEGE, '충차',
150, 100, 6, 0, 0, 10, 3,
1000, null, null, 3,
[self::T_CASTLE=>2.4],
[],
['엄청난 위력으로 성벽을 부수어버립니다.'],
['che_성벽부상무효'], null
],
[
1502, self::T_SIEGE, '벽력거',
200, 100, 6, 0, 0, 20, 4,
3000, ['업'], null, 0,
1502, self::T_SIEGE, '벽력거',
200, 100, 6, 0, 0, 20, 4,
3000, ['업'], null, 0,
[self::T_CASTLE=>1.8],
[],
['상대에게 돌덩이를 날립니다.'],
['che_성벽부상무효'], null
],
[
1503, self::T_SIEGE, '목우',
50, 200, 5, 0, 0, 15, 3,
3000, ['성도'], null, 0,
1503, self::T_SIEGE, '목우',
50, 200, 5, 0, 0, 15, 3,
3000, ['성도'], null, 0,
[],
[],
['상대를 저지하는 특수병기입니다.'],
+1
View File
@@ -7,6 +7,7 @@ $input-color: #303030;
@import url("https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css");
@import "./break_500px.scss";
@import "./variables.scss";
@import "./bootswatch_custom_variables.scss";
@import "bootstrap/scss/bootstrap.scss";
+13 -1
View File
@@ -12,4 +12,16 @@ $grid-breakpoints: (
lg: 992px,
xl: 1200px,
xxl: 1400px
);
);
@mixin media-1000px{
@media (min-width: map-get($grid-breakpoints, 'md')) {
@content;
}
}
@mixin media-500px{
@media (max-width: calc(map-get($grid-breakpoints, 'md') - .02px)) {
@content;
}
}
+2
View File
@@ -17,6 +17,8 @@ table.tb_layout {
html,
body {
font-size: 14px;
background-position: center;
background-repeat: repeat-y;
}
.tb_layout td,
+12 -24
View File
@@ -1,6 +1,6 @@
@import "./common/break_500px.scss";
@import "./common/bootstrap5.scss";
@import "./editor_component.scss";
@import "./util.scss";
body {
color: white;
background-color: black;
@@ -10,14 +10,14 @@ body {
background-repeat: repeat-y;
}
@include media-breakpoint-up(md) {
@include media-1000px {
#container {
width: 1000px;
margin: 0 auto;
}
}
@include media-breakpoint-down(md) {
@include media-500px {
body {
overflow-x: hidden;
}
@@ -52,6 +52,8 @@ body {
}
box-shadow: 0 -1px 0 $dark;
border-left: none !important;
border-right: none !important;
.toolbarButton2 {
text-decoration: underline;
@@ -118,7 +120,7 @@ body {
text-overflow: ellipsis;
}
@include media-breakpoint-up(md) {
@include media-1000px {
#map_view {
grid-template-columns: 500px 200px 300px;
grid-template-rows: 520px 130px;
@@ -218,7 +220,7 @@ body {
overflow: hidden;
}
@include media-breakpoint-down(md) {
@include media-500px {
.buttonPlate {
display: grid;
grid-template-columns: repeat(5, 1fr);
@@ -394,7 +396,7 @@ body {
}
@include media-breakpoint-up(md) {
@include media-1000px {
#message_board section {
overflow-y: auto;
overflow-x: hidden;
@@ -410,7 +412,7 @@ body {
}
}
@include media-breakpoint-down(md) {
@include media-500px {
#message_board .message_input_form {
position: -webkit-sticky;
@@ -535,7 +537,7 @@ body {
}
@include media-breakpoint-down(md) {
@include media-500px {
#mailbox_list-col {
order: 1;
}
@@ -586,13 +588,13 @@ body {
}
@include media-breakpoint-up(md) {
@include media-1000px {
#float-tabs {
display: none;
}
}
@include media-breakpoint-down(md) {
@include media-500px {
#float-tabs {
position: fixed;
right: -1rem;
@@ -627,17 +629,3 @@ body {
}
}
}
.s-border-t {
border-top: gray solid 1px;
}
.s-border-b {
border-bottom: gray solid 1px;
}
.s-border-tb {
border-top: gray solid 1px;
border-bottom: gray solid 1px;
}
+2 -7
View File
@@ -1,9 +1,7 @@
@import "./common/break_500px.scss";
@import "./common/bootstrap5.scss";
@import "./common_legacy.scss";
@include media-breakpoint-up(md) {
@include media-1000px{
#container {
width: 1000px;
margin: 0 auto;
@@ -17,10 +15,7 @@
}
}
@include media-breakpoint-down(md) {
@include media-500px{
body {
overflow-x: hidden;
}
+53
View File
@@ -0,0 +1,53 @@
@import "./common/bootstrap5.scss";
@import "./editor_component.scss";
@import "./common_legacy.scss";
@import "./util.scss";
#amount {
width: 100px;
}
#colorType {
width: 150px;
}
.no-padding .select2-results__option {
padding: 0;
}
@include media-1000px{
#container {
width: 1000px;
margin: 0 auto;
}
}
@include media-500px{
body {
overflow-x: hidden;
}
#container {
width: 500px;
margin: 0 auto;
.world_map {
width: 500px;
height: calc((500px + 20px) * 500 / 700);
}
.map_title {
transform-origin: 0px 0px;
transform: scale(calc(500 / 700));
margin-bottom: calc(-20px * 200 / 700);
}
.map_body {
transform-origin: 0px 0px;
transform: scale(calc(500 / 700));
}
}
}
+14
View File
@@ -0,0 +1,14 @@
.s-border-t {
border-top: gray solid 1px;
}
.s-border-b {
border-bottom: gray solid 1px;
}
.s-border-tb {
border-top: gray solid 1px;
border-bottom: gray solid 1px;
}
-2
View File
@@ -39,8 +39,6 @@ if ($permission < 1) {
<meta name="viewport" content="width=1024" />
<?= WebUtil::printCSS('../e_lib/select2/select2.min.css') ?>
<?= WebUtil::printCSS('../e_lib/select2/select2-bootstrap4.css') ?>
<!--<?= WebUtil::printCSS('../e_lib/tui.editor/tui-editor.min.css') ?>-->
<!--<?= WebUtil::printCSS('../e_lib/tui.editor/tui-editor-contents.min.css') ?>-->
<?= WebUtil::printCSS('../d_shared/common.css') ?>
<?= WebUtil::printCSS('../css/config.css') ?>
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/static/pretendard.css" />
-326
View File
@@ -1,326 +0,0 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// Type definitions for Summernote 0.8
// Project: https://github.com/summernote/summernote#readme
// Definitions by: Wouter Staelens <https://github.com/wstaelens>
// Denny Harijanto <https://github.com/nusantara-cloud>
// Corbin Crutchley <https://github.com/crutchcorn>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.7
/// <reference types="jquery"/>
declare global {
namespace Summernote {
type fontSizeUnitOptions = 'px' | 'pt';
interface Options {
airMode?: boolean | undefined;
tabDisable?: boolean | undefined;
codeviewFilter?: boolean | undefined;
codeviewFilterRegex?: string | undefined;
codeviewIframeWhitelistSrc?: string[] | undefined;
codeviewIframeFilter?: boolean | undefined;
callbacks?: SummernoteCallbacks | SummernoteUndocumentedCallbacks | undefined; // todo
codemirror?: CodemirrorOptions | undefined;
colors?: colorsDef | undefined;
dialogsInBody?: boolean | undefined;
dialogsFade?: boolean | undefined;
direction?: string | undefined;
disableDragAndDrop?: boolean | undefined;
focus?: boolean | undefined;
fontNames?: string[] | undefined;
fontNamesIgnoreCheck?: string[] | undefined;
fontSizes?: string[] | undefined;
fontSizeUnits?: fontSizeUnitOptions[] | undefined;
height?: number | undefined;
hint?: HintOptions | undefined;
icons?: IconsOptions | undefined;
insertTableMaxSize?: InsertTableMaxSizeOptions | undefined;
keyMap?: KeyMapOptions | undefined;
lang?: string | undefined;
lineHeights?: string[] | undefined;
minHeight?: number | undefined;
maxHeight?: number | undefined;
maximumImageFileSize?: any;
modules?: ModuleOptions | undefined;
popover?: PopoverOptions | undefined;
placeholder?: string | undefined;
shortcuts?: boolean | undefined;
styleTags?: styleTagsOptions[] | undefined;
styleWithSpan?: boolean | undefined;
tabsize?: number | undefined;
tableClassName?: string | undefined;
textareaAutoSync?: boolean | undefined;
toolbar?: toolbarDef | undefined;
width?: number | undefined;
}
type toolbarStyleGroupOptions = 'style' | 'bold' | 'italic' | 'underline' | 'clear';
type toolbarFontGroupOptions = 'fontname' | 'fontsize' | 'fontsizeunit' | 'color' | 'forecolor' | 'backcolor' | 'bold' | 'italic' | 'underline' | 'strikethrough' | 'superscript' |
'subscript' | 'clear';
type toolbarFontsizeGroupOptions = 'fontsize' | 'fontname' | 'color';
type toolbarColorGroupOptions = 'color';
type toolbarParaGroupOptions = 'ul' | 'ol' | 'paragraph' | 'style' | 'height';
type toolbarHeightGroupOptions = 'height';
type toolbarTableGroupOptions = 'table';
type toolbarInsertGroupOptions = 'link' | 'picture' | 'hr' | 'table' | 'video';
type toolbarViewGroupOptions = 'fullscreen' | 'codeview' | 'help';
type toolbarHelpGroupOptions = 'help';
type miscGroupOptions = 'fullscreen' | 'codeview' | 'undo' | 'redo' | 'help';
// type toolbarDef = [string, string[]][]
type toolbarDef = Array<
['style', toolbarStyleGroupOptions[]]
| ['font', toolbarFontGroupOptions[]]
| ['fontname', toolbarFontNameOptions[]]
| ['fontsize', toolbarFontsizeGroupOptions[]]
| ['color', toolbarColorGroupOptions[]]
| ['para', toolbarParaGroupOptions[]]
| ['height', toolbarHeightGroupOptions[]]
| ['table', toolbarTableGroupOptions[]]
| ['insert', toolbarInsertGroupOptions[]]
| ['view', toolbarViewGroupOptions[]]
| ['help', toolbarHelpGroupOptions[]]
| ['misc', miscGroupOptions[]]
>;
type colorsDef = Array<[string[]]>;
type styleTagsOptions = 'p' | 'blockquote' | 'pre' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
interface InsertTableMaxSizeOptions {
col: number;
row: number;
}
interface IconsOptions {
options?: any; // todo
}
interface KeyMapOptions {
pc?: KeyMapPcOptions | undefined;
mac?: KeyMapMacOptions | undefined;
}
interface KeyMapPcOptions {
options?: any; // todo
}
interface KeyMapMacOptions {
options?: any; // todo
}
type htmlElement = string;
interface HintOptions {
words?: string[] | undefined;
mentions?: string[] | undefined;
match: RegExp;
search: (keyword: string, callback: (plausibleItems: string[]) => void) => void;
template?: ((item: string) => htmlElement) | undefined;
content?: ((item: string) => htmlElement | JQuery.Node) | undefined;
}
interface CodemirrorOptions {
mode?: string | undefined;
htmlNode?: boolean | undefined;
lineNumbers?: boolean | undefined;
theme?: string | undefined;
}
type popoverImageOptionsImagesize = 'imageSize100' | 'imageSize50' | 'imageSize25';
type popoverImageOptionsFloat = 'floatLeft' | 'floatRight' | 'floatNone';
type popoverImageOptionsRemove = 'removeMedia';
type popoverImageDef = [
['imagesize', popoverImageOptionsImagesize[]],
['float', popoverImageOptionsFloat[]],
['remove', popoverImageOptionsRemove[]],
['custom', string[]]?,//NOTE: 이 부분이 바뀜(Hide_D)
];
type popoverLinkLinkOptions = 'linkDialogShow' | 'unlink';
type popoverLinkDef = [
['link', popoverLinkLinkOptions[]]
];
type popoverAirOptionsColor = 'color';
type popoverAirOptionsFont = 'bold' | 'underline' | 'clear';
type popoverAirOptionsPara = 'ul' | 'paragraph';
type popoverAirOptionsTable = 'table';
type popoverAirOptionsInsert = 'link' | 'picture';
type popoverAirDef = [
['color', popoverAirOptionsColor],
['font', popoverAirOptionsFont],
['para', popoverAirOptionsPara],
['table', popoverAirOptionsTable],
['insert', popoverAirOptionsInsert]
];
interface PopoverOptions {
image?: popoverImageDef | undefined;
link?: popoverLinkDef | undefined;
air?: popoverAirDef | undefined;
}
interface ModuleOptions {
options?: any; // todo
}
interface CreateLinkOptions {
text: string;
url: string;
newWindow: boolean;
}
type EditImageCallback = ($image: JQuery.Node) => void;
type toolbarFontNameOptions = string;
interface SummernoteCallbacks {
onBeforeCommand?: (contents: string) => void;
onChange?: (contents: string, $editable: JQuery) => void;
onChangeCodeview?: (code: string, $editor: JQuery) => void;
onDialogShown?: () => void;
onEnter?: (ev: Event) => void;
onFocus?: (ev: Event) => void;
onBlur?: (ev: Event) => void;
onBlurCodeview?: (code: string, ev: Event) => void;
onImageLinkInsert?: (url: string) => void;
onImageUpload?: (files: Blob[]) => void;
onImageUploadError?: (err: any) => void;
onInit?: () => void;
onKeyup?: (ev: KeyboardEvent) => void;
onKeydown?: (ev: KeyboardEvent) => void;
onMouseDown?: (ev: MouseEvent) => void;
onMouseUp?: (ev: MouseEvent) => void;
onPaste?: (e: Event) => void;
onScroll?: (e: Event) => void;
}
type SummernoteUndocumentedCallbacks = {
[key in Exclude<keyof SummernoteCallbacks, string>]: (...args: any[]) => void;
};
interface Plugin{
showLinkDialog(...args:any[]);
show: () => void;
bindEnterKey: ($input: JQuery<HTMLElement>, $btn: JQuery<HTMLElement>) => void;
destroy: () => void;
$dialog: JQuery<HTMLElement>;
initialize: () => void;
}
interface Context {
//src/js/base/Context.js
triggerEvent(namespace: string, ...args:any[]);
options: any;
invoke(arg0: string);
memo(arg0: string, arg1: () => any);
layoutInfo: {
note: JQuery<HTMLElement>,
editor: JQuery<HTMLElement>,
editable: JQuery<HTMLElement>,
toolbar: JQuery<HTMLElement>,
}
}
interface UI{
showDialog($dialog: JQuery<HTMLElement>);
onDialogHidden($dialog: JQuery<HTMLElement>, arg1: () => void);
onDialogShown($dialog: JQuery<HTMLElement>, arg1: () => void);
hideDialog($dialog: JQuery<HTMLElement>);
dialog(arg0: { title: any; body: string; footer: string; });
button: (...args:any[])=>any,
icon: (...args:any[])=>any,
}
}
interface JQueryStatic{
summernote: {
lang: Record<string, Record<string, any>>,
options: Record<string, any>,
plugins: Record<string, any>,
ui: Summernote.UI,
};
}
interface JQuery {
summernote(): JQuery;
summernote(command: string): JQuery;
summernote(options?: Summernote.Options): JQuery;
summernote(command: string, markupString: string): JQuery;
summernote(command: string, value: number): JQuery;
summernote(command: string, node: JQuery.Node): JQuery;
summernote(command: string, url: string, filename?: (string | Summernote.EditImageCallback)): JQuery;
summernote(command: 'destroy'): JQuery;
summernote(command: 'code'): string;
summernote(command: 'code', markupStr: string): undefined;
summernote(command: 'editor.pasteHTML' | 'pasteHTML', markup: string): JQuery;
// Basic API
summernote(command: 'editor.createRange' | 'createRange'): JQuery;
summernote(command: 'editor.saveRange' | 'saveRange'): JQuery;
summernote(command: 'editor.restoreRange' | 'restoreRange'): JQuery;
summernote(command: 'editor.undo' | 'undo'): JQuery;
summernote(command: 'editor.redo' | 'redo'): JQuery;
summernote(command: 'editor.focus' | 'focus'): JQuery;
summernote(command: 'editor.isEmpty' | 'isEmpty'): boolean;
summernote(command: 'reset'): JQuery;
summernote(command: 'disable'): JQuery;
summernote(command: 'enable'): JQuery;
// Font style API
summernote(fontStyle: 'editor.bold' | 'bold'): JQuery;
summernote(fontStyle: 'editor.italic' | 'italic'): JQuery;
summernote(fontStyle: 'editor.underline' | 'underline'): JQuery;
summernote(fontStyle: 'editor.strikethrough' | 'strikethrough'): JQuery;
summernote(command: 'editor.superscript' | 'superscript'): JQuery;
summernote(command: 'editor.subscript' | 'subscript'): JQuery;
summernote(command: 'editor.removeFormat' | 'removeFormat'): JQuery;
summernote(command: 'backColor', color: string): JQuery;
summernote(command: 'foreColor', color: string): JQuery;
summernote(command: 'fontName', fontName: string): JQuery;
summernote(command: 'editor.fontSize' | 'fontSize', fontSize: number): JQuery;
// Paragraph API
summernote(command: 'editor.justifyLeft' | 'justifyLeft'): JQuery;
summernote(command: 'editor.justifyRight' | 'justifyRight'): JQuery;
summernote(command: 'editor.justifyCenter' | 'justifyCenter'): JQuery;
summernote(command: 'editor.justifyFull' | 'justifyFull'): JQuery;
summernote(command: 'insertParagraph'): JQuery;
summernote(command: 'editor.insertOrderedList' | 'insertOrderedList'): JQuery;
summernote(command: 'editor.insertUnorderedList' | 'insertUnorderedList'): JQuery;
summernote(command: 'editor.indent' | 'indent'): JQuery;
summernote(command: 'editor.outdent' | 'outdent'): JQuery;
summernote(command: 'formatPara'): JQuery;
summernote(command: 'formatH1'): JQuery;
summernote(command: 'formatH2'): JQuery;
summernote(command: 'formatH3'): JQuery;
summernote(command: 'formatH4'): JQuery;
summernote(command: 'formatH5'): JQuery;
summernote(command: 'formatH6'): JQuery;
// Insertion API
summernote(command: 'editor.insertImage' | 'insertImage', url: string, filename?: (string | Summernote.EditImageCallback)): JQuery;
summernote(command: 'editor.insertNode' | 'insertNode', node: JQuery.Node): JQuery;
summernote(command: 'editor.insertText' | 'insertText', text: string): JQuery;
summernote(command: 'createLink', options: Summernote.CreateLinkOptions): JQuery;
// TODO: Callbacks
// TODO: Editor
// editor.insertImagesOrCallback ??
// editor.afterCommand ??
// editor.resizeTo ??
// editor.resize ??
// editor.saveTarget ??
// editor.isActivated ??
// editor.formatBlock ??
// editor.color ??
// editor.lineHeight ??
// editor.insertTable ??
// editor.insertHorizontalRule ??
// editor.floatMe ??
// editor.removeMedia ??
// editor.currentStyle ??
// editor.getLinkInfo ??
// editor.getSelectedText ??
}
}
export { };
+16 -22
View File
@@ -133,9 +133,9 @@
<div class="row gx-0">
<div class="col-2 d-grid">
<b-button
:pressed="searchMode"
@click="searchCommand()"
:variant="searchMode ? 'info' : 'primary'"
:pressed="searchModeOn"
@click="toggleSearchCommand()"
:variant="searchModeOn ? 'info' : 'primary'"
v-b-tooltip.hover
title="검색 기능을 활성화합니다."
><i class="bi bi-search"></i
@@ -160,7 +160,7 @@
deselectGroupLabel=""
placeholder="턴 선택"
:maxHeight="400"
:searchable="searchMode"
:searchable="searchModeOn"
>
<template v-slot:noResult>검색 결과가 없습니다.</template>
<template v-slot:option="props"
@@ -209,8 +209,7 @@ import { mb_strwidth } from "@util/mb_strwidth";
import { parseTime } from "@util/parseTime";
import { parseYearMonth } from "@util/parseYearMonth";
import { sammoAPI } from "@util/sammoAPI";
import { filter초성withAlphabet } from "@util/filter초성withAlphabet";
import { convertSearch초성 } from "./util/convertSearch초성";
type commandItem = {
value: string;
title: string;
@@ -228,7 +227,6 @@ declare const commandList: {
values: commandItem[];
}[];
declare const serverNow: string;
declare const serverID: string;
type TurnObj = {
action: string;
brief: string;
@@ -284,7 +282,7 @@ function isDropdownChildren(e?: Event): boolean {
return false;
}
const searchModeKey = `sammo_${serverID}_searchMode`;
const searchModeKey = `sammo_searchModeOn`;
export default defineComponent({
name: "PartialReservedCommand",
@@ -449,7 +447,7 @@ export default defineComponent({
if (listReqArgCommand.has(commandName)) {
document.location.href = stringifyUrl({
url: "b_processing.php",
url: "v_processing.php",
query: {
command: commandName,
turnList: turnList.join("_"),
@@ -478,10 +476,10 @@ export default defineComponent({
}
await this.reloadCommandList();
},
searchCommand() {
const searchMode = !this.searchMode;
this.searchMode = searchMode;
localStorage.setItem(searchModeKey, searchMode ? "1" : "0");
toggleSearchCommand() {
const searchModeOn = !this.searchModeOn;
this.searchModeOn = searchModeOn;
localStorage.setItem(searchModeKey, searchModeOn ? "1" : "0");
},
},
data() {
@@ -499,14 +497,11 @@ export default defineComponent({
if (command.searchText) {
continue;
}
const [filteredTextH, filteredTextA] = filter초성withAlphabet(
command.simpleName.replace(/\s+/g, "")
);
command.searchText = `${command.simpleName} ${filteredTextH} ${filteredTextA}`;
command.searchText = convertSearch초성(command.simpleName).join('|');
}
}
const searchMode = (localStorage.getItem(searchModeKey) ?? "1") != "0";
const searchModeOn = (localStorage.getItem(searchModeKey) ?? "0") != "0";
const emptyTurn: TurnObjWithTime[] = Array.from<TurnObjWithTime>({
length: maxTurn,
@@ -535,7 +530,7 @@ export default defineComponent({
selectedCommand,
reservedCommandList: emptyTurn,
autorun_limit: null as null | number,
searchMode,
searchModeOn,
};
},
mounted() {
@@ -547,7 +542,6 @@ export default defineComponent({
@import "@scss/common/break_500px.scss";
@import "@scss/common/variables.scss";
@import "@scss/common/bootswatch_custom_variables.scss";
@import "bootstrap/scss/bootstrap-utilities.scss";
.commandPad {
background-color: $gray-900;
@@ -560,7 +554,7 @@ export default defineComponent({
//30, 70, 37.65, 160
}
@include media-breakpoint-up(md) {
@include media-1000px{
.commandPad {
margin-left: 10px;
@@ -582,7 +576,7 @@ export default defineComponent({
}
}
@include media-breakpoint-down(md) {
@include media-500px {
.dropdown-item {
padding: 8px;
}
+2 -1
View File
@@ -3,7 +3,8 @@ import 'bootstrap';
import download from 'downloadjs';
import { unwrap } from "@util/unwrap";
import { isInteger } from 'lodash';
import { combineArray, errUnknown, getNpcColor } from '@/common_legacy';
import { errUnknown, getNpcColor } from '@/common_legacy';
import { combineArray } from "@util/combineArray";
import { isBrightColor } from "@util/isBrightColor";
import { numberWithCommas } from "@util/numberWithCommas";
import { unwrap_any } from '@util/unwrap_any';
+2 -3
View File
@@ -7,7 +7,6 @@
"install": "install.ts",
"battle_simulator": "battle_simulator.ts",
"recent_map": "recent_map.ts",
"processing": "processing.ts",
"select_npc": "select_npc.ts",
"betting": "betting.ts",
"bossInfo": "bossInfo.ts",
@@ -27,7 +26,7 @@
"v_NPCControl": "v_NPCControl.ts",
"v_join": "v_join.ts",
"v_main": "v_main.ts",
"v_dipcenter": "v_dipcenter.ts"
"v_dipcenter": "v_dipcenter.ts",
"v_processing": "v_processing.ts"
}
}
+1 -1
View File
@@ -237,7 +237,7 @@ $(function ($) {
const $command = $('#chiefCommandList option:selected');
if ($command.data('reqarg')) {
document.location.href = stringifyUrl({
url: 'b_processing.php',
url: 'v_processing.php',
query: {
command: unwrap_any<string>($command.val()),
turnList: turnList.join('_'),
-14
View File
@@ -1,14 +0,0 @@
export function colorSelect(): void{
function changeNationColorPlate(){
const $this = $('#colorType');
const $option = $this.find('option:selected');
$this.css({
'background-color':$option.css('background-color'),
'color':$option.css('color')
});
}
$('#colorType').on('change', function(){
changeNationColorPlate();
});
changeNationColorPlate();
}
-10
View File
@@ -69,16 +69,6 @@ export function combineObject<K extends string, V>(item: V[], columnList: K[]):
return newItem;
}
export function combineArray<K extends string, V>(array: V[][], columnList: K[]): Record<K, V>[] {
const result: Record<K, V>[] = [];
for (const key of array.keys()) {
const item = array[key];
result[key] = combineObject(item, columnList);
}
return result;
}
export function errUnknown(): void {
alert('작업을 실패했습니다.');
}
+6 -11
View File
@@ -1,15 +1,10 @@
<template>
<table style="width: 1000px; margin: auto" class="bg0">
<tr>
<td style="text-align: left">
{{title}}<br /><button
type="button"
class="btn btn-primary"
@click="back"
>돌아가기</button><br />
</td>
</tr>
</table>
<div class="bg0">
<button type="button" class="btn btn-sammo-base2 back_btn" @click="back">
돌아가기
</button>
<div></div>
</div>
</template>
<script lang="ts">
+134
View File
@@ -0,0 +1,134 @@
<template>
<div
:id="uuid"
:class="['world_map', `map_theme_${mapTheme}`, 'draw_required']"
>
<div
class="map_title obj_tooltip"
data-bs-toggle="tooltip"
data-bs-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>
<div class="map_bglayer2"></div>
<div class="map_bgroad"></div>
<div class="map_button_stack">
<button
type="button"
class="btn btn-primary map_toggle_cityname btn-sm btn-minimum"
data-bs-toggle="button"
aria-pressed="false"
autocomplete="off"
>
도시명 표기</button
><br />
<button
type="button"
class="btn btn-secondary map_toggle_single_tap btn-sm btn-minimum"
data-bs-toggle="button"
aria-pressed="false"
autocomplete="off"
>
두번 도시 이동
</button>
</div>
</div>
<div class="city_tooltip">
<div class="city_name"></div>
<div class="nation_name"></div>
</div>
</div>
</template>
<script lang="ts">
import "@/../css/map.css";
import { reloadWorldMap, loadMapOption, MapCityParsed } from "@/map";
import { defineComponent, onMounted, PropType, ref } from "vue";
import { v4 as uuidv4 } from "uuid";
export type { MapCityParsed };
export default defineComponent({
props: {
mapTheme: {
type: String,
required: true,
},
isDetailMap: { type: Boolean, default: undefined, required: false },
clickableAll: { type: Boolean, default: undefined, required: false },
selectCallback: {
type: Function as PropType<loadMapOption["selectCallback"]>,
required: false,
},
hrefTemplate: { type: String, default: undefined, required: false },
useCachedMap: { type: Boolean, default: undefined, required: false },
year: { type: Number, default: undefined, required: false },
month: { type: Number, default: undefined, required: false },
aux: {
type: Object as PropType<loadMapOption["aux"]>,
default: undefined,
required: false,
},
neutralView: { type: Boolean, default: undefined, required: false },
showMe: { type: Boolean, default: undefined, required: false },
targetJson: { type: String, default: undefined, required: false },
reqType: {
type: String as PropType<loadMapOption["reqType"]>,
default: undefined,
},
dynamicMapTheme: { type: Boolean, default: undefined, required: false },
callback: {
type: Function as PropType<loadMapOption["callback"]>,
default: undefined,
},
startYear: { type: Number, default: undefined, required: false },
modelValue: {
type: Object as PropType<MapCityParsed>,
required: false,
},
},
emits: ["update:modelValue", "loaded"],
setup(props, { emit }) {
const uuid = uuidv4();
const modelValue = ref(props.modelValue);
onMounted(async () => {
const option: loadMapOption = {
isDetailMap: props.isDetailMap,
clickableAll: props.clickableAll,
selectCallback: (city) => {
modelValue.value = city;
emit("update:modelValue", city);
},
hrefTemplate: props.hrefTemplate,
useCachedMap: props.useCachedMap,
year: props.year,
month: props.month,
aux: props.aux,
neutralView: props.neutralView,
showMe: props.showMe,
targetJson: props.targetJson,
reqType: props.reqType,
dynamicMapTheme: props.dynamicMapTheme,
callback: (a, rawObject) => {
emit("loaded", [a, rawObject]);
},
startYear: props.startYear,
};
await reloadWorldMap(option, `#${uuid}`);
});
return {
uuid,
};
},
});
</script>
+32 -4
View File
@@ -1,9 +1,17 @@
<template>
<div class="bg0 back_bar">
<button type="button" class="btn btn-primary back_btn" @click="back">
<button type="button" class="btn btn-sammo-base2 back_btn" @click="back">
돌아가기
</button>
</button><b-button
class="btn-toggle-zoom"
:variant="toggleSearch ? 'info' : 'secondary'"
:pressed="toggleSearch"
v-if="toggleSearch !== undefined"
@click="toggleSearch = !toggleSearch"
>{{ toggleSearch ? "검색 켜짐" : "검색 꺼짐" }}</b-button
>
<h2 class="title">{{ title }}</h2>
</div>
</template>
@@ -25,6 +33,17 @@ export default defineComponent({
}
},
},
data() {
return {
toggleSearch: this.searchable,
};
},
emits: ["update:searchable"],
watch: {
toggleSearch(val: boolean) {
this.$emit("update:searchable", val);
},
},
props: {
title: {
type: String,
@@ -35,6 +54,11 @@ export default defineComponent({
default: "normal",
required: false,
},
searchable: {
type: Boolean,
default: undefined,
required: false,
},
},
});
</script>
@@ -46,8 +70,6 @@ export default defineComponent({
width: 100%;
margin: auto;
position: relative;
border-left: 1px solid #888;
border-right: 1px solid #888;
height: 24pt;
}
@@ -57,6 +79,12 @@ export default defineComponent({
height: 24pt;
}
.btn-toggle-zoom{
position: absolute;
height: 24pt;
right: 0;
}
.title {
text-align: center;
line-height: 24pt;
-22
View File
@@ -1,22 +0,0 @@
import { reloadWorldMap } from "@/map";
import { unwrap_any } from "@util/unwrap_any";
export function defaultSelectCityByMap(): void {
const $target = $("#destCityID");
console.log('city', $target);
void reloadWorldMap({
isDetailMap: false,
clickableAll: true,
neutralView: true,
useCachedMap: true,
selectCallback: function (city) {
const currVal = unwrap_any<string>($target.val());
$target.val(city.id);
$target.trigger("change");
if ($target.val() === null) {
$target.val(currVal).trigger("change").blur();
}
return false;
}
});
}
-24
View File
@@ -1,24 +0,0 @@
import { reloadWorldMap } from "@/map";
import { unwrap_any } from "@util/unwrap_any";
export function defaultSelectNationByMap(): void{
const $target = $("#destNationID");
console.log('nation', $target);
void reloadWorldMap({
isDetailMap: false,
clickableAll: true,
neutralView: true,
useCachedMap: true,
selectCallback: (city)=>{
const currVal = unwrap_any<string>($target.val());
if(!city.nationID){
return false;
}
$target.val(city.nationID).trigger("change");
if ($target.val() === null) {
$target.val(currVal).trigger("change");
}
return false;
}
});
}
+13 -1
View File
@@ -133,6 +133,14 @@ export type NationPolicy = {
properWarTrainAtmos: number,
}
export type ItemTypeKey = 'horse' | 'weapon' | 'book' | 'item';
export const ItemTypeNameMap: Record<ItemTypeKey, string> = {
horse: '명마',
weapon: '무기',
book: '서적',
item: '도구',
}
export declare type Colors = 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info' | 'dark' | 'light';
export type IDItem<T> = {
@@ -147,4 +155,8 @@ export type ToastType = {
}
export const keyScreenMode = 'sam.screenMode';
export type ScreenModeType = 'auto'|'500px'|'1000px';
export type ScreenModeType = 'auto'|'500px'|'1000px';
export declare type ValuesOf<T> = T[keyof T];
export const NoneValue = 'None' as const;
-15
View File
@@ -1,15 +0,0 @@
type JQueryBootstrapToast = {
container?: JQuery<HTMLElement>,
position?: 'top-right' | 'top-left' | 'top-center' | 'bottom-right' | 'bottom-left' | 'bottom-center',
title?: string,
subtitle?: string,
content?: string,
type?: 'info'|'success'|'warning'|'danger'|'error',
delay?: number,
img?: string,
pause_on_hover?: boolean,
}
interface JQueryStatic {
toast(opt: JQueryBootstrapToast): void;
}
+19 -11
View File
@@ -1,11 +1,13 @@
import axios from 'axios';
import $ from 'jquery';
import { extend, isNumber } from 'lodash';
import { isNumber, merge } from 'lodash';
import { convColorValue, convertDictById, stringFormat } from '@/common_legacy';
import { InvalidResponse } from '@/defs';
import { unwrap } from "@util/unwrap";
import { convertFormData } from '@util/convertFormData';
import { exportWindow } from '@util/exportWindow';
import { htmlReady } from './util/htmlReady';
import { initTooltip } from './legacy/initTooltip';
declare const serverNick: string;
declare const serverID: string;
@@ -57,7 +59,7 @@ type MapCityParsedRegionLevelText = MapCityParsedClickable & {
text: string,
}
type MapCityParsed = MapCityParsedRegionLevelText;
export type MapCityParsed = MapCityParsedRegionLevelText;
type MapCityDrawable = {
cityList: MapCityParsed[],
@@ -107,7 +109,7 @@ function is_touch_device(): boolean {
return mq(query);
}
type loadMapOption = {
export type loadMapOption = {
isDetailMap?: boolean, //복잡 지도, 단순 지도
clickableAll?: boolean, //어떤 경우든 클릭을 가능하게 함. 해당 동작의 동작 가능성 여부와는 별도.
selectCallback?: (city: MapCityParsed) => void, //callback을 지정시 clickable과 관계 없이 해당 함수를 실행.
@@ -160,7 +162,7 @@ export async function reloadWorldMap(option: loadMapOption, drawTarget = '.world
startYear: undefined,
};
option = extend({}, defaultOption, option);
option = merge({}, defaultOption, option);
const useCachedMap = option.useCachedMap;
const isDetailMap = option.isDetailMap;
@@ -505,6 +507,9 @@ export async function reloadWorldMap(option: loadMapOption, drawTarget = '.world
}
function setMouseWork(obj: MapCityDrawable) {
initTooltip($(drawTarget));
const $tooltip = $(drawTarget + ' .city_tooltip');
const $tooltip_city = $tooltip.find('.city_name');
const $tooltip_nation = $tooltip.find('.nation_name');
@@ -655,7 +660,8 @@ export async function reloadWorldMap(option: loadMapOption, drawTarget = '.world
}
if (selectCallback) {
$cityLink.on('click', function () {
$cityLink.on('click', function (e) {
e.preventDefault();
return selectCallback(city);
});
}
@@ -671,7 +677,6 @@ export async function reloadWorldMap(option: loadMapOption, drawTarget = '.world
const $hideCityNameBtn = $world_map.find('.map_toggle_cityname');
if (localStorage.getItem('sam.hideMapCityName') == 'yes') {
console.log('tryHide!');
$world_map.addClass('hide_cityname');
$hideCityNameBtn.addClass('active').attr('aria-pressed', 'true');
}
@@ -680,7 +685,6 @@ export async function reloadWorldMap(option: loadMapOption, drawTarget = '.world
//이전 상태 확인
const state = localStorage.getItem('sam.hideMapCityName') == 'no';
if (state) {
console.log('tryHide!');
$world_map.addClass('hide_cityname');
localStorage.setItem('sam.hideMapCityName', 'yes');
} else {
@@ -789,8 +793,12 @@ export async function reloadWorldMap(option: loadMapOption, drawTarget = '.world
}
exportWindow(reloadWorldMap, 'reloadWorldMap');
$(function ($) {
if (is_touch_device()) {
$('.map_body .map_toggle_single_tap').show();
htmlReady(function(){
if( is_touch_device()){
const target = document.querySelector('.map_body .map_toggle_single_tap') as HTMLElement | null;
if(!target){
return;
}
target.style.display = 'block';
}
})
});
-300
View File
@@ -1,300 +0,0 @@
import $ from 'jquery';
exportWindow($, '$');
import { exportWindow } from '@util/exportWindow';
import axios from 'axios';
import 'bootstrap';
import 'select2/dist/js/select2.full.js'
import { setAxiosXMLHttpRequest } from '@util/setAxiosXMLHttpRequest';
import { convertFormData } from '@util/convertFormData';
import { InvalidResponse } from '@/defs';
import { unwrap_any } from '@util/unwrap_any';
import { DataFormat, IdTextPair, OptionData } from 'select2';
import { unwrap } from "@util/unwrap";
import { defaultSelectCityByMap } from '@/defaultSelectCityByMap';
import { defaultSelectNationByMap } from '@/defaultSelectNationByMap';
import { colorSelect } from '@/colorSelect';
import { recruitCrewForm } from '@/recruitCrewForm';
declare const isChiefTurn: boolean;
declare const jsPlugins: string[];
declare global {
interface Window {
submitAction: () => Promise<void>;
turnList: number[],
command: string,
}
}
async function reserveTurn(turnList: number[], command: string, arg: Record<string, unknown>) {
const target = isChiefTurn ? 'j_set_chief_command.php' : 'j_set_general_command.php';
let data: InvalidResponse;
try {
const response = await axios({
url: target,
responseType: 'json',
method: 'post',
data: convertFormData({
action: command,
turnList: turnList,
arg: JSON.stringify(arg)
})
});
data = response.data;
}
catch (e) {
console.error(e);
alert(`에러가 발생했습니다: ${e}`);
return;
}
if (!data.result) {
alert(data.reason);
return;
}
if (!isChiefTurn) {
window.location.href = './';
} else {
window.location.href = 'b_chiefcenter.php';
}
}
$(function ($) {
setAxiosXMLHttpRequest();
//checkCommandArg 참고
const availableArgumentList = {
'string': [
'nationName', 'optionText', 'itemType', 'nationType', 'itemCode', 'commandType',
],
'int': [
'crewType', 'destGeneralID', 'destCityID', 'destNationID',
'amount', 'colorType',
'year', 'month',
'srcArmType', 'destArmType', //숙련전환 전용
],
'boolean': [
'isGold', 'buyRice',
],
'integerArray': [
'destNationIDList', 'destGeneralIDList', 'amountList'
]
}
type argTypes = keyof typeof availableArgumentList;
type argValues = string | number | boolean | number[];
const handlerList: Record<argTypes, ($obj: JQuery<HTMLInputElement>) => argValues> = {
'string': function ($obj: JQuery<HTMLInputElement>) {
return $.trim(unwrap_any<string>($obj.eq(0).val()));
},
'int': function ($obj: JQuery<HTMLInputElement>) {
return parseInt(unwrap_any<string>($obj.eq(0).val()));
},
'boolean': function ($obj: JQuery<HTMLInputElement>) {
switch (unwrap_any<string>($obj.eq(0).val()).toLowerCase()) {
case "true":
case "yes":
case "1":
return true;
case "false":
case "no":
case "0":
return false;
default:
throw new Error("Boolean.parse: Cannot convert string to boolean.");
}
},
'integerArray': function ($obj: JQuery<HTMLInputElement>) {
const result: number[] = [];
$obj.each(function () {
result.push(parseInt(unwrap_any<string>($(this).val())));
});
return result;
}
}
window.submitAction = async function (): Promise<void> {
const argument: Record<string, argValues> = {};
for (const typeName of Object.keys(availableArgumentList) as argTypes[]) {
const typeKeys = availableArgumentList[typeName];
for (const typeKey of typeKeys) {
let $obj = $('#' + typeKey) as JQuery<HTMLInputElement>;
if ($obj.length == 0) {
$obj = $('.' + typeKey);
if ($obj.length == 0) {
continue;
}
}
argument[typeKey] = handlerList[typeName]($obj);
}
}
console.log(argument);
await reserveTurn(window.turnList, window.command, argument);
};
$('#commonSubmit').on('click', window.submitAction);
const $colorType = $('#colorType');
if ($colorType.length) {
$colorType.select2({
theme: 'bootstrap4',
placeholder: "색상을 선택해 주세요.",
language: "ko",
containerCss: {
display: "inline-block !important",
color: 'white !important',
},
templateSelection: function (item) {
if ((item as DataFormat).disabled) {
return item.text;
}
const element = (item as OptionData).element;
if (!element) {
throw 'invalid type';
}
const bgcolor = unwrap(element.dataset.color);
const fgcolor = unwrap(element.dataset.fontColor);
// eslint-disable-next-line no-irregular-whitespace
return $(`<span><span style='background-color:${bgcolor};color:${fgcolor};'> </span>&nbsp;${item.text}</span>`);
},
templateResult: function (item) {
if ((item as DataFormat).disabled) {
return item.text;
}
const element = (item as OptionData).element;
if (!element) {
throw 'invalid type';
}
const bgcolor = unwrap(element.dataset.color);
const fgcolor = unwrap(element.dataset.fontColor);
return $(`<div style='padding: 0.75rem 0.375rem; background-color:${bgcolor};color:${fgcolor};'>${item.text}</div>`);
},
containerCssClass: 'simple-select2-align-center bg-secondary text-secondary',
dropdownCssClass: 'no-padding simple-select2-align-center bg-secondary text-secondary',
});
}
const $nationType = $('#nationType');
if ($nationType.length) {
$nationType.select2({
theme: 'bootstrap4',
language: "ko",
containerCss: {
display: "inline-block !important",
color: 'white !important',
},
containerCssClass: 'simple-select2-align-center bg-secondary text-secondary',
dropdownCssClass: 'simple-select2-align-center bg-secondary text-secondary',
});
}
const $destCityID = $('#destCityID');
if ($destCityID.length) {
$destCityID.select2({
theme: 'bootstrap4',
placeholder: "도시를 선택해 주세요.",
language: "ko",
containerCss: {
display: "inline-block !important",
color: 'white !important',
},
containerCssClass: 'simple-select2-align-center bg-secondary text-secondary',
dropdownCssClass: 'simple-select2-align-center bg-secondary text-secondary',
});
}
const $destNationID = $('#destNationID');
if ($destNationID.length) {
$destNationID.select2({
theme: 'bootstrap4',
placeholder: "국가를 선택해 주세요.",
language: "ko",
containerCss: {
display: "inline-block !important",
color: 'white !important',
},
containerCssClass: 'simple-select2-align-center bg-secondary text-secondary',
dropdownCssClass: 'simple-select2-align-center bg-secondary text-secondary',
});
}
const $destGeneralID = $('#destGeneralID');
if ($destGeneralID.length) {
$destGeneralID.select2({
theme: 'bootstrap4',
placeholder: "장수를 선택해 주세요.",
language: "ko",
containerCss: {
display: "inline-block !important",
color: 'white !important',
},
containerCssClass: 'simple-select2-align-center bg-secondary text-secondary',
dropdownCssClass: 'simple-select2-align-center bg-secondary text-secondary',
});
}
const $isGold = $('#isGold');
if ($isGold.length) {
$isGold.select2({
theme: 'bootstrap4',
placeholder: "분량을 지정해 주세요.",
language: "ko",
containerCss: {
display: "inline-block !important",
color: 'white !important',
},
minimumResultsForSearch: -1,
containerCssClass: 'simple-select2-align-center bg-secondary text-secondary',
dropdownCssClass: 'simple-select2-align-center bg-secondary text-secondary',
});
}
const $amount = $('#amount:not([type=hidden])');
if ($amount.length) {
$amount.select2({
theme: 'bootstrap4',
placeholder: "분량을 지정해 주세요.",
allowClear: false,
language: "ko",
containerCss: {
display: "inline-block !important",
color: 'white !important',
},
tags: true,
sorter: function (items) {
(items as IdTextPair[]).sort(function (lhs, rhs) {
return parseInt(lhs.id) - parseInt(rhs.id);
})
return items;
},
containerCssClass: 'simple-select2-align-center bg-secondary text-secondary',
dropdownCssClass: 'select2-only-number simple-select2-align-center bg-secondary text-secondary',
})
}
$(document).on('keypress', '.select2-only-number .select2-search__field', function (e) {
$(this).val(unwrap_any<string>($(this).val()).replace(/[^\d].+/, ""));
if ((e.which < 48 || e.which > 57)) {
e.preventDefault();
}
});
const pluginMap:Record<string, ()=>void> = {
'defaultSelectCityByMap': defaultSelectCityByMap,
'defaultSelectNationByMap': defaultSelectNationByMap,
'colorSelect': colorSelect,
'recruitCrewForm': recruitCrewForm,
};
for (const jsPlugin of jsPlugins) {
if (jsPlugin in pluginMap) {
pluginMap[jsPlugin]()
}
else{
console.error(`'${jsPlugin}' is not supported`);
}
}
});
@@ -0,0 +1,37 @@
<template>
<div v-for="(cityList, distance) in distanceList" :key="distance">
{{ distance }} 떨어진 도시:
<template v-for="(cityID, key) in cityList" :key="key">
<template v-if="key !== 0">, </template>
<a :style="{ color: colorMap[distance] ?? undefined, textDecoration:'underline' }" @click="$emit('selected', cityID)">{{
citiesMap.get(cityID)?.name
}}</a>
</template>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType } from "vue";
export default defineComponent({
props: {
distanceList: {
type: Object as PropType<Record<number, number[]>>,
required: true,
},
citiesMap: {
type: Object as PropType<Map<number, { name: string }>>,
required: true,
},
},
emits: ['selected'],
data() {
return {
colorMap: {
1: "magenta",
2: "orange",
3: "yellow",
},
};
},
});
</script>
+135
View File
@@ -0,0 +1,135 @@
<template>
<div class="crewTypeItem crewTypeSubGrid text-center s-border-b">
<div
class="crewTypeImg"
:style="{
background: '#222222 no-repeat center',
backgroundImage: `url('${crewType.img}')`,
backgroundSize: '64px',
outline: 'solid 1px gray',
}"
></div>
<div
:style="{
backgroundColor: crewType.notAvailable
? 'red'
: crewType.reqTech == 0
? 'green'
: 'limegreen',
height: '100%',
}"
class="d-grid crewTypeName"
>
<div style="margin: auto">
{{ crewType.name }}
</div>
</div>
<div>{{ crewType.attack }}</div>
<div>{{ crewType.defence }}</div>
<div>{{ crewType.speed }}</div>
<div>{{ crewType.avoid }}</div>
<div>{{ crewType.baseCost.toFixed(1) }}</div>
<div>{{ crewType.baseRice.toFixed(1) }}</div>
<div class="crewTypePanel">
<b-button-group
><b-button class="py-1" variant="dark" @click="beHalf">절반</b-button
><b-button class="py-1" variant="dark" @click="beFilled"
>채우기</b-button
><b-button class="py-1" variant="dark" @click="beFull"
>가득</b-button
></b-button-group
>
<div class="row">
<div class="col mx-2">
<div class="input-group my-0">
<span class="input-group-text py-1">병력</span>
<input
type="number"
class="form-control py-1 f_tnum px-0 text-end"
v-model="amount"
min="1"
/>
<span class="input-group-text py-1 f_tnum">00</span>
<span
class="input-group-text py-1 f_tnum"
style="
text-align: right;
min-width: 10ch;
color: #303030;
background-color: #ddd;
"
><div style="margin-left: auto">
{{ Math.ceil(amount * crewType.baseCost * goldCoeff).toLocaleString() }}
</div></span
>
</div>
</div>
</div>
</div>
<div class="crewTypeBtn d-grid">
<b-button variant="primary" @click="doSubmit">{{ commandName }}</b-button>
</div>
<div
class="crewTypeInfo text-start"
v-html="crewType.info.join('<br>')"
></div>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
import VueTypes from "vue-types";
export default defineComponent({
props: {
crewType: VueTypes.object.isRequired,
leadership: VueTypes.number.isRequired,
commandName: VueTypes.string.isRequired,
currentCrewType: VueTypes.number.def(-1),
crew: VueTypes.number.def(0),
goldCoeff: VueTypes.number.isRequired,
},
emits: ["submitOutput", "update:amount"],
watch: {
amount(val: number) {
this.$emit("update:amount", val);
},
},
setup(props, { emit }) {
const amount = ref(0);
function beHalf() {
amount.value = Math.ceil(props.leadership * 0.5);
}
function beFilled() {
if (props.crewType.id == props.currentCrewType) {
amount.value = Math.max(
1,
props.leadership - Math.floor(props.crew / 100)
);
} else {
amount.value = props.leadership;
}
}
function beFull() {
amount.value = Math.floor(props.leadership * 1.2);
}
function doSubmit(e: Event) {
console.log(e);
emit("submitOutput", e, amount.value, props.crewType.id);
}
beFilled();
return {
amount,
beHalf,
beFilled,
beFull,
doSubmit,
};
},
});
</script>
+104
View File
@@ -0,0 +1,104 @@
<template>
<TopBackBar :title="commandName" type="chief" />
<div class="bg0" v-if="!available건국"> 이상 건국은 불가능합니다.</div>
<div class="bg0" v-else>
<div>현재 도시에서 나라를 세웁니다. , 소도시에서만 가능합니다.</div>
<ul>
<li v-for="nationType in nationTypes" :key="nationType.type" class="row">
<div class="col-2 col-md-1">- {{ nationType.name }}</div>
<div class="col-4 col-md-2">
: <span style="color: cyan">{{ nationType.pros }}</span
>,
</div>
<div class="col-4 col-md-2">
<span style="color: magenta">{{ nationType.cons }}</span>
</div>
</li>
</ul>
<div class="row">
<div class="col-4 col-md-2">
국명 : <b-form-input maxlength="18" v-model="destNationName" />
</div>
<div class="col-3 col-md-2">
색상 : <ColorSelect :colors="colors" v-model="selectedColorID" />
</div>
<div class="col-3 col-md-2">
<label>성향 :</label>
<b-form-select
:options="nationTypesOption"
v-model="selectedNationType"
/>
</div>
<div class="col-2 col-md-2 d-grid">
<b-button @click="submit">{{ commandName }}</b-button>
</div>
</div>
</div>
<BottomBar :title="commandName" />
</template>
<script lang="ts">
import ColorSelect from "@/processing/SelectColor.vue";
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
import { procNationTypeList } from "../processingRes";
declare const commandName: string;
declare const procRes: {
available건국: boolean;
colors: string[];
nationTypes: procNationTypeList;
};
export default defineComponent({
components: {
ColorSelect,
TopBackBar,
BottomBar,
},
setup() {
const destNationName = ref("");
const selectedColorID = ref(0);
console.log(procRes.nationTypes);
const selectedNationType = ref(Object.values(procRes.nationTypes)[0].type);
async function submit(e: Event) {
const event = new CustomEvent<Args>("customSubmit", {
detail: {
colorType: selectedColorID.value,
nationName: destNationName.value,
nationType: selectedNationType.value,
},
});
unwrap(e.target).dispatchEvent(event);
}
const nationTypesOption: { html: string; value: string }[] = [];
for (const nationType of Object.values(procRes.nationTypes)) {
nationTypesOption.push({
html: nationType.name,
value: nationType.type,
});
}
console.log(nationTypesOption);
return {
available건국: procRes.available건국,
selectedColorID,
selectedNationType,
colors: procRes.colors,
nationTypes: procRes.nationTypes,
nationTypesOption,
destNationName,
commandName,
submit,
};
},
});
</script>
@@ -0,0 +1,78 @@
<template>
<TopBackBar :title="commandName" type="chief" />
<div class="bg0">
<div>
자신의 군량을 사거나 팝니다.<br />
</div>
<div class="row">
<div class="col-2 col-md-1">
군량을 :
<b-button-group>
<b-button :pressed="buyRice" @click="buyRice=true"></b-button>
<b-button :pressed="!buyRice" @click="buyRice=false"></b-button>
</b-button-group>
</div>
<div class="col-7 col-md-4">
금액 :
<SelectAmount
:amountGuide="amountGuide"
v-model="amount"
:maxAmount="maxAmount"
:minAmount="minAmount"
/>
</div>
<div class="col-3 col-md-2 d-grid">
<b-button variant="primary" @click="submit">{{ commandName }}</b-button>
</div>
</div>
</div>
<BottomBar :title="commandName" />
</template>
<script lang="ts">
import SelectAmount from "@/processing/SelectAmount.vue";
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
declare const commandName: string;
declare const procRes: {
minAmount: number;
maxAmount: number;
amountGuide: number[];
};
export default defineComponent({
components: {
SelectAmount,
TopBackBar,
BottomBar,
},
setup() {
const amount = ref(1000);
const buyRice = ref(true);
async function submit(e: Event) {
const event = new CustomEvent<Args>("customSubmit", {
detail: {
amount: amount.value,
buyRice: buyRice.value,
},
});
unwrap(e.target).dispatchEvent(event);
}
return {
buyRice,
amount,
minAmount: ref(procRes.minAmount),
maxAmount: ref(procRes.maxAmount),
amountGuide: procRes.amountGuide,
commandName,
submit,
};
},
});
</script>
+100
View File
@@ -0,0 +1,100 @@
<template>
<TopBackBar :title="commandName" type="chief" v-model:searchable="searchable" />
<div class="bg0">
<div>
재야나 타국의 장수를 등용합니다.<br />
서신은 개인 메세지로 전달됩니다.<br />
등용할 장수를 목록에서 선택하세요.<br />
</div>
<div class="row">
<div class="col-12 col-md-6">
장수 :
<SelectGeneral
:generals="generalList"
:groupByNation="nationList"
:textHelper="textHelpGeneral"
:searchable="searchable"
v-model="selectedGeneralID"
/>
</div>
<div class="col-4 col-md-2 d-grid">
<b-button variant="primary" @click="submit">{{ commandName }}</b-button>
</div>
</div>
</div>
<BottomBar :title="commandName" />
</template>
<script lang="ts">
import SelectGeneral from "@/processing/SelectGeneral.vue";
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
import {
convertGeneralList,
getProcSearchable,
procGeneralItem,
procGeneralKey,
procGeneralRawItemList,
procNationItem,
procNationList,
} from "../processingRes";
import { getNpcColor } from "@/common_legacy";
declare const commandName: string;
declare const procRes: {
generals: procGeneralRawItemList;
generalsKey: procGeneralKey[];
nationList: procNationList;
};
export default defineComponent({
components: {
SelectGeneral,
TopBackBar,
BottomBar,
},
setup() {
const generalList = convertGeneralList(
procRes.generalsKey,
procRes.generals
);
const selectedGeneralID = ref(generalList[0].no);
function textHelpGeneral(gen: procGeneralItem): string {
const nameColor = getNpcColor(gen.npc);
const name = nameColor
? `<span style="color:${nameColor}">${gen.name}</span>`
: gen.name;
return name;
}
async function submit(e: Event) {
const event = new CustomEvent<Args>("customSubmit", {
detail: {
destGeneralID: selectedGeneralID.value,
},
});
unwrap(e.target).dispatchEvent(event);
}
const nationList = new Map<number, procNationItem>();
for (const nationItem of procRes.nationList) {
nationList.set(nationItem.id, nationItem);
}
return {
searchable: getProcSearchable(),
selectedGeneralID,
generalList,
nationList,
commandName,
textHelpGeneral,
submit,
};
},
});
</script>
+94
View File
@@ -0,0 +1,94 @@
<template>
<TopBackBar :title="commandName" type="chief" v-model:searchable="searchable" />
<div class="bg0">
<div v-if="commandName == '등용'">
재야나 타국의 장수를 등용합니다.<br />
서신은 개인 메세지로 전달됩니다.<br />
등용할 장수를 목록에서 선택하세요.<br />
</div>
<div v-if="commandName == '선양'">
군주의 자리를 다른 장수에게 물려줍니다.<br />
장수를 선택하세요.<br />
</div>
<div class="row">
<div class="col-9 col-md-4">
장수 :
<SelectGeneral
:generals="generalList"
:textHelper="textHelpGeneral"
:searchable="searchable"
v-model="selectedGeneralID"
/>
</div>
<div class="col-3 col-md-2 d-grid">
<b-button variant="primary" @click="submit">{{ commandName }}</b-button>
</div>
</div>
</div>
<BottomBar :title="commandName" />
</template>
<script lang="ts">
import SelectGeneral from "@/processing/SelectGeneral.vue";
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
import {
convertGeneralList,
getProcSearchable,
procGeneralItem,
procGeneralKey,
procGeneralRawItemList,
} from "../processingRes";
import { getNpcColor } from "@/common_legacy";
declare const commandName: string;
declare const procRes: {
generals: procGeneralRawItemList;
generalsKey: procGeneralKey[];
};
export default defineComponent({
components: {
SelectGeneral,
TopBackBar,
BottomBar,
},
setup() {
const generalList = convertGeneralList(
procRes.generalsKey,
procRes.generals
);
const selectedGeneralID = ref(generalList[0].no);
function textHelpGeneral(gen: procGeneralItem): string {
const nameColor = getNpcColor(gen.npc);
const name = nameColor
? `<span style="color:${nameColor}">${gen.name}</span>`
: gen.name;
return `${name} (${gen.leadership}/${gen.strength}/${gen.leadership})`;
}
async function submit(e: Event) {
const event = new CustomEvent<Args>("customSubmit", {
detail: {
destGeneralID: selectedGeneralID.value,
},
});
unwrap(e.target).dispatchEvent(event);
}
return {
searchable: getProcSearchable(),
selectedGeneralID,
generalList,
commandName,
textHelpGeneral,
submit,
};
},
});
</script>
@@ -0,0 +1,220 @@
<template>
<TopBackBar :title="commandName" type="chief" />
<div class="bg0">
<div>
본인의 특정 병종 숙련을 40% 줄이고, 줄어든 숙련 9/10(90%p) 다른 병종
숙련으로 전환합니다.
</div>
<div class="row">
<div class="col-4 col-md-2">
감소 대상 숙련 :
<b-form-select v-model="srcArmTypeID">
<b-form-select-option
v-for="[armType, dexInfo] in dexFullInfo"
:key="armType"
:value="armType"
>{{ dexInfo.name }} (<span
:style="{ color: dexInfo.currentInfo.color }"
>{{ dexInfo.currentInfo.name }}</span>)</b-form-select-option
>
</b-form-select>
</div>
<div class="col-4 col-md-2">
전환 대상 숙련 :
<b-form-select v-model="destArmTypeID">
<b-form-select-option
v-for="[armType, dexInfo] in dexFullInfo"
:key="armType"
:value="armType"
>{{ dexInfo.name }} (<span
:style="{ color: dexInfo.currentInfo.color }"
>{{ dexInfo.currentInfo.name }}</span>)</b-form-select-option
>
</b-form-select>
</div>
<div class="col-4 col-md-2 d-grid">
<b-button @click="submit">{{ commandName }}</b-button>
</div>
<div :style="{
display:'grid',
gridTemplateColumns: '3ch 1ch 2ch 10ch 1ch 3ch 1ch 2ch 10ch 1ch'
}">
<div>{{dexFullInfo.get(srcArmTypeID).name}}</div>
<div class="text-end">[</div>
<div :style="`color:${dexFullInfo.get(srcArmTypeID).currentInfo.color}`">{{dexFullInfo.get(srcArmTypeID).currentInfo.name}}</div>
<div class="f_tnum text-end">{{convNumberFormat(dexFullInfo.get(srcArmTypeID).currentInfo.amount)}}</div>
<div>]</div>
<div class="text-center"></div>
<div class="text-end">[</div>
<div :style="`color:${dexFullInfo.get(srcArmTypeID).decreasedInfo.color}`">{{dexFullInfo.get(srcArmTypeID).decreasedInfo.name}}</div>
<div class="f_tnum text-end">{{convNumberFormat(dexFullInfo.get(srcArmTypeID).decreasedInfo.amount)}}</div>
<div>]</div>
</div>
<div :style="{
display:'grid',
gridTemplateColumns: '3ch 1ch 2ch 10ch 1ch 3ch 1ch 2ch 10ch 1ch'
}">
<div>{{dexFullInfo.get(destArmTypeID).name}}</div>
<div class="text-end">[</div>
<template v-if="srcArmTypeID == destArmTypeID">
<div :style="`color:${dexFullInfo.get(destArmTypeID).decreasedInfo.color}`">{{dexFullInfo.get(destArmTypeID).decreasedInfo.name}}</div>
<div class="f_tnum text-end">{{convNumberFormat(dexFullInfo.get(destArmTypeID).decreasedInfo.amount)}}</div>
</template>
<template v-else>
<div :style="`color:${dexFullInfo.get(destArmTypeID).currentInfo.color}`">{{dexFullInfo.get(destArmTypeID).currentInfo.name}}</div>
<div class="f_tnum text-end">{{convNumberFormat(dexFullInfo.get(destArmTypeID).currentInfo.amount)}}</div>
</template>
<div>]</div>
<div class="text-center"></div>
<div class="text-end">[</div>
<div :style="`color:${dexFullInfo.get(destArmTypeID).afterInfo.get(srcArmTypeID).color}`">{{dexFullInfo.get(destArmTypeID).afterInfo.get(srcArmTypeID).name}}</div>
<div class="f_tnum text-end">{{convNumberFormat(dexFullInfo.get(destArmTypeID).afterInfo.get(srcArmTypeID).amount)}}</div>
<div>]</div>
</div>
</div>
</div>
<BottomBar :title="commandName" />
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
declare const commandName: string;
type dexInfo = {
amount: number;
color: string;
name: string;
};
declare const procRes: {
ownDexList: {
armType: number;
name: string;
amount: number;
}[];
dexLevelList: dexInfo[];
decreaseCoeff: number;
convertCoeff: number;
};
export default defineComponent({
components: {
TopBackBar,
BottomBar,
},
setup() {
const srcArmTypeID = ref(procRes.ownDexList[0].armType);
const destArmTypeID = ref(procRes.ownDexList[0].armType);
async function submit(e: Event) {
const event = new CustomEvent<Args>("customSubmit", {
detail: {
srcArmType: srcArmTypeID.value,
destArmType: destArmTypeID.value,
},
});
unwrap(e.target).dispatchEvent(event);
}
function getDexCall(dex: number): { color: string; name: string } {
if (dex < 0) {
throw `올바르지 않은 수치: ${dex}`;
}
let color = "";
let name = "";
for (const nextDexLevel of procRes.dexLevelList) {
if (dex < nextDexLevel.amount) {
break;
}
color = nextDexLevel.color;
name = nextDexLevel.name;
}
return {
color,
name,
};
}
const dexFullInfo = new Map<
number,
{
armType: number;
name: string;
amount: number;
decresedAmount: number;
currentInfo: dexInfo;
decreasedInfo: dexInfo;
afterInfo: Map<number, dexInfo>;
}
>();
for (const dexItem of procRes.ownDexList) {
const amount = dexItem.amount;
const currentInfo = { ...getDexCall(amount), amount };
const decresedAmount = amount * procRes.decreaseCoeff;
const decresedAfterAmount = amount - decresedAmount;
const decreasedInfo = {
...getDexCall(decresedAfterAmount),
amount: decresedAfterAmount,
};
dexFullInfo.set(dexItem.armType, {
...dexItem,
decresedAmount,
currentInfo,
decreasedInfo,
afterInfo: new Map(),
});
}
for (const [armType, dexItem] of dexFullInfo.entries()) {
for (const [fromArmType, fromDexItem] of dexFullInfo.entries()) {
let afterAmount = fromDexItem.decresedAmount * procRes.convertCoeff;
if (armType != fromArmType) {
afterAmount += dexItem.amount;
} else {
afterAmount += dexItem.decresedAmount;
}
dexItem.afterInfo.set(fromArmType, {
amount: afterAmount,
...getDexCall(afterAmount),
});
}
}
function convDexFormat(value: dexInfo): string {
const amount = convNumberFormat(value.amount);
return `<span class="f_tnum" style="color:${value.color}">${value.name}</span>,${"\xa0".repeat(Math.max(0, 3 - value.name.length))} ${amount}`;
}
function convNumberFormat(value: number): string {
return Math.floor(value).toLocaleString();
}
console.log(dexFullInfo);
return {
...procRes,
srcArmTypeID,
destArmTypeID,
dexFullInfo,
getDexCall,
commandName,
submit,
convDexFormat,
convNumberFormat,
};
},
});
</script>
+180
View File
@@ -0,0 +1,180 @@
<template>
<TopBackBar :title="commandName" type="chief" v-model:searchable="searchable" />
<div class="bg0">
<div>
국가에 임관합니다.<br />
이미 임관/등용되었던 국가는 다시 임관할 없습니다.<br />
바로 군주의 위치로 이동합니다.<br />
임관할 국가를 목록에서 선택하세요.<br />
</div>
<div class="row">
<div class="col-6 col-md-3">
국가 :
<SelectNation
:nations="nationList"
v-model="selectedNationID"
:searchable="searchable"
/>
</div>
<div class="col-4 col-md-2 d-grid">
<b-button @click="submit">{{ commandName }}</b-button>
</div>
</div>
<div class="nation-list">
<div class="nation-header nation-row bg1 center">
<div>국가명</div>
<div>임관권유문</div>
<div class="zoom-toggle d-grid">
<b-button
:pressed="toggleZoom"
:variant="toggleZoom ? 'info' : 'secondary'"
v-model="toggleZoom"
@click="toggleZoom = !toggleZoom"
>{{ toggleZoom ? "작게 보기" : "크게 보기" }}</b-button
>
</div>
</div>
<div
v-for="[, nation] in nationList"
:key="nation.id"
:class="['nation-row', 's-border-b', toggleZoom ? 'on-zoom' : 'on-fit']"
@click="selectedNationID = nation.id"
>
<div
:style="{
backgroundColor: nation.color,
color: isBrightColor(nation.color) ? 'black' : 'white',
fontSize: '1.3em',
}"
class="d-grid"
>
<div class="align-self-center center">{{ nation.name }}</div>
</div>
<div class="nation-scout-plate align-self-center">
<div class="nation-scout-msg" v-html="nation.scoutMsg" />
</div>
</div>
</div>
</div>
<BottomBar :title="commandName" />
</template>
<script lang="ts">
import SelectNation from "@/processing/SelectNation.vue";
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
import { procNationItem, procNationList, getProcSearchable } from "../processingRes";
import { isBrightColor } from "@/util/isBrightColor";
declare const commandName: string;
declare const procRes: {
nationList: procNationList;
};
export default defineComponent({
components: {
SelectNation,
TopBackBar,
BottomBar,
},
setup() {
const nationList = new Map<number, procNationItem>();
for (const nationItem of procRes.nationList) {
nationList.set(nationItem.id, nationItem);
}
const toggleZoom = ref(true);
const selectedNationID = ref(procRes.nationList[0].id);
function selectedNation(nationID: number) {
selectedNationID.value = nationID;
}
async function submit(e: Event) {
const event = new CustomEvent<Args>("customSubmit", {
detail: {
destNationID: selectedNationID.value,
},
});
unwrap(e.target).dispatchEvent(event);
}
return {
searchable: getProcSearchable(),
nationList: ref(nationList),
selectedNationID,
commandName,
toggleZoom,
isBrightColor,
selectedNation,
submit,
};
},
});
</script>
<style lang="scss" scoped>
@import "@scss/common/break_500px.scss";
@include media-1000px {
.nation-list .nation-row {
display: grid;
grid-template-columns: 130px 870px;
}
.zoom-toggle {
display: none;
}
.zoom-toggle > * {
display: none;
}
}
@include media-500px {
.nation-list .nation-row.nation-header {
display: grid;
grid-template-columns: 3fr 1fr;
grid-template-rows: 1fr 1fr;
.zoom-toggle {
grid-column: 2/3;
grid-row: 1/3;
}
}
.nation-list .nation-row {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: 1fr minmax(1fr, calc(200px * 500 / 870));
}
.on-fit {
.nation-scout-plate {
max-height: calc(200px * 500 / 870);
overflow: hidden;
}
.nation-scout-msg {
width: 870px;
transform-origin: 0px 0px;
transform: scale(calc(500 / 870));
}
}
.on-zoom {
.nation-scout-plate {
max-height: 200px;
overflow-y: hidden;
overflow-x: auto;
}
.nation-scout-msg {
max-width: 870px;
}
}
}
</style>
@@ -0,0 +1,190 @@
<template>
<TopBackBar :title="commandName" type="chief" v-model:searchable="searchable" />
<div class="bg0">
<div>
장비를 구입하거나 매각합니다.<br />
현재 구입 불가능한 것은 <span style="color: red">붉은색</span>으로
표시됩니다.<br />
현재 도시 치안 : {{ citySecu.toLocaleString() }} &nbsp;&nbsp;&nbsp;현재
자금 : {{ gold.toLocaleString() }}<br />
</div>
<div class="row">
<div class="col-8 col-md-4">
장비:
<v-multiselect
v-model="selectedItemObj"
class="selectedItemObj"
:allow-empty="false"
:options="forFind"
:group-select="false"
group-values="values"
group-label="category"
label="searchText"
track-by="simpleName"
:show-labels="false"
selectLabel="선택(엔터)"
selectGroupLabel=""
selectedLabel="선택됨"
deselectLabel="해제(엔터)"
deselectGroupLabel=""
placeholder="아이템 선택"
:maxHeight="400"
:searchable="searchable"
>
<template v-slot:option="props">
<div
v-if="props.option.html"
v-html="
`${props.option.html} ${
props.option.notAvailable ? '(불가)' : ''
}`
"
:style="{
color: props.option.notAvailable ? 'red' : undefined,
}"
></div>
<div
v-else-if="props.option.simpleName"
:style="{
color: props.option.notAvailable ? 'red' : undefined,
}"
>
{{ props.option.simpleName }}
{{ props.option.notAvailable ? "(불가)" : undefined }}
</div>
</template>
<template v-slot:singleLabel="props">
[{{ ItemTypeNameMap[props.option.type] }}]
{{ props.option.simpleName }}
</template>
</v-multiselect>
</div>
<div class="col-4 col-md-2 d-grid">
<b-button @click="submit">{{ commandName }}</b-button>
</div>
</div>
<div v-if="selectedItemObj.obj.id != NoneValue" class="row">
<div class="col-4 col-md-2 align-self-center text-center">
{{ selectedItemObj.obj.name }}
</div>
<div class="col" v-html="selectedItemObj.obj.info"></div>
</div>
</div>
<BottomBar :title="commandName" />
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { entriesWithType } from "@util/entriesWithType";
import { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
import { getProcSearchable, procItemList, procItemType } from "../processingRes";
import { ItemTypeKey, ItemTypeNameMap, NoneValue, ValuesOf } from "@/defs";
import { convertSearch초성 } from "@/util/convertSearch초성";
declare const commandName: string;
declare const procRes: {
citySecu: number;
gold: number;
itemList: procItemList;
ownItem: Record<ItemTypeKey, procItemType>;
};
type selectItemKey = {
type: ItemTypeKey;
id: string;
html: string;
simpleName: string;
searchText: string;
notAvailable?: boolean;
obj: procItemType;
};
export default defineComponent({
components: {
TopBackBar,
BottomBar,
},
setup() {
const forFind: {
category: ValuesOf<typeof ItemTypeNameMap> | "판매";
values: selectItemKey[];
}[] = [];
//
const forSell: typeof forFind[0] = {
category: "소유 물품 판매",
values: [],
};
for (const [type, ownItem] of entriesWithType(procRes.ownItem)) {
const typeName = ItemTypeNameMap[type];
const itemNameHelp =
ownItem.id == NoneValue
? ""
: ` [${ownItem.name}, ${(ownItem.cost / 2).toLocaleString()}금]`;
forSell.values.push({
type,
id: NoneValue,
html: `${typeName} 판매${itemNameHelp}`,
simpleName: `${ownItem.id == NoneValue ? typeName : ownItem.name} 판매`,
searchText: convertSearch초성(typeName).join("|"),
notAvailable: ownItem.id == NoneValue,
obj: ownItem,
});
}
forFind.push(forSell);
const selectedItemObj = ref<selectItemKey>(forSell.values[0]);
for (const [type, itemSubList] of entriesWithType(procRes.itemList)) {
const values: selectItemKey[] = [];
const forBuy: typeof forFind[0] = {
category: `${ItemTypeNameMap[type]} 구매`,
values,
};
for (const itemObj of itemSubList.values) {
values.push({
type,
id: itemObj.id,
html: `${
itemObj.name
} 구매 [${itemObj.cost.toLocaleString()}, 필요 치안 ${itemObj.reqSecu.toLocaleString()}]`,
simpleName: `${itemObj.name} 구매`,
searchText: convertSearch초성(itemObj.name).join("|"),
notAvailable:
itemObj.reqSecu > procRes.citySecu || procRes.gold < itemObj.cost,
obj: itemObj,
});
}
forFind.push(forBuy);
}
async function submit(e: Event) {
const event = new CustomEvent<Args>("customSubmit", {
detail: {
itemType: selectedItemObj.value.type,
itemCode: selectedItemObj.value.id,
},
});
unwrap(e.target).dispatchEvent(event);
}
return {
...procRes,
searchable: getProcSearchable(),
forFind,
NoneValue,
ItemTypeNameMap,
selectedItemObj,
commandName,
submit,
};
},
});
</script>
@@ -0,0 +1,202 @@
<template>
<TopBackBar :title="commandName" type="chief" v-model:searchable="searchable" />
<div class="bg0">
<div>
장수를 따라 임관합니다.<br />
이미 임관/등용되었던 국가는 다시 임관할 없습니다.<br />
바로 군주의 위치로 이동합니다.<br />
임관할 국가를 목록에서 선택하세요.<br />
</div>
<div class="row">
<div class="col-8 col-md-4">
장수 :
<SelectGeneral
:generals="generalList"
:groupByNation="nationList"
:textHelper="textHelpGeneral"
:searchable="searchable"
v-model="selectedGeneralID"
/>
</div>
<div class="col-4 col-md-2 d-grid">
<b-button variant="primary" @click="submit">{{ commandName }}</b-button>
</div>
</div>
<div class="nation-list">
<div class="nation-header nation-row bg1 center">
<div>국가명</div>
<div>임관권유문</div>
<div class="zoom-toggle d-grid">
<b-button
:pressed="toggleZoom"
:variant="toggleZoom ? 'info' : 'secondary'"
v-model="toggleZoom"
@click="toggleZoom = !toggleZoom"
>{{ toggleZoom ? "작게 보기" : "크게 보기" }}</b-button
>
</div>
</div>
<div
v-for="[, nation] in nationList"
:key="nation.id"
:class="['nation-row', 's-border-b', toggleZoom ? 'on-zoom' : 'on-fit']"
>
<div
:style="{
backgroundColor: nation.color,
color: isBrightColor(nation.color) ? 'black' : 'white',
fontSize: '1.3em',
}"
class="d-grid"
>
<div class="align-self-center center">{{ nation.name }}</div>
</div>
<div class="nation-scout-plate align-self-center">
<div class="nation-scout-msg" v-html="nation.scoutMsg" />
</div>
</div>
</div>
</div>
<BottomBar :title="commandName" />
</template>
<script lang="ts">
import SelectGeneral from "@/processing/SelectGeneral.vue";
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
import {
convertGeneralList,
getProcSearchable,
procGeneralItem,
procGeneralKey,
procGeneralRawItemList,
procNationItem,
procNationList,
} from "../processingRes";
import { getNpcColor } from "@/common_legacy";
import { isBrightColor } from "@/util/isBrightColor";
declare const commandName: string;
declare const procRes: {
generals: procGeneralRawItemList;
generalsKey: procGeneralKey[];
nationList: procNationList;
};
export default defineComponent({
components: {
SelectGeneral,
TopBackBar,
BottomBar,
},
setup() {
const generalList = convertGeneralList(
procRes.generalsKey,
procRes.generals
);
const toggleZoom = ref(true);
const selectedGeneralID = ref(generalList[0].no);
function textHelpGeneral(gen: procGeneralItem): string {
const nameColor = getNpcColor(gen.npc);
const name = nameColor
? `<span style="color:${nameColor}">${gen.name}</span>`
: gen.name;
return name;
}
const nationList = new Map<number, procNationItem>();
for (const nationItem of procRes.nationList) {
nationList.set(nationItem.id, nationItem);
}
async function submit(e: Event) {
const event = new CustomEvent<Args>("customSubmit", {
detail: {
destGeneralID: selectedGeneralID.value,
},
});
unwrap(e.target).dispatchEvent(event);
}
return {
searchable: getProcSearchable(),
nationList: ref(nationList),
selectedGeneralID,
generalList,
commandName,
toggleZoom,
isBrightColor,
textHelpGeneral,
submit,
};
},
});
</script>
<style lang="scss" scoped>
@import "@scss/common/break_500px.scss";
@include media-1000px {
.nation-list .nation-row {
display: grid;
grid-template-columns: 130px 870px;
}
.zoom-toggle {
display: none;
}
.zoom-toggle > * {
display: none;
}
}
@include media-500px {
.nation-list .nation-row.nation-header {
display: grid;
grid-template-columns: 3fr 1fr;
grid-template-rows: 1fr 1fr;
.zoom-toggle {
grid-column: 2/3;
grid-row: 1/3;
}
}
.nation-list .nation-row {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: 1fr minmax(1fr, calc(200px * 500 / 870));
}
.on-fit {
.nation-scout-plate {
max-height: calc(200px * 500 / 870);
overflow: hidden;
}
.nation-scout-msg {
width: 870px;
transform-origin: 0px 0px;
transform: scale(calc(500 / 870));
}
}
.on-zoom {
.nation-scout-plate {
max-height: 200px;
overflow-y: hidden;
overflow-x: auto;
}
.nation-scout-msg {
max-width: 870px;
}
}
}
</style>
+379
View File
@@ -0,0 +1,379 @@
<template>
<TopBackBar :title="commandName" />
<div class="bg0">
<div>
병사를 모집합니다.
<template v-if="commandName == '징병'">
훈련과 사기치는 낮지만 가격이 저렴합니다.<br />
</template>
<template v-else-if="commandName == '모병'">
훈련과 사기치는 높지만 자금이 많이 듭니다.<br />
</template>
가능한 수보다 많게 입력하면 가능한 최대 병사를 모집합니다.<br />
이미 병사가 있는 경우 추가 {{ commandName }}되며, 병종이 다를경우는 기존의
병사는 소집해제됩니다. <br />
현재 {{ commandName }} 가능한 병종은
<span style="color: green">녹색</span>으로 표시되며, 현재
{{ commandName }} 가능한 특수병종은
<span style="color: limegreen">초록색</span>으로 표시됩니다.
</div>
<div class="crewTypeList" ref="defaultTarget">
<div class="listFront">
<div class="row gx-0 bg0">
<div class="col-12 col-md-12 d-flex align-items-center">
<div v-if="commandName == '모병'" class="text-center w-100">
모병은 가격 2배의 자금이 소요됩니다.<br />
</div>
</div>
</div>
<div class="row text-center bg2 gx-0">
<div class="col-4 col-md-2">현재 기술력 : {{ techLevel }}등급</div>
<div class="col-4 col-md-2">
현재 통솔 :
<span
:style="{
color: leadership < fullLeadership ? 'red' : undefined,
}"
>{{ leadership }}</span
>
</div>
<div class="col-4 col-md-2">최대 통솔 : {{ fullLeadership }}</div>
<div class="col-4 col-md-2">
현재 병종 : {{ crewTypeMap.get(currentCrewType).name }}
</div>
<div class="col-4 col-md-2">
현재 병사 : {{ crew.toLocaleString() }}
</div>
<div class="col-4 col-md-2">
현재 자금 : {{ gold.toLocaleString() }}
</div>
</div>
<div class="miniCrewPanel center bg0">
<div
class="crewTypeImg"
:style="{
background: '#222222 no-repeat center',
backgroundImage: `url('${destCrewType.img}')`,
backgroundSize: '64px',
outline: 'solid 1px gray',
height: '64px',
}"
></div>
<div
:style="{
backgroundColor:
(destCrewType.notAvailable
? 'red'
: destCrewType.reqTech == 0
? 'green'
: 'limegreen') + ' !important',
height: '100%',
}"
class="d-grid"
>
<div style="margin: auto">
{{ destCrewType.name }}
</div>
</div>
<div></div>
<div class="crewTypePanel">
<b-button-group
><b-button class="py-1" variant="dark" @click="beHalf"
>절반</b-button
><b-button class="py-1" variant="dark" @click="beFilled"
>채우기</b-button
><b-button class="py-1" variant="dark" @click="beFull"
>가득</b-button
></b-button-group
>
<div class="row">
<div class="col mx-2">
<div class="input-group my-0">
<span class="input-group-text py-1">병력</span>
<input
type="number"
class="form-control py-1 f_tnum px-0 text-end"
v-model="amount"
min="1"
/>
<span class="input-group-text py-1 f_tnum">00</span>
<span
class="input-group-text py-1 f_tnum"
style="
text-align: right;
min-width: 10ch;
color: #303030;
background-color: #ddd;
"
><div style="margin-left: auto">
{{
Math.ceil(
amount * destCrewType.baseCost * goldCoeff
).toLocaleString()
}}
</div></span
>
</div>
</div>
</div>
</div>
<div></div>
<b-button variant="primary" @click="submit">{{
commandName
}}</b-button>
</div>
<div class="listHeader crewTypeSubGrid text-center bg1">
<div class="crewTypeImg">사진</div>
<div class="crewTypeName">병종</div>
<div>공격</div>
<div>방어</div>
<div>기동</div>
<div>회피</div>
<div>가격</div>
<div>군량</div>
<div class="crewTypePanel">병사 </div>
<div class="crewTypeBtn">행동</div>
<div class="crewTypeInfo">특징</div>
</div>
</div>
<div class="listMain">
<template
v-for="armCrewType in armCrewTypes"
:key="armCrewType.armType"
>
<div class="s-border-b row gx-0">
<div
class="col-7 col-md-10 align-self-center px-3"
style="font-size: 1.3em"
>
{{ armCrewType.armName }} 계열
</div>
<div class="col-5 col-md-2 d-grid">
<b-button
:variant="
showNotAvailable.get(armCrewType.armType) ? 'warning' : 'dark'
"
:pressed="showNotAvailable.get(armCrewType.armType)"
class="btn-sm"
@click="toggleShowNotAvailable(armCrewType.armType)"
>{{
showNotAvailable.get(armCrewType.armType)
? "선택 할 수 있는 병종만 보기"
: "선택 할 수 없는 병종도 보기"
}}</b-button
>
</div>
</div>
<template v-for="crewType in armCrewType.values" :key="crewType.id">
<CrewTypeItem
v-if="
showNotAvailable.get(armCrewType.armType) ||
!crewType.notAvailable
"
:crewType="crewType"
:leadership="fullLeadership"
:commandName="commandName"
:currentCrewType="currentCrewType"
:crew="crew"
:goldCoeff="goldCoeff"
@submitOutput="trySubmit"
@click="
destCrewType = crewTypeMap.get(crewType.id);
beFilled();
"
/>
</template>
</template>
</div>
</div>
</div>
<BottomBar :title="commandName" />
</template>
<script lang="ts">
import CrewTypeItem from "@/processing/CrewTypeItem.vue";
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
import { procArmTypeItem, procCrewTypeItem } from "../processingRes";
declare const commandName: string;
declare const procRes: {
relYear: number;
year: number;
tech: number;
techLevel: number;
startYear: number;
goldCoeff: number;
leadership: number;
fullLeadership: number;
armCrewTypes: procArmTypeItem[];
currentCrewType: number;
crew: number;
gold: number;
};
export default defineComponent({
components: {
CrewTypeItem,
TopBackBar,
BottomBar,
},
setup() {
const amount = ref(procRes.fullLeadership - Math.floor(procRes.crew / 100));
async function submit(e: Event) {
const event = new CustomEvent<Args>("customSubmit", {
detail: {
amount: amount.value * 100,
crewType: destCrewType.value.id,
},
});
unwrap(e.target).dispatchEvent(event);
}
const showNotAvailable = ref(new Map<number, boolean>());
const crewTypeMap = new Map<number, procCrewTypeItem>();
for (const armType of procRes.armCrewTypes) {
showNotAvailable.value.set(armType.armType, false);
for (const crewType of armType.values) {
crewTypeMap.set(crewType.id, crewType);
}
}
const destCrewType = ref(unwrap(crewTypeMap.get(procRes.currentCrewType)));
function beHalf() {
amount.value = Math.ceil(procRes.fullLeadership * 0.5);
}
function beFilled() {
if (destCrewType.value.id == procRes.currentCrewType) {
amount.value = Math.max(
1,
procRes.fullLeadership - Math.floor(procRes.crew / 100)
);
} else {
amount.value = procRes.fullLeadership;
}
}
function beFull() {
amount.value = Math.floor(procRes.fullLeadership * 1.2);
}
function trySubmit(e: Event, inAmount: number, inCrewType: number) {
e.preventDefault();
amount.value = inAmount;
destCrewType.value = unwrap(crewTypeMap.get(inCrewType));
void submit(e);
}
function toggleShowNotAvailable(armType: number) {
showNotAvailable.value.set(
armType,
!(showNotAvailable.value.get(armType) ?? 0)
);
}
return {
destCrewType,
amount,
showNotAvailable,
...procRes,
crewTypeMap,
commandName,
beHalf,
beFilled,
beFull,
submit,
toggleShowNotAvailable,
trySubmit,
};
},
});
</script>
<style lang="scss">
@import "@scss/common/break_500px.scss";
.crewTypeSubGrid {
display: grid;
align-items: center;
}
.crewTypeItem .crewTypeImg {
height: 64px;
}
.crewTypeInfo {
padding-left: 0.5em;
padding-right: 0.5em;
}
@include media-1000px {
.crewTypeSubGrid {
grid-template-columns: 64px 1.5fr 1fr 1fr 1fr 1fr 1fr 1fr 250px 1.5fr 270px;
}
.miniCrewPanel {
display: none;
}
.only500pxMode {
display: none;
}
}
@include media-500px {
.only1000pxMode {
display: none !important;
}
.listFront {
position: sticky;
top: 0px;
}
.crewTypeSubGrid {
grid-template-columns: 64px 1.5fr 1fr 1fr 1fr 270px;
grid-template-rows: 1fr 1fr;
}
.crewTypeImg {
grid-column: 1 / 2;
grid-row: 1 / 3;
}
.crewTypeName {
grid-row: 1/ 3;
}
.crewTypeInfo {
grid-column: -2 / -1;
grid-row: 1 / 3;
}
.crewTypePanel {
display: none;
}
.crewTypeBtn {
display: none;
}
.crewTypeBtn > button {
display: none;
}
.miniCrewPanel .crewTypePanel {
display: block;
}
.miniCrewPanel {
display: grid;
grid-template-columns: 64px 1.5fr 0.5fr 270px 0.5fr 2fr;
grid-template-rows: 64px;
align-items: center;
}
}
</style>
<style scoped>
</style>
+79
View File
@@ -0,0 +1,79 @@
<template>
<TopBackBar :title="commandName" type="chief" />
<div class="bg0">
<div>
자신의 자금이나 군량을 국가 재산으로 헌납합니다.
</div>
<div class="row">
<div class="col-2 col-md-1">
자원 :
<b-button-group>
<b-button :pressed="isGold" @click="isGold = true"></b-button>
<b-button :pressed="!isGold" @click="isGold = false"></b-button>
</b-button-group>
</div>
<div class="col-7 col-md-4">
금액 :
<SelectAmount
:amountGuide="amountGuide"
v-model="amount"
:maxAmount="maxAmount"
:minAmount="minAmount"
/>
</div>
<div class="col-3 col-md-2 d-grid">
<b-button variant="primary" @click="submit">{{ commandName }}</b-button>
</div>
</div>
</div>
<BottomBar :title="commandName" />
</template>
<script lang="ts">
import SelectAmount from "@/processing/SelectAmount.vue";
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
declare const commandName: string;
declare const procRes: {
minAmount: number;
maxAmount: number;
amountGuide: number[];
};
export default defineComponent({
components: {
SelectAmount,
TopBackBar,
BottomBar,
},
setup() {
const amount = ref(1000);
const isGold = ref(true);
async function submit(e: Event) {
const event = new CustomEvent<Args>("customSubmit", {
detail: {
amount: amount.value,
isGold: isGold.value,
},
});
unwrap(e.target).dispatchEvent(event);
}
return {
amount,
isGold,
minAmount: ref(procRes.minAmount),
maxAmount: ref(procRes.maxAmount),
amountGuide: procRes.amountGuide,
commandName,
submit,
};
},
});
</script>
+44
View File
@@ -0,0 +1,44 @@
import { default as che_건국 } from "./che_건국.vue";
import { default as che_군량매매 } from "./che_군량매매.vue";
import { default as che_등용 } from "./che_등용.vue";
import { default as che_선양 } from "./che_선양.vue";
import { default as che_숙련전환 } from "./che_숙련전환.vue";
import { default as che_임관 } from "./che_임관.vue";
import { default as che_장비매매 } from "./che_장비매매.vue";
import { default as che_장수대상임관 } from "./che_장수대상임관.vue";
import { default as che_징병 } from "./che_징병.vue";
import { default as che_헌납 } from "./che_헌납.vue";
import { default as ProcessCity } from "../ProcessCity.vue";
import { default as ProcessGeneralAmount } from "../ProcessGeneralAmount.vue";
//TODO: 자주 쓰는 녀석들은 Slot으로 변경
export const commandMap: Record<string, typeof ProcessCity> = {
che_강행: ProcessCity,
che_군량매매,
che_건국,
che_등용,
che_모병: che_징병,
che_선동: ProcessCity,
che_선양,
che_숙련전환,
che_이동: ProcessCity,
che_임관,
che_장비매매,
che_장수대상임관,
che_징병,
che_증여: ProcessGeneralAmount,
che_첩보: ProcessCity,
che_출병: ProcessCity,
che_탈취: ProcessCity,
che_파괴: ProcessCity,
che_화계: ProcessCity,
che_헌납,
}
/*
-
-
*/
@@ -0,0 +1,61 @@
<template>
<TopBackBar :title="commandName" type="chief" />
<div class="bg0">
<div>
국기를 변경합니다. 1 가능합니다.<br>
</div>
<div class="row">
<div class="col-6 col-md-3">
색상 :
<ColorSelect :colors="colors" v-model="selectedColorID" />
</div>
<div class="col-4 col-md-2 d-grid">
<b-button @click="submit">{{ commandName }}</b-button>
</div>
</div>
</div>
<BottomBar :title="commandName" />
</template>
<script lang="ts">
import ColorSelect from "@/processing/SelectColor.vue";
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
declare const commandName: string;
declare const procRes: {
colors: string[],
};
export default defineComponent({
components: {
ColorSelect,
TopBackBar,
BottomBar,
},
setup() {
const selectedColorID = ref(0);
async function submit(e: Event) {
const event = new CustomEvent<Args>("customSubmit", {
detail: {
colorType: selectedColorID.value,
},
});
unwrap(e.target).dispatchEvent(event);
}
return {
selectedColorID,
colors: procRes.colors,
commandName,
submit,
};
},
});
</script>
@@ -0,0 +1,53 @@
<template>
<TopBackBar :title="commandName" type="chief" />
<div class="bg0">
<div>
나라의 이름을 바꿉니다. 황제가 1 가능합니다.<br />
</div>
<div class="row">
<div class="col-6 col-md-3">
국명 :
<b-form-input maxlength="18" v-model="destNationName"/>
</div>
<div class="col-4 col-md-2 d-grid">
<b-button @click="submit">{{ commandName }}</b-button>
</div>
</div>
</div>
<BottomBar :title="commandName" />
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
declare const commandName: string;
export default defineComponent({
components: {
TopBackBar,
BottomBar,
},
setup() {
const destNationName = ref("");
async function submit(e: Event) {
const event = new CustomEvent<Args>("customSubmit", {
detail: {
nationName: destNationName.value,
},
});
unwrap(e.target).dispatchEvent(event);
}
return {
destNationName,
commandName,
submit,
};
},
});
</script>
@@ -0,0 +1,157 @@
<template>
<TopBackBar :title="commandName" type="chief" v-model:searchable="searchable" />
<div class="bg0">
<MapLegacyTemplate
:isDetailMap="false"
:clickableAll="true"
:neutralView="true"
:useCachedMap="true"
:mapTheme="mapTheme"
v-model="selectedCityObj"
/>
<div>
타국에게 원조합니다.<br />
작위별로 금액 제한이 있습니다.<br /><br />
<ul>
<template v-for="({ text, amount }, level) in levelInfo" :key="level">
<li>
<span
:style="{
width: '4em',
display: 'inline-block',
...(level != currentNationLevel
? {}
: {
textDecoration: 'underline',
fontWeight: 'bold',
}),
}"
>{{ text }}</span
>: {{ amount.toLocaleString() }}
</li>
</template>
</ul>
<br />
원조할 국가를 목록에서 선택하세요.<br /><br />
</div>
<div class="row">
<div class="col-6 col-md-3">
국가 :
<SelectNation :nations="nationList" v-model="selectedNationID" :searchable="searchable" />
</div>
<div class="col-6 col-md-0"></div>
<div class="col-8 col-md-4">
:
<SelectAmount
:amountGuide="amountGuide"
v-model="goldAmount"
:step="10"
:maxAmount="maxAmount"
:minAmount="minAmount"
/>
</div>
<div class="col-8 col-md-4">
:
<SelectAmount
:amountGuide="amountGuide"
v-model="riceAmount"
:step="10"
:maxAmount="maxAmount"
:minAmount="minAmount"
/>
</div>
<div class="col-4 col-md-2 d-grid">
<b-button variant="primary" @click="submit">{{ commandName }}</b-button>
</div>
</div>
</div>
<BottomBar :title="commandName" />
</template>
<script lang="ts">
import MapLegacyTemplate, {
MapCityParsed,
} from "@/components/MapLegacyTemplate.vue";
import SelectNation from "@/processing/SelectNation.vue";
import SelectAmount from "@/processing/SelectAmount.vue";
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
import { getProcSearchable, procNationItem, procNationList } from "../processingRes";
declare const mapTheme: string;
declare const commandName: string;
declare const procRes: {
nationList: procNationList;
currentNationLevel: number;
levelInfo: Record<
number,
{
text: string;
amount: number;
}
>;
minAmount: number;
maxAmount: number;
amountGuide: number[];
};
export default defineComponent({
components: {
MapLegacyTemplate,
SelectNation,
SelectAmount,
TopBackBar,
BottomBar,
},
watch: {
selectedCityObj(city: MapCityParsed) {
if (!city.nationID) {
return;
}
this.selectedNationID = city.nationID;
},
},
setup() {
const nationList = new Map<number, procNationItem>();
for (const nationItem of procRes.nationList) {
nationList.set(nationItem.id, nationItem);
}
const goldAmount = ref(procRes.minAmount);
const riceAmount = ref(procRes.minAmount);
const selectedNationID = ref(procRes.nationList[0]?.id);
const selectedCityObj = ref(); //mapping
async function submit(e: Event) {
const event = new CustomEvent<Args>("customSubmit", {
detail: {
amountList: [goldAmount.value, riceAmount.value],
destNationID: selectedNationID.value,
},
});
unwrap(e.target).dispatchEvent(event);
}
return {
searchable: getProcSearchable(),
mapTheme,
goldAmount,
riceAmount,
nationList,
selectedNationID,
selectedCityObj,
currentNationLevel: procRes.currentNationLevel,
levelInfo: procRes.levelInfo,
minAmount: ref(procRes.minAmount),
maxAmount: ref(procRes.maxAmount),
amountGuide: procRes.amountGuide,
commandName,
submit,
};
},
});
</script>
+148
View File
@@ -0,0 +1,148 @@
<template>
<TopBackBar :title="commandName" type="chief" v-model:searchable="searchable" />
<div class="bg0">
<MapLegacyTemplate
:isDetailMap="false"
:clickableAll="true"
:neutralView="true"
:useCachedMap="true"
:mapTheme="mapTheme"
v-model="selectedCityObj"
/>
<div>
선택된 도시로 아국 장수를 발령합니다.<br />
아국 도시로만 발령이 가능합니다.<br />
목록을 선택하거나 도시를 클릭하세요.<br />
</div>
<div class="row">
<div class="col-12 col-md-6">
장수 :
<SelectGeneral
:cities="citiesMap"
:generals="generalList"
:troops="troops"
:textHelper="textHelpGeneral"
:searchable="searchable"
v-model="selectedGeneralID"
/>
</div>
<div class="col-6 col-md-4">
도시 :
<SelectCity :cities="citiesMap" v-model="selectedCityID" :searchable="searchable" />
</div>
<div class="col-4 col-md-2 d-grid">
<b-button variant="primary" @click="submit">{{ commandName }}</b-button>
</div>
</div>
</div>
<BottomBar :title="commandName" />
</template>
<script lang="ts">
import MapLegacyTemplate, {
MapCityParsed,
} from "@/components/MapLegacyTemplate.vue";
import SelectCity from "@/processing/SelectCity.vue";
import SelectGeneral from "@/processing/SelectGeneral.vue";
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
import {
convertGeneralList,
getProcSearchable,
procGeneralItem,
procGeneralKey,
procGeneralRawItemList,
procTroopList,
} from "../processingRes";
import { getNpcColor } from "@/common_legacy";
declare const mapTheme: string;
declare const currentCity: number;
declare const commandName: string;
declare const procRes: {
distanceList: Record<number, number[]>;
cities: [number, string][];
troops: procTroopList;
generals: procGeneralRawItemList;
generalsKey: procGeneralKey[];
};
export default defineComponent({
components: {
MapLegacyTemplate,
SelectCity,
SelectGeneral,
TopBackBar,
BottomBar,
},
watch: {
selectedCityObj(city: MapCityParsed) {
this.selectedCityID = city.id;
},
},
setup() {
const citiesMap = new Map<
number,
{
name: string;
info?: string;
}
>();
for (const [id, name] of procRes.cities) {
citiesMap.set(id, { name });
}
const generalList = convertGeneralList(
procRes.generalsKey,
procRes.generals
);
const selectedCityID = ref(currentCity);
function selectedCity(cityID: number) {
selectedCityID.value = cityID;
}
const selectedGeneralID = ref(generalList[0].no);
function textHelpGeneral(gen: procGeneralItem): string{
const troops = (!gen.troopID)?'':`,${procRes.troops[gen.troopID].name}`;
const nameColor = getNpcColor(gen.npc);
const name = nameColor?`<span style="color:${nameColor}">${gen.name}</span>`:gen.name;
return `${name} [${citiesMap.get(unwrap(gen.cityID))?.name}${troops}] (${gen.leadership}/${gen.leadership}/${gen.intel}) <병${unwrap(gen.crew).toLocaleString()}/훈${gen.train}/사${gen.atmos}>`;
}
async function submit(e: Event) {
const event = new CustomEvent<Args>("customSubmit", {
detail: {
destCityID: selectedCityID.value,
destGeneralID: selectedGeneralID.value,
},
});
unwrap(e.target).dispatchEvent(event);
// title: `${gen.name}[${this.cities.get(gen.cityID)?.name}] (${gen.leadership}/${gen.leadership}/${gen.intel}) <${gen.crew.toLocaleString()}/${gen.train}/${gen.atmos}`,
}
return {
searchable: getProcSearchable(),
mapTheme: ref(mapTheme),
citiesMap: ref(citiesMap),
selectedCityID,
selectedGeneralID,
selectedCityObj: ref(undefined as MapCityParsed | undefined),
distanceList: procRes.distanceList,
troops: procRes.troops,
generalList,
commandName,
selectedCity,
textHelpGeneral,
submit,
};
},
});
</script>
@@ -0,0 +1,138 @@
<template>
<TopBackBar :title="commandName" type="chief" v-model:searchable="searchable" />
<div class="bg0">
<MapLegacyTemplate
:isDetailMap="false"
:clickableAll="true"
:neutralView="true"
:useCachedMap="true"
:mapTheme="mapTheme"
v-model="selectedCityObj"
/>
<div>
타국에게 불가침을 제의합니다.<br />
제의할 국가를 목록에서 선택하세요.<br />
불가침 기한 다음 달부터 선포 가능합니다.<br />
현재 제의가 불가능한 국가는
<span style="color: red">붉은색</span>으로 표시됩니다.<br />
</div>
<div class="row">
<div class="col-4 col-md-3">
국가 :
<SelectNation :nations="nationList" v-model="selectedNationID" :searchable="searchable" />
</div>
<div class="col-5 col-md-3">
기간 :
<div class="input-group">
<b-form-select class="text-end selectedYear" v-model="selectedYear"
><b-form-select-option
v-for="yearP in maxYear - minYear + 1"
:key="yearP"
:value="yearP + minYear - 1"
>{{ yearP + minYear - 1 }}</b-form-select-option
>
</b-form-select>
<span class="input-group-text px-2"></span>
<b-form-select class="text-center" v-model="selectedMonth"
><b-form-select-option v-for="month in 12" :key="month" :value="month">{{
month
}}</b-form-select-option>
</b-form-select>
<span class="input-group-text px-2"></span>
</div>
</div>
<div class="col-3 col-md-2 d-grid">
<b-button @click="submit">{{ commandName }}</b-button>
</div>
</div>
</div>
<BottomBar :title="commandName" />
</template>
<script lang="ts">
import MapLegacyTemplate, {
MapCityParsed,
} from "@/components/MapLegacyTemplate.vue";
import SelectNation from "@/processing/SelectNation.vue";
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
import { getProcSearchable, procNationItem, procNationList } from "../processingRes";
declare const mapTheme: string;
declare const commandName: string;
declare const procRes: {
nationList: procNationList;
startYear: number;
minYear: number;
maxYear: number;
month: number;
};
export default defineComponent({
components: {
MapLegacyTemplate,
SelectNation,
TopBackBar,
BottomBar,
},
watch: {
selectedCityObj(city: MapCityParsed) {
if (!city.nationID) {
return;
}
this.selectedNationID = city.nationID;
},
},
setup() {
const nationList = new Map<number, procNationItem>();
for (const nationItem of procRes.nationList) {
nationList.set(nationItem.id, nationItem);
}
const selectedNationID = ref(procRes.nationList[0].id);
const selectedCityObj = ref(); //mapping
const selectedYear = ref(procRes.minYear);
const selectedMonth = ref(procRes.month);
function selectedNation(nationID: number) {
selectedNationID.value = nationID;
}
async function submit(e: Event) {
const event = new CustomEvent<Args>("customSubmit", {
detail: {
destNationID: selectedNationID.value,
year: selectedYear.value,
month: selectedMonth.value,
},
});
unwrap(e.target).dispatchEvent(event);
}
return {
searchable: getProcSearchable(),
...procRes,
selectedYear,
selectedMonth,
mapTheme: ref(mapTheme),
nationList: ref(nationList),
selectedCityObj,
selectedNationID,
commandName,
selectedNation,
submit,
};
},
});
</script>
<style lang="scss" scoped>
.selectedYear{
width:32%;
}
</style>
@@ -0,0 +1,139 @@
<template>
<TopBackBar :title="commandName" type="chief" v-model:searchable="searchable" />
<div class="bg0">
<MapLegacyTemplate
:isDetailMap="false"
:clickableAll="true"
:neutralView="true"
:useCachedMap="true"
:mapTheme="mapTheme"
v-model="selectedCityObj"
/>
<div>
선택된 국가에 피장파장을 발동합니다.<br />
지정한 전략을 상대국이
{{ delayCnt }} 동안 사용할 없게됩니다.<br />
대신 아국은 지정한 전략을 {{ postReqTurn }} 동안 사용할 없습니다.<br />
선포, 전쟁중인 상대국에만 가능합니다.<br />
상대 국가를 목록에서 선택하세요.<br />
현재 피장파장이 불가능한 국가는
<span style="color: red">붉은색</span>으로 표시됩니다.<br />
</div>
<div class="row">
<div class="col-6 col-md-3">
국가 :
<SelectNation :nations="nationList" v-model="selectedNationID" :searchable="searchable" />
</div>
<div class="col-3 col-md-2">
<label>전략 :</label>
<b-form-select
:options="commandTypesOption"
v-model="selectedCommandID"
/>
</div>
<div class="col-3 col-md-2 d-grid">
<b-button @click="submit">{{ commandName }}</b-button>
</div>
</div>
</div>
<BottomBar :title="commandName" />
</template>
<script lang="ts">
import MapLegacyTemplate, {
MapCityParsed,
} from "@/components/MapLegacyTemplate.vue";
import SelectNation from "@/processing/SelectNation.vue";
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
import { getProcSearchable, procNationItem, procNationList } from "../processingRes";
declare const mapTheme: string;
declare const commandName: string;
declare const procRes: {
nationList: procNationList;
startYear: number;
delayCnt: number;
postReqTurn: number;
availableCommandTypeList: Record<
number,
{
name: string;
remainTurn: number;
}
>;
};
export default defineComponent({
components: {
MapLegacyTemplate,
SelectNation,
TopBackBar,
BottomBar,
},
watch: {
selectedCityObj(city: MapCityParsed) {
if (!city.nationID) {
return;
}
this.selectedNationID = city.nationID;
},
},
setup() {
const nationList = new Map<number, procNationItem>();
for (const nationItem of procRes.nationList) {
nationList.set(nationItem.id, nationItem);
}
const selectedNationID = ref(procRes.nationList[0].id);
const selectedCityObj = ref(); //mapping
const commandTypesOption: { html: string; value: string }[] = [];
for (const [commandTypeID, commandTypeInfo] of Object.entries(procRes.availableCommandTypeList)) {
const notAvailable = commandTypeInfo.remainTurn > 0;
const notAvailableText = notAvailable?' (불가)':'';
const name = `${commandTypeInfo.name}${notAvailableText}`;
const html = notAvailable?`<span style='color:red;'>${name}</span>`:name;
commandTypesOption.push({
html,
value: commandTypeID,
});
}
const selectedCommandID = ref(
Object.keys(procRes.availableCommandTypeList)[0]
);
function selectedNation(nationID: number) {
selectedNationID.value = nationID;
}
async function submit(e: Event) {
const event = new CustomEvent<Args>("customSubmit", {
detail: {
destNationID: selectedNationID.value,
commandType: selectedCommandID.value,
},
});
unwrap(e.target).dispatchEvent(event);
}
return {
searchable: getProcSearchable(),
...procRes,
selectedCommandID,
commandTypesOption,
mapTheme: ref(mapTheme),
nationList: ref(nationList),
selectedCityObj,
selectedNationID,
commandName,
selectedNation,
submit,
};
},
});
</script>
+36
View File
@@ -0,0 +1,36 @@
import { default as che_국기변경 } from "./che_국기변경.vue";
import { default as che_국호변경 } from "./che_국호변경.vue";
import { default as che_물자원조 } from "./che_물자원조.vue";
import { default as che_불가침제의 } from "./che_불가침제의.vue";
import { default as che_피장파장 } from "./che_피장파장.vue";
import { default as ProcessNation } from "../ProcessNation.vue";
import { default as ProcessGeneralAmount } from "../ProcessGeneralAmount.vue";
import { default as ProcessGeneralCity } from "./che_발령.vue";
import { default as ProcessCity } from "../ProcessCity.vue";
export const commandMap: Record<string, typeof ProcessNation> = {
che_국기변경,
che_국호변경,
che_급습: ProcessNation,
che_몰수: ProcessGeneralAmount,
che_물자원조,
che_발령: ProcessGeneralCity,
che_백성동원: ProcessCity,
che_불가침제의,
che_불가침파기제의: ProcessNation,
che_선전포고: ProcessNation,
che_수몰: ProcessCity,
che_이호경식: ProcessNation,
che_종전제의: ProcessNation,
che_천도: ProcessCity,
che_초토화: ProcessCity,
che_포상: ProcessGeneralAmount,
che_피장파장,
che_허보: ProcessCity,
}
/*
-
-
*/
+157
View File
@@ -0,0 +1,157 @@
<template>
<TopBackBar :title="commandName" v-model:searchable="searchable" />
<div class="bg0">
<MapLegacyTemplate
:isDetailMap="false"
:clickableAll="true"
:neutralView="true"
:useCachedMap="true"
:mapTheme="mapTheme"
v-model="selectedCityObj"
/>
<div v-if="commandName == '강행'">
선택된 도시로 강행합니다.<br />
최대 3칸내 도시로만 강행이 가능합니다.<br />
목록을 선택하거나 도시를 클릭하세요.<br />
</div>
<div v-else-if="commandName == '이동'">
선택된 도시로 이동합니다.<br />
인접 도시로만 이동이 가능합니다.<br />
목록을 선택하거나 도시를 클릭하세요.<br />
</div>
<div v-else-if="commandName == '출병'">
선택된 도시를 향해 침공을 합니다.<br />
침공 경로에 적군의 도시가 있다면 전투를 벌입니다.<br />
목록을 선택하거나 도시를 클릭하세요.<br />
</div>
<div v-else-if="commandName == '첩보'">
선택된 도시에 첩보를 실행합니다.<br />
인접도시일 경우 많은 정보를 얻을 있습니다.<br />
목록을 선택하거나 도시를 클릭하세요.<br />
</div>
<div v-else-if="commandName in { 화계: 1, 탈취: 1, 파괴: 1, 선동: 1 }">
선택된 도시에 {{ commandName
}}{{ JosaPick(commandName, "을") }} 실행합니다.<br />
목록을 선택하거나 도시를 클릭하세요.<br />
</div>
<div v-else-if="commandName == '수몰'">
선택된 도시에 수몰을 발동합니다.<br />
전쟁중인 상대국 도시만 가능합니다.<br />
목록을 선택하거나 도시를 클릭하세요.<br />
</div>
<div v-else-if="commandName == '백성동원'">
선택된 도시에 백성을 동원해 성벽을 쌓습니다.<br />
아국 도시만 가능합니다.<br />
목록을 선택하거나 도시를 클릭하세요.<br />
</div>
<div v-else-if="commandName == '천도'">
선택된 도시로 천도합니다.<br />
현재 수도에서 연결된 도시만 가능하며, 1+2×거리만큼의 턴이 필요합니다.<br />
목록을 선택하거나 도시를 클릭하세요.<br />
</div>
<div v-else-if="commandName == '허보'">
선택된 도시에 허보를 발동합니다.<br />
전쟁중인 상대국 도시만 가능합니다.<br />
목록을 선택하거나 도시를 클릭하세요.<br />
</div>
<div v-else-if="commandName == '초토화'">
선택된 도시를 초토화 시킵니다.<br />
도시가 공백지가 되며, 도시의 인구, 내정 상태에 따라 상당량의 국고가
확보됩니다.<br />
국가의 수뇌들은 명성을 잃고, 모든 장수들은 배신 수치가 1 증가합니다.<br />
목록을 선택하거나 도시를 클릭하세요.<br />
</div>
<div class="row">
<div class="col-4 col-md-2">
도시:
<SelectCity :cities="citiesMap" v-model="selectedCityID" :searchable="searchable" />
</div>
<div class="col-4 col-md-2 d-grid">
<b-button @click="submit">{{ commandName }}</b-button>
</div>
</div>
<CityBasedOnDistance
:citiesMap="citiesMap"
:distanceList="distanceList"
@selected="selected"
/>
</div>
<BottomBar :title="commandName" />
</template>
<script lang="ts">
import MapLegacyTemplate, {
MapCityParsed,
} from "@/components/MapLegacyTemplate.vue";
import SelectCity from "@/processing/SelectCity.vue";
import CityBasedOnDistance from "@/processing/CitiesBasedOnDistance.vue";
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
import { pick as JosaPick } from "@util/JosaUtil";
import { getProcSearchable } from "./processingRes";
declare const mapTheme: string;
declare const currentCity: number;
declare const commandName: string;
declare const procRes: {
distanceList: Record<number, number[]>;
cities: [number, string][];
};
export default defineComponent({
components: {
MapLegacyTemplate,
SelectCity,
CityBasedOnDistance,
TopBackBar,
BottomBar,
},
watch: {
selectedCityObj(city: MapCityParsed) {
this.selectedCityID = city.id;
},
},
setup() {
const citiesMap = new Map<
number,
{
name: string;
info?: string;
}
>();
for (const [id, name] of procRes.cities) {
citiesMap.set(id, { name });
}
const selectedCityID = ref(currentCity);
function selected(cityID: number) {
selectedCityID.value = cityID;
}
async function submit(e: Event) {
const event = new CustomEvent<Args>("customSubmit", {
detail: {
destCityID: selectedCityID.value,
},
});
unwrap(e.target).dispatchEvent(event);
}
return {
searchable: getProcSearchable(),
mapTheme: ref(mapTheme),
citiesMap: ref(citiesMap),
selectedCityID,
selectedCityObj: ref(undefined as MapCityParsed | undefined),
distanceList: procRes.distanceList,
commandName,
JosaPick,
selected,
submit,
};
},
});
</script>
+143
View File
@@ -0,0 +1,143 @@
<template>
<TopBackBar :title="commandName" type="chief" v-model:searchable="searchable" />
<div class="bg0">
<div v-if="commandName == '몰수'">
장수의 자금이나 군량을 몰수합니다.<br />
몰수한것은 국가재산으로 귀속됩니다.<br />
</div>
<div v-else-if="commandName == '포상'">
국고로 장수에게 자금이나 군량을 지급합니다.<br />
</div>
<div v-else-if="commandName == '증여'">
자신의 자금이나 군량을 다른 장수에게 증여합니다.<br>
</div>
<div class="row">
<div class="col-12 col-md-5">
장수 :
<SelectGeneral
:cities="citiesMap"
:generals="generalList"
v-model="selectedGeneralID"
:textHelper="textHelpGeneral"
:searchable="searchable"
/>
</div>
<div class="col-2 col-md-1">
자원 :
<b-button-group>
<b-button :pressed="isGold" @click="isGold=true"></b-button>
<b-button :pressed="!isGold" @click="isGold=false"></b-button>
</b-button-group>
</div>
<div class="col-7 col-md-4">
금액 :
<SelectAmount
:amountGuide="amountGuide"
v-model="amount"
:maxAmount="maxAmount"
:minAmount="minAmount"
/>
</div>
<div class="col-3 col-md-2 d-grid">
<b-button variant="primary" @click="submit">{{ commandName }}</b-button>
</div>
</div>
</div>
<BottomBar :title="commandName" />
</template>
<script lang="ts">
import SelectGeneral from "@/processing/SelectGeneral.vue";
import SelectAmount from "@/processing/SelectAmount.vue";
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
import {
convertGeneralList,
getProcSearchable,
procGeneralItem,
procGeneralKey,
procGeneralRawItemList,
} from "./processingRes";
import { getNpcColor } from "@/common_legacy";
declare const commandName: string;
declare const procRes: {
distanceList: Record<number, number[]>;
cities: [number, string][];
generals: procGeneralRawItemList;
generalsKey: procGeneralKey[];
minAmount: number;
maxAmount: number;
amountGuide: number[];
};
export default defineComponent({
components: {
SelectGeneral,
SelectAmount,
TopBackBar,
BottomBar,
},
setup() {
const citiesMap = new Map<
number,
{
name: string;
info?: string;
}
>();
for (const [id, name] of procRes.cities) {
citiesMap.set(id, { name });
}
const generalList = convertGeneralList(
procRes.generalsKey,
procRes.generals
);
const amount = ref(1000);
const isGold = ref(true);
const selectedGeneralID = ref(generalList[0].no);
function textHelpGeneral(gen: procGeneralItem): string {
const nameColor = getNpcColor(gen.npc);
const name = nameColor
? `<span style="color:${nameColor}">${gen.name}</span>`
: gen.name;
return `${name} (금${unwrap(gen.gold).toLocaleString()}/쌀${unwrap(gen.rice).toLocaleString()}) (${
gen.leadership
}/${gen.leadership}/${gen.intel})`;
}
async function submit(e: Event) {
const event = new CustomEvent<Args>("customSubmit", {
detail: {
amount: amount.value,
isGold: isGold.value,
destGeneralID: selectedGeneralID.value,
},
});
unwrap(e.target).dispatchEvent(event);
}
return {
searchable: getProcSearchable(),
amount,
isGold,
selectedGeneralID,
citiesMap: ref(citiesMap),
distanceList: procRes.distanceList,
minAmount: ref(procRes.minAmount),
maxAmount: ref(procRes.maxAmount),
amountGuide: procRes.amountGuide,
generalList,
commandName,
textHelpGeneral,
submit,
};
},
});
</script>
+136
View File
@@ -0,0 +1,136 @@
<template>
<TopBackBar :title="commandName" type="chief" v-model:searchable="searchable" />
<div class="bg0">
<MapLegacyTemplate
:isDetailMap="false"
:clickableAll="true"
:neutralView="true"
:useCachedMap="true"
:mapTheme="mapTheme"
v-model="selectedCityObj"
/>
<div v-if="commandName == '선전포고'">
타국에게 선전 포고합니다.<br />
선전 포고할 국가를 목록에서 선택하세요.<br />
고립되지 않은 아국 도시에서 인접한 국가에 선포 가능합니다.<br />
초반제한 해제 2년전부터 선포가 가능합니다. ({{ startYear + 1 }} 1월부터
가능)<br />
현재 선포가 불가능한 국가는 배경색이
<span style="color: red">붉은색</span>으로 표시됩니다.<br />
</div>
<div v-else-if="commandName == '급습'">
선택된 국가에 급습을 발동합니다.<br />
선포, 전쟁중인 상대국에만 가능합니다.<br />
상대 국가를 목록에서 선택하세요.<br />
현재 급습이 불가능한 국가는
<span style="color: red">붉은색</span>으로 표시됩니다.<br />
</div>
<div v-else-if="commandName == '불가침 파기 제의'">
불가침중인 국가에 조약 파기를 제의합니다.<br />
제의할 국가를 목록에서 선택하세요.<br />
현재 제의가 불가능한 국가는
<span style="color: red">붉은색</span>으로 표시됩니다.<br />
</div>
<div v-else-if="commandName == '이호경식'">
선택된 국가에 이호경식을 발동합니다.<br />
선포, 전쟁중인 상대국에만 가능합니다.<br />
상대 국가를 목록에서 선택하세요.<br />
현재 이호경식이 불가능한 국가는
<span style="color: red">붉은색</span>으로 표시됩니다.<br />
</div>
<div v-else-if="commandName == '종전 제의'">
전쟁중인 국가에 종전을 제의합니다.<br />
제의할 국가를 목록에서 선택하세요.<br />
현재 제의가 불가능한 국가는
<span style="color: red">붉은색</span>으로 표시됩니다.<br />
</div>
<div v-else-if="commandName == '허보'">
전쟁중인 국가에 종전을 제의합니다.<br />
제의할 국가를 목록에서 선택하세요.<br />
현재 제의가 불가능한 국가는
<span style="color: red">붉은색</span>으로 표시됩니다.<br />
</div>
<div class="row">
<div class="col-6 col-md-3">
국가 :
<SelectNation :nations="nationList" v-model="selectedNationID" :searchable="searchable" />
</div>
<div class="col-4 col-md-2 d-grid">
<b-button @click="submit">{{ commandName }}</b-button>
</div>
</div>
</div>
<BottomBar :title="commandName" />
</template>
<script lang="ts">
import MapLegacyTemplate, {
MapCityParsed,
} from "@/components/MapLegacyTemplate.vue";
import SelectNation from "@/processing/SelectNation.vue";
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
import { getProcSearchable, procNationItem, procNationList } from "./processingRes";
declare const mapTheme: string;
declare const commandName: string;
declare const procRes: {
nationList: procNationList;
startYear: number;
};
export default defineComponent({
components: {
MapLegacyTemplate,
SelectNation,
TopBackBar,
BottomBar,
},
watch: {
selectedCityObj(city: MapCityParsed) {
if (!city.nationID) {
return;
}
this.selectedNationID = city.nationID;
},
},
setup() {
const nationList = new Map<number, procNationItem>();
for (const nationItem of procRes.nationList) {
nationList.set(nationItem.id, nationItem);
}
const selectedNationID = ref(procRes.nationList[0].id);
const selectedCityObj = ref(); //mapping
function selectedNation(nationID: number) {
selectedNationID.value = nationID;
}
async function submit(e: Event) {
const event = new CustomEvent<Args>("customSubmit", {
detail: {
destNationID: selectedNationID.value,
},
});
unwrap(e.target).dispatchEvent(event);
}
return {
searchable: getProcSearchable(),
startYear: procRes.startYear,
mapTheme: ref(mapTheme),
nationList: ref(nationList),
selectedCityObj,
selectedNationID,
commandName,
selectedNation,
submit,
};
},
});
</script>
+110
View File
@@ -0,0 +1,110 @@
<template>
<div class="input-group">
<b-button
v-if="maxAmount > 20000"
class="btn-sm"
@click="amount = Math.max(amount - 10000, minAmount)"
>-</b-button
>
<b-button
v-if="maxAmount > 2000"
class="btn-sm"
@click="amount = Math.max(amount - 1000, minAmount)"
>-</b-button
>
<b-button
v-if="maxAmount > 200"
class="btn-sm"
@click="amount = Math.max(amount - 100, minAmount)"
>-</b-button
>
<input
type="number"
class="form-control text-end"
:max="maxAmount"
:min="minAmount"
:step="step"
v-model="amount"
placeholder="금액"
/>
<b-dropdown right text="" class="amount-dropdown" v-if="amountGuide">
<b-dropdown-item
v-for="guide in amountGuide"
:key="guide"
@click="amount = guide"
><div class="text-end">
{{ guide.toLocaleString() }}
</div></b-dropdown-item
>
</b-dropdown>
<b-button
v-if="maxAmount > 200"
class="btn-sm"
@click="amount = Math.min(amount + 100, maxAmount)"
>+</b-button
>
<b-button
v-if="maxAmount > 2000"
class="btn-sm"
@click="amount = Math.min(amount + 1000, maxAmount)"
>+</b-button
>
<b-button
v-if="maxAmount > 20000"
class="btn-sm"
@click="amount = Math.min(amount + 10000, maxAmount)"
>+</b-button
>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType } from "vue";
export default defineComponent({
props: {
modelValue: {
type: Number,
required: true,
},
minAmount: {
type: Number,
required: true,
},
maxAmount: {
type: Number,
required: true,
},
amountGuide: {
type: Array as PropType<number[]>,
required: false,
},
step: {
type: Number,
required: false,
default: 1,
},
},
emits: ["update:modelValue"],
watch: {
amount(val: number) {
this.$emit("update:modelValue", val);
},
},
data() {
return {
amount: this.modelValue,
};
},
});
</script>
<style lang="scss">
.btn-group.amount-dropdown > .btn {
border-radius: 0;
}
.btn-group.amount-dropdown .dropdown-menu.show {
min-width: 6rem;
}
</style>
+102
View File
@@ -0,0 +1,102 @@
<template>
<v-multiselect
v-model="selectedCity"
:allow-empty="false"
:options="citiesForFind"
:group-select="false"
label="searchText"
track-by="value"
:show-labels="false"
selectLabel="선택(엔터)"
selectGroupLabel=""
selectedLabel="선택됨"
deselectLabel="해제(엔터)"
deselectGroupLabel=""
placeholder="도시 선택"
:maxHeight="400"
:searchable="searchable"
>
<template v-slot:option="props"
><span
:style="{
color: props.option.notAvailable ? 'red' : undefined,
}"
>
{{ props.option.title }}
<span v-if="props.option.info">({{ props.option.info }})</span> {{ props.option.notAvailable ? "(불가)" : undefined }}</span
>
</template>
<template v-slot:singleLabel="props">
<span
:style="{
color: props.option.notAvailable ? 'red' : undefined,
}"
>{{ props.option.simpleName }} {{ props.option.notAvailable ? "(불가)" : undefined }}</span>
</template>
</v-multiselect>
</template>
<script lang="ts">
import { convertSearch초성 } from "@/util/convertSearch초성";
import { defineComponent, PropType } from "vue";
type SelectedCity = {
value: number;
searchText: string;
title: string;
simpleName: string;
info?: string;
notAvailable?: boolean;
};
export default defineComponent({
props: {
modelValue: {
type: Number,
required: true,
},
cities: {
type: Map as PropType<Map<number, { name: string; info?: string }>>,
required: true,
},
searchable: {
type: Boolean,
required: false,
default: true,
},
},
emits: ["update:modelValue"],
watch: {
modelValue(val: number) {
const target = this.targets.get(val);
this.selectedCity = target;
},
selectedCity(val: SelectedCity) {
this.$emit("update:modelValue", val.value);
},
},
data() {
const citiesForFind = [];
const targets = new Map<number, SelectedCity>();
let selectedCity;
for (const [value, { name, info }] of this.cities.entries()) {
const obj: SelectedCity = {
value,
title: name,
info: info,
simpleName: name,
searchText: convertSearch초성(name).join('|'),
};
if (value == this.modelValue) {
selectedCity = obj;
}
citiesForFind.push(obj);
targets.set(value, obj);
}
return {
selectedCity,
citiesForFind,
targets,
};
},
});
</script>
+105
View File
@@ -0,0 +1,105 @@
<template>
<v-multiselect
v-model="selectedColor"
:allow-empty="false"
:options="forFind"
:group-select="false"
label="searchText"
track-by="value"
:show-labels="false"
selectLabel="선택(엔터)"
selectGroupLabel=""
selectedLabel="선택됨"
deselectLabel="해제(엔터)"
deselectGroupLabel=""
placeholder="색상 선택"
:maxHeight="400"
:searchable="false"
>
<template v-slot:option="props">
<div
:class="`sam-color-${props.option.title.slice(1)}`"
:style="{
margin: '-0.375rem -0.75rem',
}"
>
<div
class="sam-nation-own-bgcolor"
:style="{
padding: '0.545rem 0.75rem',
}"
>
{{ props.option.title }}
</div>
</div>
</template>
<template v-slot:singleLabel="props">
<div
:class="`sam-color-${props.option.title.slice(1)}`"
:style="{
margin: '-0.25rem -0.75rem',
}"
><div
class="sam-nation-own-bgcolor"
:style="{
padding: '0.30rem 0.75rem',
borderRadius: '0.25rem',
}"
>{{ props.option.title }}</div
></div
>
</template>
</v-multiselect>
</template>
<script lang="ts">
import { unwrap } from "@/util/unwrap";
import { defineComponent, PropType } from "vue";
type SelectedColor = {
value: number;
title: string;
};
export default defineComponent({
props: {
modelValue: {
type: Number,
required: true,
},
colors: {
type: Array as PropType<string[]>,
required: true,
},
},
emits: ["update:modelValue"],
watch: {
modelValue(val: number) {
const target = unwrap(this.targets.get(val));
this.selectedColor = target;
},
selectedColor(val: SelectedColor) {
this.$emit("update:modelValue", val.value);
},
},
data() {
const forFind = [];
const targets = new Map<number, SelectedColor>();
for (const [value, title] of this.colors.entries()) {
const obj: SelectedColor = {
value,
title,
};
forFind.push(obj);
targets.set(value, obj);
}
let selectedColor = forFind[0];
return {
selectedColor,
searchMode: false,
forFind,
targets,
};
},
});
</script>
+189
View File
@@ -0,0 +1,189 @@
<template>
<v-multiselect
v-model="selectedGeneral"
class="selectedGeneral"
:allow-empty="false"
:options="forFind"
:group-select="false"
:group-values="groupByNation ? 'values' : undefined"
group-label="nationID"
label="searchText"
track-by="value"
:show-labels="false"
selectLabel="선택(엔터)"
selectGroupLabel=""
selectedLabel="선택됨"
deselectLabel="해제(엔터)"
deselectGroupLabel=""
placeholder="장수 선택"
:maxHeight="400"
:searchable="searchable"
>
<template v-slot:option="props">
<div v-if="props.option.title" v-html="props.option.title"></div>
<div
v-if="props.option.$groupLabel !== undefined"
class="margin-filler"
:style="{
backgroundColor: groupByNation.get(props.option.$groupLabel).color,
color: isBrightColor(
groupByNation.get(props.option.$groupLabel).color
)
? 'black'
: 'white',
}"
>
{{ groupByNation.get(props.option.$groupLabel).name }}
</div>
</template>
<template v-slot:singleLabel="props">
{{ props.option.simpleName }} {{groupByNation?`[${groupByNation.get(props.option.obj.nationID).name}]`:undefined}}
</template>
</v-multiselect>
</template>
<script lang="ts">
import { getNpcColor } from "@/common_legacy";
import { convertSearch초성 } from "@/util/convertSearch초성";
import { isBrightColor } from "@/util/isBrightColor";
import { unwrap } from "@/util/unwrap";
import { defineComponent, PropType } from "vue";
import {
procGeneralItem,
procGeneralList,
procNationItem,
} from "./processingRes";
type SelectedGeneral = {
value: number;
searchText: string;
title: string;
simpleName: string;
obj: procGeneralItem;
};
export default defineComponent({
props: {
modelValue: {
type: Number,
required: true,
},
generals: {
type: Array as PropType<procGeneralList>,
required: true,
},
textHelper: {
type: Function as PropType<(item: procGeneralItem) => string>,
required: false,
default: undefined,
},
groupByNation: {
type: Map as PropType<Map<number, procNationItem>>,
required: false,
default: undefined,
},
searchable:{
type: Boolean,
required: false,
default: true,
}
},
emits: ["update:modelValue"],
watch: {
modelValue(val: number) {
const target = this.targets.get(val);
this.selectedGeneral = target;
},
selectedGeneral(val: SelectedGeneral) {
this.$emit("update:modelValue", val.value);
},
},
data() {
const forFind: (
| SelectedGeneral
| {
values: SelectedGeneral[];
nationID: number;
}
)[] = [];
const forFindGroup = new Map<number, SelectedGeneral[]>();
const targets = new Map<number, SelectedGeneral>();
let selectedGeneral;
for (const gen of this.generals) {
let groupArray = forFind;
if (this.groupByNation) {
const nationID = gen.nationID ?? 0;
if (!forFindGroup.has(nationID)) {
const nationItem = unwrap(this.groupByNation.get(nationID));
let tmpArr: SelectedGeneral[] = [];
forFindGroup.set(nationID, tmpArr);
groupArray = tmpArr;
forFind.push({
nationID: nationItem.id,
values: tmpArr,
});
} else {
groupArray = unwrap(forFindGroup.get(nationID));
}
}
const nameColor = getNpcColor(gen.npc);
const name = nameColor
? `<span style="color:${nameColor}">${gen.name}</span>`
: gen.name;
const obj: SelectedGeneral = {
value: gen.no,
title: this.textHelper
? this.textHelper(gen)
: `${name} (${gen.leadership}/${gen.leadership}/${gen.intel})`,
simpleName: gen.name,
searchText: convertSearch초성(gen.name).join('|'),
obj: gen,
};
if (gen.no == this.modelValue) {
selectedGeneral = obj;
}
groupArray.push(obj);
targets.set(gen.no, obj);
}
return {
selectedGeneral,
forFind,
targets,
isBrightColor,
};
},
});
</script>
<style lang="scss">
@import "@scss/common/break_500px.scss";
@import "@scss/common/variables.scss";
@import "@scss/common/bootswatch_custom_variables.scss";
@import "bootstrap/scss/bootstrap-utilities.scss";
.selectedGeneral {
$vue-multiselect-bg: $gray-700;
$vue-multiselect-color: $gray-100;
$form-select-color: $gray-100;
$text-muted: $gray-400;
$dark: $gray-100;
$light: $gray-700;
$vue-multiselect-option-selected-bg: $gray-600;
@import "@scss/common/vue-multiselect.scss";
color: $vue-multiselect-color;
input {
color: $vue-multiselect-color;
}
.multiselect__option--group {
padding: 0;
}
.margin-filler {
padding: calc($vue-multiselect-padding-y + 0.17em) $vue-multiselect-padding-x;
}
}
</style>
+108
View File
@@ -0,0 +1,108 @@
<template>
<v-multiselect
v-model="selectedNation"
:allow-empty="false"
:options="forFind"
:group-select="false"
label="searchText"
track-by="value"
:show-labels="false"
selectLabel="선택(엔터)"
selectGroupLabel=""
selectedLabel="선택됨"
deselectLabel="해제(엔터)"
deselectGroupLabel=""
placeholder="국가 선택"
:maxHeight="400"
:searchable="searchable"
>
<template v-slot:option="props">
<span
:style="{
color: props.option.notAvailable ? 'red' : undefined,
}"
>
{{ props.option.title }}
<span v-if="props.option.info">({{ props.option.info }})</span>
{{ props.option.notAvailable ? "(불가)" : undefined }}
</span>
</template>
<template v-slot:singleLabel="props">
<span
:style="{
color: props.option.notAvailable ? 'red' : undefined,
}"
>
{{ props.option.simpleName }}
{{ props.option.notAvailable ? "(불가)" : undefined }}</span
>
</template>
</v-multiselect>
</template>
<script lang="ts">
import { convertSearch초성 } from "@/util/convertSearch초성";
import { defineComponent, PropType } from "vue";
import { procNationItem } from "./processingRes";
type SelectedNation = {
value: number;
searchText: string;
title: string;
simpleName: string;
info?: string;
notAvailable?: boolean;
};
export default defineComponent({
props: {
modelValue: {
type: Number,
required: true,
},
nations: {
type: Map as PropType<Map<number, procNationItem>>,
required: true,
},
searchable: {
type: Boolean,
required: false,
default: true,
}
},
emits: ["update:modelValue"],
watch: {
modelValue(val: number) {
const target = this.targets.get(val);
this.selectedNation = target;
},
selectedNation(val: SelectedNation) {
this.$emit("update:modelValue", val.value);
},
},
data() {
const forFind = [];
const targets = new Map<number, SelectedNation>();
let selectedNation;
for (const nationItem of this.nations.values()) {
const obj: SelectedNation = {
value: nationItem.id,
title: nationItem.name,
info: nationItem.info,
simpleName: nationItem.name,
notAvailable: nationItem.notAvailable,
searchText: convertSearch초성(nationItem.name).join('|'),
};
if (nationItem.id == this.modelValue) {
selectedNation = obj;
}
forFind.push(obj);
targets.set(nationItem.id, obj);
}
return {
selectedNation,
forFind,
targets,
};
},
});
</script>
+87
View File
@@ -0,0 +1,87 @@
import { mb_strwidth } from "@/util/mb_strwidth";
import { isArray, isBoolean, isInteger, isString } from "lodash";
const stringArgs = [
'nationName', 'optionText', 'itemType', 'nationType', 'itemCode', 'commandType',
] as const;
const intArgs = [
'crewType', 'destGeneralID', 'destCityID', 'destNationID',
'amount', 'colorType',
'year', 'month',
'srcArmType', 'destArmType', //숙련전환 전용
] as const;
const booleanArgs = [
'isGold', 'buyRice',
] as const;
const integerArrayArgs = [
'destNationIDList', 'destGeneralIDList', 'amountList'
] as const;
type StringKeys = typeof stringArgs[number];
type IntKeys = typeof intArgs[number];
type BooleanKeys = typeof booleanArgs[number];
type IntegerArrayKeys = typeof integerArrayArgs[number];
export type Args = {
[key in StringKeys]?: string;
} & {
[key in IntKeys]?: number;
} & {
[key in BooleanKeys]?: boolean;
} & {
[key in IntegerArrayKeys]?: number[]
};
export function testSubmitArgs(args: Args): true | ['int' | 'string' | 'boolean' | 'int[]', keyof Args, number | string | boolean | number[]] {
for (const intKey of intArgs) {
const testVal = args[intKey];
if (testVal === undefined) {
continue;
}
if (!isInteger(testVal)) {
return ['int', intKey, testVal];
}
}
for (const stringKey of stringArgs) {
const testVal = args[stringKey];
if (testVal === undefined) {
continue;
}
if (!isString(testVal)) {
return ['string', stringKey, testVal];
}
if(stringKey == 'nationName' && mb_strwidth(testVal) > 18){
throw `길이가 반각 18자 분량을 넘었습니다.`;
}
}
for (const booleanKey of booleanArgs) {
const testVal = args[booleanKey];
if (testVal === undefined) {
continue;
}
if (!isBoolean(testVal)) {
return ['boolean', booleanKey, testVal];
}
}
for (const integerArrayKey of integerArrayArgs) {
const testVal = args[integerArrayKey];
if (testVal === undefined) {
continue;
}
if (!isArray(args[integerArrayKey])) {
return ['int[]', integerArrayKey, testVal];
}
for (const value of testVal) {
if (!isInteger(value)) {
return ['int[]', integerArrayKey, testVal];
}
}
}
return true;
}
+111
View File
@@ -0,0 +1,111 @@
import { ItemTypeKey } from "@/defs";
import { combineArray } from "@/util/combineArray";
import { Ref, ref, watch } from "vue";
export type procGeneralItem = {
no: number,
name: string,
nationID?: number,
officerLevel: number,
npc: number,
gold?: number,
rice?: number,
leadership: number,
strength: number,
intel: number,
cityID?: number,
crew?: number,
train?: number,
atmos?: number,
troopID?: number,
}
export type procGeneralList = procGeneralItem[];
export type procGeneralKey = 'no' | 'name' | 'nationID' | 'officerLevel' | 'npc' | 'gold' | 'rice' | 'leadership' | 'strength' | 'intel' | 'cityID' | 'crew' | 'train' | 'atmos' | 'troopID';
export type procGeneralRawItem = procGeneralItem[procGeneralKey][];
export type procTroopItem = {
troop_leader: number,
nation: number,
name: string
};
export type procTroopList = Record<number, procTroopItem>;
export type procGeneralRawItemList = procGeneralRawItem[];
export function convertGeneralList(keys: procGeneralKey[], rawList: procGeneralRawItemList): procGeneralList {
return combineArray(rawList, keys) as procGeneralList;
}
export type procNationItem = {
id: number,
name: string,
color: string,
power: number,
scoutMsg?: string,
info?: string,
notAvailable?: boolean,
};
export type procNationList = procNationItem[];
export type procNationTypeItem = {
type: string,
name: string,
pros: string,
cons: string,
}
export type procNationTypeList = Record<string, procNationTypeItem>;
export type procArmTypeItem = {
armType: number,
armName: string,
values: procCrewTypeItem[],
}
export type procCrewTypeItem = {
id: number,
reqTech: number,
reqYear: number,
notAvailable?: boolean,
baseRice: number,
baseCost: number,
name: string,
attack: number,
defence: number,
speed: number,
avoid: number,
img: string,
info: string[],
}
export type procItemType = {
id: string,
name: string,
reqSecu: number,
cost: number,
info: string,//<br>
isBuyable: boolean,
}
export type procItemList = Record<ItemTypeKey, {
typeName: string,
values: procItemType[],
}>
//XXX: vuex 쓰기 전까지...
export const searchableProcessingMode = 'sam.processing.searchable';
const searchable = ref((localStorage.getItem(searchableProcessingMode) ?? "0") != "0");
watch(searchable, (val) => {
localStorage.setItem(searchableProcessingMode, val ? "1" : "0");
});
export function getProcSearchable():Ref<boolean>{
return searchable;
}
-103
View File
@@ -1,103 +0,0 @@
import { unwrap_any } from '@util/unwrap_any';
declare const leadership: number;
declare const fullLeadership: number;
declare const currentCrewType: number;
declare const currentCrew: number;
//declare const currentGold: number;
declare const is모병 = false;
function calc(id: number) {
const $obj = $(`#crewType${id}`);
const crew = parseInt(unwrap_any<string>($obj.find('.form_double').val()));
const baseCost = $obj.data('cost');
const $cost = $obj.find('.form_cost');
let cost = crew * baseCost;
if (is모병) {
cost *= 2;
}
$cost.val(Math.round(cost));
}
export function recruitCrewForm(): void {
const $formAmount = $('#amount');
const $formCrewtype = $('#crewType');
$('.form_double').on('keyup change', function (e) {
const $this = $(this);
const $parent = $this.parents('.input_form');
const crewtype = parseInt($parent.data('crewtype'));
calc(crewtype);
$formCrewtype.val(crewtype);
$formAmount.val(parseFloat(unwrap_any<string>($this.val())) * 100);
if (e.which === 13) {
void window.submitAction();
}
return false;
});
$('.btn_half').on('click', function () {
const $this = $(this);
const $parent = $this.closest('.input_form');
const crewtype = parseInt($parent.data('crewtype'));
const $input = $parent.find('.form_double:eq(0)');
const fillValue = Math.round(leadership / 2);
$formCrewtype.val(crewtype);
$input.val(fillValue).change();
return false;
});
$('.btn_fill').on('click', function () {
const $this = $(this);
const $parent = $this.closest('.input_form');
const crewtype = parseInt($parent.data('crewtype'));
const $input = $parent.find('.form_double:eq(0)');
let fillValue = Math.ceil((leadership * 100 - currentCrew) / 100);
if (crewtype != currentCrewType) {
fillValue = leadership;
}
$formCrewtype.val(crewtype);
$input.val(fillValue).change();
return false;
});
$('.btn_full').on('click', function () {
const $this = $(this);
const $parent = $this.closest('.input_form');
const crewtype = parseInt($parent.data('crewtype'));
const $input = $parent.find('.form_double:eq(0)');
const fillValue = fullLeadership + 15;
$formCrewtype.val(crewtype);
$input.val(fillValue).change();
return false;
});
$('.submit_btn').on('click', function () {
const $this = $(this);
const $parent = $this.closest('tr').find('.input_form');
const crewtype = parseInt($parent.data('crewtype'));
const $input = $parent.find('.form_double');
$formCrewtype.val(crewtype);
$formAmount.val(parseFloat(unwrap_any<string>($input.val())) * 100);
void window.submitAction();
});
$('.btn_fill').click();
$('#show_unavailable_troops').change(function () {
const show = $('#show_unavailable_troops').is(":checked");
if (show) {
$('.show_default_false').show();
}
else {
$('.show_default_false').hide();
}
});
$('.show_default_false').hide();
}

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