diff --git a/hwe/sammo/ActionItem/che_Dummy.php b/hwe/sammo/ActionItem/che_Dummy.php
index 99a8e83f..ba2c671b 100644
--- a/hwe/sammo/ActionItem/che_Dummy.php
+++ b/hwe/sammo/ActionItem/che_Dummy.php
@@ -10,11 +10,11 @@ use function sammo\getItemCost2;
//XXX:임시용!
class che_Dummy extends \sammo\BaseItem{
- public $id;
- public $name;
- public $info;
- public $cost;
- public $consumable = false;
+ static $id;
+ static $name;
+ static $info;
+ static $cost;
+ static $consumable = false;
public function __construct(int $itemCode)
{
diff --git a/hwe/sammo/ActionItem/che_계략_삼략.php b/hwe/sammo/ActionItem/che_계략_삼략.php
index 2c8111f0..2cefe851 100644
--- a/hwe/sammo/ActionItem/che_계략_삼략.php
+++ b/hwe/sammo/ActionItem/che_계략_삼략.php
@@ -5,11 +5,11 @@ use \sammo\General;
class che_계략_삼략 extends \sammo\BaseItem{
- public $id = 22;
- public $name = '삼략(계략)';
- public $info = '[계략] 화계·탈취·파괴·선동 : 성공률 +20%p';
- public $cost = 200;
- public $consumable = false;
+ static $id = 22;
+ static $name = '삼략(계략)';
+ static $info = '[계략] 화계·탈취·파괴·선동 : 성공률 +20%p';
+ static $cost = 200;
+ static $consumable = false;
public function onCalcDomestic(string $turnType, string $varType, float $value, $aux=null):float{
if($turnType == '계략'){
diff --git a/hwe/sammo/ActionItem/che_계략_육도.php b/hwe/sammo/ActionItem/che_계략_육도.php
index cd3783ec..f1951901 100644
--- a/hwe/sammo/ActionItem/che_계략_육도.php
+++ b/hwe/sammo/ActionItem/che_계략_육도.php
@@ -5,11 +5,11 @@ use \sammo\General;
class che_계략_육도 extends \sammo\BaseItem{
- public $id = 21;
- public $name = '육도(계략)';
- public $info = '[계략] 화계·탈취·파괴·선동 : 성공률 +20%p';
- public $cost = 200;
- public $consumable = false;
+ static $id = 21;
+ static $name = '육도(계략)';
+ static $info = '[계략] 화계·탈취·파괴·선동 : 성공률 +20%p';
+ static $cost = 200;
+ static $consumable = false;
public function onCalcDomestic(string $turnType, string $varType, float $value, $aux=null):float{
if($turnType == '계략'){
diff --git a/hwe/sammo/ActionItem/che_계략_이추.php b/hwe/sammo/ActionItem/che_계략_이추.php
index abbf7b5e..85f78f86 100644
--- a/hwe/sammo/ActionItem/che_계략_이추.php
+++ b/hwe/sammo/ActionItem/che_계략_이추.php
@@ -5,11 +5,11 @@ use \sammo\General;
class che_계략_이추 extends \sammo\BaseItem{
- public $id = 5;
- public $name = '이추(계략)';
- public $info = '[계략] 화계·탈취·파괴·선동 : 성공률 +10%p';
- public $cost = 1000;
- public $consumable = true;
+ static $id = 5;
+ static $name = '이추(계략)';
+ static $info = '[계략] 화계·탈취·파괴·선동 : 성공률 +10%p';
+ static $cost = 1000;
+ static $consumable = true;
public function onCalcDomestic(string $turnType, string $varType, float $value, $aux=null):float{
if($turnType == '계략'){
diff --git a/hwe/sammo/ActionItem/che_계략_향낭.php b/hwe/sammo/ActionItem/che_계략_향낭.php
index 2a832d6a..e4a7833a 100644
--- a/hwe/sammo/ActionItem/che_계략_향낭.php
+++ b/hwe/sammo/ActionItem/che_계략_향낭.php
@@ -5,11 +5,11 @@ use \sammo\General;
class che_계략_향낭 extends \sammo\BaseItem{
- public $id = 5;
- public $name = '항냥(계략)';
- public $info = '[계략] 화계·탈취·파괴·선동 : 성공률 +20%p';
- public $cost = 3000;
- public $consumable = true;
+ static $id = 5;
+ static $name = '항냥(계략)';
+ static $info = '[계략] 화계·탈취·파괴·선동 : 성공률 +20%p';
+ static $cost = 3000;
+ static $consumable = true;
public function onCalcDomestic(string $turnType, string $varType, float $value, $aux=null):float{
if($turnType == '계략'){
diff --git a/hwe/sammo/ActionItem/che_의술_청낭서.php b/hwe/sammo/ActionItem/che_의술_청낭서.php
index bc5df56b..b5473e82 100644
--- a/hwe/sammo/ActionItem/che_의술_청낭서.php
+++ b/hwe/sammo/ActionItem/che_의술_청낭서.php
@@ -6,11 +6,11 @@ use \sammo\GeneralTrigger;
class che_의술_청낭서 extends \sammo\BaseItem{
- public $id = 23;
- public $name = '청낭서(의술)';
- public $info = '[군사] 매 턴마다 자신(100%)과 소속 도시 장수(적 포함 50%) 부상 회복
[전투] 페이즈마다 20% 확률로 치료 발동(아군 피해 1/3 감소)';
- public $cost = 200;
- public $consumable = false;
+ static $id = 23;
+ static $name = '청낭서(의술)';
+ static $info = '[군사] 매 턴마다 자신(100%)과 소속 도시 장수(적 포함 50%) 부상 회복
[전투] 페이즈마다 20% 확률로 치료 발동(아군 피해 1/3 감소)';
+ static $cost = 200;
+ static $consumable = false;
public function getPreTurnExecuteTriggerList(General $general):?GeneralTriggerCaller{
return new GeneralTriggerCaller([
diff --git a/hwe/sammo/ActionItem/che_의술_태평청령.php b/hwe/sammo/ActionItem/che_의술_태평청령.php
index 4b9458af..3c5b0222 100644
--- a/hwe/sammo/ActionItem/che_의술_태평청령.php
+++ b/hwe/sammo/ActionItem/che_의술_태평청령.php
@@ -5,11 +5,11 @@ use \sammo\General;
class che_의술_태평청령 extends \sammo\BaseItem{
- public $id = 24;
- public $name = '태평청령(의술)';
- public $info = '[군사] 매 턴마다 자신(100%)과 소속 도시 장수(적 포함 50%) 부상 회복
[전투] 페이즈마다 20% 확률로 치료 발동(아군 피해 1/3 감소)';
- public $cost = 200;
- public $consumable = false;
+ static $id = 24;
+ static $name = '태평청령(의술)';
+ static $info = '[군사] 매 턴마다 자신(100%)과 소속 도시 장수(적 포함 50%) 부상 회복
[전투] 페이즈마다 20% 확률로 치료 발동(아군 피해 1/3 감소)';
+ static $cost = 200;
+ static $consumable = false;
public function getPreTurnExecuteTriggerList(General $general):?GeneralTriggerCaller{
return new GeneralTriggerCaller([
diff --git a/hwe/sammo/ActionItem/che_치료_도소연명.php b/hwe/sammo/ActionItem/che_치료_도소연명.php
index b22bc49d..15784da8 100644
--- a/hwe/sammo/ActionItem/che_치료_도소연명.php
+++ b/hwe/sammo/ActionItem/che_치료_도소연명.php
@@ -6,11 +6,11 @@ use \sammo\GeneralTrigger;
class che_치료_도소연명 extends \sammo\BaseItem{
- public $id = 9;
- public $name = '도소연명(치료)';
- public $info = '[군사] 턴 실행 전 부상 회복.';
- public $cost = 200;
- public $consumable = false;
+ static $id = 9;
+ static $name = '도소연명(치료)';
+ static $info = '[군사] 턴 실행 전 부상 회복.';
+ static $cost = 200;
+ static $consumable = false;
public function getPreTurnExecuteTriggerList(General $general):?GeneralTriggerCaller{
return new GeneralTriggerCaller([
diff --git a/hwe/sammo/ActionItem/che_치료_무후행군.php b/hwe/sammo/ActionItem/che_치료_무후행군.php
index bd4adaf4..f4ca04d1 100644
--- a/hwe/sammo/ActionItem/che_치료_무후행군.php
+++ b/hwe/sammo/ActionItem/che_치료_무후행군.php
@@ -6,11 +6,11 @@ use \sammo\GeneralTrigger;
class che_치료_무후행군 extends \sammo\BaseItem{
- public $id = 8;
- public $name = '무후행군(치료)';
- public $info = '[군사] 턴 실행 전 부상 회복.';
- public $cost = 200;
- public $consumable = false;
+ static $id = 8;
+ static $name = '무후행군(치료)';
+ static $info = '[군사] 턴 실행 전 부상 회복.';
+ static $cost = 200;
+ static $consumable = false;
public function getPreTurnExecuteTriggerList(General $general):?GeneralTriggerCaller{
return new GeneralTriggerCaller([
diff --git a/hwe/sammo/ActionItem/che_치료_오석산.php b/hwe/sammo/ActionItem/che_치료_오석산.php
index 112886cc..2d3ad3fa 100644
--- a/hwe/sammo/ActionItem/che_치료_오석산.php
+++ b/hwe/sammo/ActionItem/che_치료_오석산.php
@@ -6,11 +6,11 @@ use \sammo\GeneralTrigger;
class che_치료_오석산 extends \sammo\BaseItem{
- public $id = 7;
- public $name = '오석산(치료)';
- public $info = '[군사] 턴 실행 전 부상 회복.';
- public $cost = 200;
- public $consumable = false;
+ static $id = 7;
+ static $name = '오석산(치료)';
+ static $info = '[군사] 턴 실행 전 부상 회복.';
+ static $cost = 200;
+ static $consumable = false;
public function getPreTurnExecuteTriggerList(General $general):?GeneralTriggerCaller{
return new GeneralTriggerCaller([
diff --git a/hwe/sammo/ActionItem/che_치료_정력견혈.php b/hwe/sammo/ActionItem/che_치료_정력견혈.php
index 64257552..a229ebcd 100644
--- a/hwe/sammo/ActionItem/che_치료_정력견혈.php
+++ b/hwe/sammo/ActionItem/che_치료_정력견혈.php
@@ -6,11 +6,11 @@ use \sammo\GeneralTrigger;
class che_치료_정력견혈 extends \sammo\BaseItem{
- public $id = 11;
- public $name = '정력견혈(치료)';
- public $info = '[군사] 턴 실행 전 부상 회복.';
- public $cost = 200;
- public $consumable = false;
+ static $id = 11;
+ static $name = '정력견혈(치료)';
+ static $info = '[군사] 턴 실행 전 부상 회복.';
+ static $cost = 200;
+ static $consumable = false;
public function getPreTurnExecuteTriggerList(General $general):?GeneralTriggerCaller{
return new GeneralTriggerCaller([
diff --git a/hwe/sammo/ActionItem/che_치료_칠엽청점.php b/hwe/sammo/ActionItem/che_치료_칠엽청점.php
index 83b5f09a..cdd0be39 100644
--- a/hwe/sammo/ActionItem/che_치료_칠엽청점.php
+++ b/hwe/sammo/ActionItem/che_치료_칠엽청점.php
@@ -6,11 +6,11 @@ use \sammo\GeneralTrigger;
class che_치료_칠엽청점 extends \sammo\BaseItem{
- public $id = 10;
- public $name = '칠엽청점(치료)';
- public $info = '[군사] 턴 실행 전 부상 회복.';
- public $cost = 200;
- public $consumable = false;
+ static $id = 10;
+ static $name = '칠엽청점(치료)';
+ static $info = '[군사] 턴 실행 전 부상 회복.';
+ static $cost = 200;
+ static $consumable = false;
public function getPreTurnExecuteTriggerList(General $general):?GeneralTriggerCaller{
return new GeneralTriggerCaller([
diff --git a/hwe/sammo/ActionItem/che_치료_환약.php b/hwe/sammo/ActionItem/che_치료_환약.php
index 64f1abce..3b2f509d 100644
--- a/hwe/sammo/ActionItem/che_치료_환약.php
+++ b/hwe/sammo/ActionItem/che_치료_환약.php
@@ -6,11 +6,11 @@ use \sammo\GeneralTrigger;
class che_치료_환약 extends \sammo\BaseItem{
- public $id = 1;
- public $name = '환약(치료)';
- public $info = '[군사] 턴 실행 전 부상 회복. 1회용';
- public $cost = 100;
- public $consumable = true;
+ static $id = 1;
+ static $name = '환약(치료)';
+ static $info = '[군사] 턴 실행 전 부상 회복. 1회용';
+ static $cost = 100;
+ static $consumable = true;
public function getPreTurnExecuteTriggerList(General $general):?GeneralTriggerCaller{
return new GeneralTriggerCaller([
diff --git a/hwe/sammo/BaseItem.php b/hwe/sammo/BaseItem.php
index a8ae85e3..f16072d8 100644
--- a/hwe/sammo/BaseItem.php
+++ b/hwe/sammo/BaseItem.php
@@ -10,6 +10,7 @@ class BaseItem implements iAction{
static $id = 0;
static $name = '-';
static $info = '';
+ static $cost = null;
static $consumable = false;
function isValidTurnItem(string $actionType, string $command):bool{
diff --git a/hwe/sammo/Command/General/che_정착장려.php b/hwe/sammo/Command/General/che_정착장려.php
index 27047cda..55e0b181 100644
--- a/hwe/sammo/Command/General/che_정착장려.php
+++ b/hwe/sammo/Command/General/che_정착장려.php
@@ -20,7 +20,6 @@ use function \sammo\{
use \sammo\Constraint\Constraint;
use \sammo\Constraint\ConstraintHelper;
-use function sammo\getGeneralLeadership;
class che_정착장려 extends Command\GeneralCommand{
diff --git a/hwe/sammo/Command/General/che_화계.php b/hwe/sammo/Command/General/che_화계.php
new file mode 100644
index 00000000..56ac5293
--- /dev/null
+++ b/hwe/sammo/Command/General/che_화계.php
@@ -0,0 +1,254 @@
+arg)){
+ return false;
+ }
+ if(!key_exists($this->arg['destCityID'], CityConst::all())){
+ return false;
+ }
+ $this->arg = [
+ 'destCityID'=>$this->arg['destCityID']
+ ];
+ return true;
+ }
+
+ protected function calcSabotageAttackProb():float{
+ $statType = static::$statType;
+ $general = $this->generalObj;
+ $nation = $this->nation;
+
+ if($statType === 'leader'){
+ $genScore = $general->getLeadership();
+ }
+ else if($statType === 'power'){
+ $genScore = $general->getPower();
+ }
+ else if($statType === 'intel'){
+ $genScore = $general->getIntel();
+ }
+ else{
+ throw new MustNotBeReachedException();
+ }
+
+ $prob = $genScore / GameConst::$sabotageProbCoefByStat;
+ $prob = $general->onCalcDomestic('계략', 'success', $prob);
+ return $prob;
+ }
+
+ protected function calcSabotageDefenceProb(array $destCityGeneralList):float{
+ $statType = static::$statType;
+ $destCity = $this->destCity;
+ $destNation = $this->destNation;
+ $destNationID = $destNation['nation'];
+
+ $maxGenScore = 0;
+ foreach($destCityGeneralList as $destGeneral){
+ /** @var General $destGeneral */
+ if($destGeneral->getNationID() != $destNationID){
+ continue;
+ }
+
+ if($statType === 'leader'){
+ $genScore = $destGeneral->getLeadership();
+ }
+ else if($statType === 'power'){
+ $genScore = $destGeneral->getPower();
+ }
+ else if($statType === 'intel'){
+ $genScore = $destGeneral->getIntel();
+ }
+ else{
+ throw new MustNotBeReachedException();
+ }
+ $maxGenScore = max($maxGenScore, $genScore);
+ }
+
+ $prob = $maxGenScore / GameConst::$sabotageProbCoefByStat;
+
+ $prob += $city['secu'] / $city['secu2'] / 5; //최대 20%p
+ $prob += $city['supply'] ? 0.1 : 0;
+ return $prob;
+ }
+
+ protected function init(){
+
+ $general = $this->generalObj;
+
+ $this->setCity();
+ $this->setDestCity($this->arg['destCityID'], null); //xxx: 이대로라면 메인 페이지 갱신시마다 DB query를 하게 된다.
+ $this->setDestNation($this->destCity['nation']);
+
+ [$reqGold, $reqRice] = $this->getCost();
+
+ $this->runnableConstraints=[
+ ConstraintHelper::NotBeNeutral(),
+ ConstraintHelper::OccupiedCity(),
+ ConstraintHelper::SuppliedCity(),
+ ConstraintHelper::NotOccupiedDestCity(),
+ ConstraintHelper::NotNeutralDestCity(),
+ ConstraintHelper::ReqGeneralGold($reqGold),
+ ConstraintHelper::ReqGeneralRice($reqRice),
+ ConstraintHelper::DisallowDiplomacyStatus([7], '불가침국입니다.'),
+ ];
+ }
+
+ public function getCost():array{
+ $env = $this->env;
+ $cost = $env['develcost'] * 5;
+ return [$cost, $cost];
+ }
+
+ public function getPreReqTurn():int{
+ return 0;
+ }
+
+ public function getPostReqTurn():int{
+ return 0;
+ }
+
+ public function getFailString():string{
+ $commandName = $this->getName();
+ $failReason = $this->testRunnable();
+ if($failReason === null){
+ throw new \RuntimeException('실행 가능한 커맨드에 대해 실패 이유를 수집');
+ }
+ $destCityName = CityConst::byID($this->arg['destCityID'])->name;
+ return "{$failReason} {$destCityName}>에 {$commandName} 실패.";
+ }
+
+ protected function affectDestCity(int $injuryCount){
+
+ }
+
+ public function run():bool{
+ if(!$this->isRunnable()){
+ throw new \RuntimeException('불가능한 커맨드를 강제로 실행 시도');
+ }
+
+ $db = DB::db();
+ $env = $this->env;
+
+ $general = $this->generalObj;
+ $date = substr($general->getVar('turntime'),11,5);
+
+ $destCity = $this->destCity;
+
+ $destCityName = $destCity['name'];
+ $destCityID = $destCity['city'];
+ $destNationID = $destCity['nation'];
+ $josaUl = JosaUtil::pick($destCityName, '을');
+
+ $commandName = $this->getName();
+ $statType = static::$statType;
+
+ $logger = $general->getLogger();
+
+ $dist = searchDistance($general->getCityID(), 5, false)[$destCityID]??99;
+
+ $destCityGeneralList = [];
+
+ [$year, $month] = [$env['year'], $env['month']];
+
+ foreach($db->query(
+ 'SELECT `no`,name,city,nation,level,leader,horse,power,weap,intel,book,item,last_turn,injury,special,special2 FROM general WHERE city = %i',
+ $destCityID,
+ $destNationID
+ ) as $rawDestCityGeneral){
+ $destCityGeneralList[] = new General($rawDestGeneral, $destCity, $year, $month, true);
+ //계략에 성공할 경우 logger를 사용해야 하므로 해야하므로, 미리 초기화한다.
+ //실패하면 날리는거지 뭐~
+ };
+
+ $prob = GameConst::$sabotageDefaultProb + $this->calcSabotageAttackProb() - $this->calcSabotageDefenceProb($destCityGeneralList);
+ $prob /= $dist[$destCityID];
+
+ if(!Util::randBool($prob)){
+ $josaYi = JosaUtil::pick($commandName, '이');
+ $logger->pushGeneralActionLog("{$destCityName}>에 {$commandName}{$josaYi} 실패했습니다. <1>$date>");
+
+ $exp = Util::randRangeInt(1, 100);
+ $ded = Util::randRangeInt(1, 70);
+
+ $exp = $general->onPreGeneralStatUpdate($general, 'experience', $exp);
+ $ded = $general->onPreGeneralStatUpdate($general, 'dedication', $ded);
+
+ [$reqGold, $reqRice] = $this->getCost();
+ $general->increaseVarWithLimit('gold', -$reqGold, 0);
+ $general->increaseVarWithLimit('rice', -$reqRice, 0);
+ $general->increaseVar('experience', $exp);
+ $general->increaseVar('dedication', $ded);
+ $general->increaseVar($statType.'2', 1);
+
+ $general->setResultTurn(new LastTurn(static::getName(), $this->arg));
+ $general->checkStatChange();
+ $general->applyDB($db);
+ return false;
+ }
+
+ if(static::$injuryGeneral){
+ $injuryCount = \sammo\SabotageInjuryEx($destCityGeneralList, true);
+ }
+ else{
+ $injuryCount = 0;
+ }
+
+ $this->affectDestCity($injuryCount);
+
+ $itemObj = $general->getItem();
+ if($itemObj->isValidTurnItem('GeneralCommand', '계략') && $itemObj::$consumable){
+ $itemName = $itemObj->$name;
+ $josaUl = JosaUtil::pick($itemName, '을');
+ $logger->pushGeneralActionLog("{$itemName}>{$josaUl} 사용!", ActionLogger::PLAIN);
+ $general->deleteItem();
+ }
+
+ $exp = Util::randRangeInt(201, 300);
+ $ded = Util::randRangeInt(141, 210);
+
+ $exp = $general->onPreGeneralStatUpdate($general, 'experience', $exp);
+ $ded = $general->onPreGeneralStatUpdate($general, 'dedication', $ded);
+
+ [$reqGold, $reqRice] = $this->getCost();
+ $general->increaseVarWithLimit('gold', -$reqGold, 0);
+ $general->increaseVarWithLimit('rice', -$reqRice, 0);
+ $general->increaseVar('experience', $exp);
+ $general->increaseVar('dedication', $ded);
+ $general->increaseVar($statType.'2', 1);
+ $general->increaseVar('firenum', 1);
+ $general->setResultTurn(new LastTurn(static::getName(), $this->arg));
+ $general->checkStatChange();
+ $general->applyDB($db);
+
+ return true;
+ }
+
+
+}
\ No newline at end of file