diff --git a/hwe/func_command.php b/hwe/func_command.php
index 8b20b672..7ad5c7f5 100644
--- a/hwe/func_command.php
+++ b/hwe/func_command.php
@@ -217,6 +217,7 @@ function checkCommandArg(?array $arg):?string{
'integer'=>[
'crewType', 'destGeneralID', 'destCityID', 'destNationID',
'amount', 'colorType',
+ 'srcArmType', 'destArmType', //숙련전환 전용
],
'boolean'=>[
'isGold', 'buyRice',
diff --git a/hwe/js/processing.js b/hwe/js/processing.js
index aeeeeda4..365866da 100644
--- a/hwe/js/processing.js
+++ b/hwe/js/processing.js
@@ -43,6 +43,7 @@ window.submitAction = function(){
'crewType', 'destGeneralID', 'destCityID', 'destNationID',
'amount', 'colorType',
'year', 'month',
+ 'srcArmType', 'destArmType', //숙련전환 전용
],
'boolean':[
'isGold', 'buyRice',
diff --git a/hwe/sammo/Command/General/che_숙련전환.php b/hwe/sammo/Command/General/che_숙련전환.php
new file mode 100644
index 00000000..a9ad4278
--- /dev/null
+++ b/hwe/sammo/Command/General/che_숙련전환.php
@@ -0,0 +1,217 @@
+arg === null){
+ return false;
+ }
+ if(!key_exists('srcArmType', $this->arg)){
+ return false;
+ }
+ if(!key_exists('destArmType', $this->arg)){
+ return false;
+ }
+ $srcArmType = $this->arg['srcArmType'];
+ $destArmType = $this->arg['destArmType'];
+
+ if(!is_int($srcArmType)){
+ return false;
+ }
+ if(!key_exists($srcArmType, GameUnitConst::allType())){
+ return false;
+ }
+
+ if(!is_int($destArmType)){
+ return false;
+ }
+ if(!key_exists($destArmType, GameUnitConst::allType())){
+ return false;
+ }
+
+ if($srcArmType === $destArmType){
+ return false;
+ }
+
+ $this->arg = [
+ 'srcArmType'=>$srcArmType,
+ 'destArmType'=>$destArmType
+ ];
+ return true;
+ }
+
+ protected function init(){
+ $general = $this->generalObj;
+
+ $this->setCity();
+ $this->setNation();
+
+ $this->srcArmType = $this->arg['srcArmType'];
+ $this->srcArmTypeName = GameUnitConst::allType()[$this->srcArmType];
+ $this->destArmType = $this->arg['destArmType'];
+ $this->destArmTypeName = GameUnitConst::allType()[$this->destArmType];
+
+
+ [$reqGold, $reqRice] = $this->getCost();
+
+ $this->runnableConstraints=[
+ ConstraintHelper::NotBeNeutral(),
+ ConstraintHelper::OccupiedCity(),
+ ConstraintHelper::ReqGeneralGold($reqGold),
+ ConstraintHelper::ReqGeneralRice($reqRice),
+ ];
+
+ }
+
+ public function getBrief():string{
+ return "【{$this->srcArmTypeName}】숙련을 【{$this->destArmTypeName}】숙련으로 전환";
+
+ }
+
+ public function getCommandDetailTitle():string{
+ $name = $this->getName();
+ [$reqGold, $reqRice] = $this->getCost();
+
+ $title = "{$name}(통솔경험";
+ if($reqGold > 0){
+ $title .= ", 자금{$reqGold}";
+ }
+ if($reqRice > 0){
+ $title .= ", 군량{$reqRice}";
+ }
+ $title .= ')';
+ return $title;
+ }
+
+ public function getCost():array{
+ $env = $this->env;
+ return [$env['develcost'], $env['develcost']];
+ }
+
+ public function getPreReqTurn():int{
+ return 0;
+ }
+
+ public function getPostReqTurn():int{
+ return 0;
+ }
+
+ public function run():bool{
+ if(!$this->isRunnable()){
+ throw new \RuntimeException('불가능한 커맨드를 강제로 실행 시도');
+ }
+
+ $db = DB::db();
+
+ $general = $this->generalObj;
+ $date = $general->getTurnTime($general::TURNTIME_HM);
+
+ $logger = $general->getLogger();
+
+ $srcDex = $general->getVar('dex'.$this->srcArmType);
+ $cutDex = Util::toInt($srcDex * 0.3);
+ $cutDexText = number_format($cutDex);
+ $addDex = Util::toInt($cutDex * 2 / 3);
+ $addDexText = number_format($addDex);
+
+ $general->increaseVar('dex'.$this->srcArmType, -$cutDex);
+ $general->increaseVar('dex'.$this->destArmType, $addDex);
+
+ $josaUl = JosaUtil::pick($cutDex, '을');
+ $josaRo = JosaUtil::pick($addDex, '로');
+
+ [$reqGold, $reqRice] = $this->getCost();
+
+ $logger->pushGeneralActionLog("{$this->srcArmTypeName} 숙련 {$cutDexText}{$josaUl} {$this->destArmTypeName} 숙련 {$addDexText}{$josaRo} 전환했습니다. <1>$date>");
+
+ $general->addExperience(10);
+ $general->increaseVarWithLimit('gold', -$reqGold, 0);
+ $general->increaseVarWithLimit('rice', -$reqRice, 0);
+ $general->increaseVar('leadership_exp', 2);
+ $general->setResultTurn(new LastTurn(static::getName(), $this->arg));
+ $general->checkStatChange();
+ tryUniqueItemLottery($general);
+ $general->applyDB($db);
+
+ return true;
+ }
+
+ public function getForm(): string
+ {
+ $db = DB::db();
+
+ $general = $this->generalObj;
+
+ $dexSrcTexts = [];
+ $dexDestTexts = [];
+ foreach(GameUnitConst::allType() as $armType => $armName){
+ $dexVal = $general->getVar('dex'.$armType);
+ $dexValText = number_format($dexVal);
+ $cutDex = Util::toInt($dexVal * 0.3);
+ $addDex = Util::toInt($cutDex * 2 / 3);
+ $addDexText = number_format($addDex);
+ $newDex = $dexVal - $cutDex;
+ $beforeDexLevel = getDexCall($dexVal);
+ $afterDexLevel = getDexCall($newDex);
+
+ $dexSrcTexts[$armType] = "{$armName} ({$beforeDexLevel} ⇒ {$afterDexLevel}, {$addDexText} 전환)";
+ $dexDestTexts[$armType] = "{$armName} ({$dexValText})";
+ }
+
+ ob_start();
+?>
+본인의 특정 병종 숙련을 30% 줄이고, 줄어든 숙련 중 2/3(20%p)를 다른 병종 숙련으로 전환합니다.
+
+
+ 숙련을
+
+
+ 숙련으로
+
+