diff --git a/hwe/battle_simulator.php b/hwe/battle_simulator.php
index 7c1f79ac..000f0d5c 100644
--- a/hwe/battle_simulator.php
+++ b/hwe/battle_simulator.php
@@ -439,7 +439,7 @@ var nation = =Json::encode($nation)?>;
-
+
diff --git a/hwe/j_export_simulator_object.php b/hwe/j_export_simulator_object.php
index 10a734a9..fb352452 100644
--- a/hwe/j_export_simulator_object.php
+++ b/hwe/j_export_simulator_object.php
@@ -32,11 +32,16 @@ $reqColumns = [
'leadership', 'horse', 'strength', 'weapon', 'intel', 'book', 'item',
'rice', 'personal', 'special2',
'crew', 'crewtype',
- 'atmos', 'train', 'dex1', 'dex2', 'dex3', 'dex4', 'dex5', 'defence_train'
+ 'atmos', 'train', 'dex1', 'dex2', 'dex3', 'dex4', 'dex5', 'defence_train',
+];
+
+$reqRankColumns = [
+ 'warnum', 'killnum', 'killcrew'
];
$dummyItems = [
'officer_level'=>1,
+ 'horse'=>'None',
'weapon'=>'None',
'book'=>'None',
'item'=>'None',
@@ -50,7 +55,10 @@ $dummyItems = [
'dex3'=>0,
'dex4'=>0,
'dex5'=>0,
- 'defence_train'=>80
+ 'defence_train'=>80,
+ 'warnum'=>0,
+ 'killnum'=>0,
+ 'killcrew'=>0,
];
$rawDestGeneral = $db->queryFirstRow('SELECT %l FROM general WHERE no=%i', Util::formatListOfBackticks($reqColumns), $destGeneralID);
@@ -60,6 +68,12 @@ if($nationID == 0 || $rawDestGeneral['nation'] != $nationID){
$rawDestGeneral[$key] = $val;
}
}
+else{
+ $rawRankValue = $db->queryAllLists('SELECT `type`, `value` FROM rank_data WHERE general_id = %i AND `type` IN %ls', $destGeneralID, $reqRankColumns);
+ foreach($rawRankValue as [$rankType, $rankValue]){
+ $rawDestGeneral[$rankType] = $rankValue;
+ }
+}
foreach(['dex1', 'dex2', 'dex3', 'dex4', 'dex5'] as $dexKey){
$dex = $rawDestGeneral[$dexKey];
diff --git a/hwe/j_simulate_battle.php b/hwe/j_simulate_battle.php
index 1a36a687..89edb9cc 100644
--- a/hwe/j_simulate_battle.php
+++ b/hwe/j_simulate_battle.php
@@ -1,451 +1,465 @@
-setReadOnly();
-$userID = Session::getUserID();
-
-increaseRefresh("시뮬레이터", 0);
-
-$query = Util::getPost('query');
-if($query === null){
- Json::die([
- 'result'=>false,
- 'reason'=>'입력값이 없습니다.'
- ]);
-}
-
-$action = Util::getPost('action');
-if($action === null || !in_array($action, ['reorder', 'battle'])){
- Json::die([
- 'result'=>false,
- 'reason'=>'원하는 동작이 지정되지 않았습니다.'
- ]);
-}
-
-$query = Json::decode($query);
-if($query === null){
- Json::die([
- 'result'=>false,
- 'reason'=>'올바르지 않은 JSON입니다.'
- ]);
-}
-
-$defaultCheck = [
- 'required'=>[
- 'attackerGeneral', 'attackerCity', 'attackerNation',
- 'defenderGenerals', 'defenderCity', 'defenderNation',
- 'year', 'month', 'repeatCnt'
- ],
- 'integer'=>[
- 'year','month','repeatCnt'
- ],
- 'between'=>[
- ['month', [1, 12]]
- ],
- 'in'=>[
- ['repeatCnt', [1, 1000]]
- ],
- 'min'=>[
- ['year', 0]
- ],
- 'array'=>[
- 'attackerGeneral', 'attackerCity', 'attackerNation',
- 'defenderGenerals', 'defenderCity', 'defenderNation'
- ],
-];
-
-$v = new Validator($query);
-$v->rules($defaultCheck);
-if(!$v->validate()){
- Json::die([
- 'result'=>false,
- 'reason'=>$v->errorStr()
- ]);
-}
-
-$year = $query['year'];
-$month = $query['month'];
-$repeatCnt = $query['repeatCnt'];
-
-$rawAttacker = $query['attackerGeneral'];
-$rawAttacker['turntime'] = TimeUtil::now();
-$rawAttackerCity = $query['attackerCity'];
-$rawAttackerNation = $query['attackerNation'];
-
-$rawDefenderList = $query['defenderGenerals'];
-$rawDefenderCity = $query['defenderCity'];
-$rawDefenderNation = $query['defenderNation'];
-
-
-$generalCheck = [
- 'required'=>[
- 'no', 'name', 'nation', 'turntime', 'personal', 'special2', 'crew', 'crewtype', 'atmos', 'train',
- 'intel', 'intel_exp', 'book', 'strength', 'strength_exp', 'weapon', 'injury', 'leadership', 'leadership_exp', 'horse', 'item',
- 'explevel', 'experience', 'dedication', 'officer_level', 'officer_city', 'gold', 'rice', 'dex1', 'dex2', 'dex3', 'dex4', 'dex5',
- 'recent_war'
- ],
- 'integer'=>[
- 'no', 'nation', 'crew', 'crewtype', 'atmos', 'train',
- 'intel', 'intel_exp', 'strength', 'strength_exp', 'injury', 'leadership', 'leadership_exp',
- 'explevel', 'experience', 'dedication', 'officer_level', 'officer_city', 'gold', 'rice', 'dex1', 'dex2', 'dex3', 'dex4', 'dex5',
- ],
- 'min'=>[
- ['no', 1],
- ['nation', 1],
- ['crew', 0],
- ['intel', 0],
- ['strength', 0],
- ['leadership', 0],
- ['experience', 0],
- ['gold', 0],
- ['rice', 0],
- ['dex1', 0],
- ['dex2', 0],
- ['dex3', 0],
- ['dex4', 0],
- ['dex5', 0],
- ],
- 'between'=>[
- ['train', [40, GameConst::$maxTrainByWar]],
- ['atmos', [40, GameConst::$maxAtmosByWar]],
- ['explevel', [0, 300]],
- ['injury', [0, 80]],
- ['officer_level', [1, 12]]
- ],
- 'in'=>[
- ['personal', array_merge(GameConst::$availablePersonality, GameConst::$optionalPersonality)],
- ['special2', array_merge(GameConst::$availableSpecialWar, GameConst::$optionalSpecialWar)],
- ['crewtype', array_keys(GameUnitConst::all())],
- ['horse', array_merge(array_keys(GameConst::$allItems['horse']), ['None'])],
- ['weapon', array_merge(array_keys(GameConst::$allItems['weapon']), ['None'])],
- ['book', array_merge(array_keys(GameConst::$allItems['book']), ['None'])],
- ['item', array_merge(array_keys(GameConst::$allItems['item']), ['None'])],
- ]
-];
-
-$v = new Validator($rawAttacker);
-$v->rules($generalCheck);
-if(!$v->validate()){
- Json::die([
- 'result'=>false,
- 'reason'=>'[출병자]'.$v->errorStr()
- ]);
-}
-
-$defenderList = [];
-foreach($rawDefenderList as $idx=>$rawDefenderGeneral){
- $v = new Validator($rawDefenderGeneral);
- $v->rules($generalCheck);
- if(!$v->validate()){
- $idx+=1;
- Json::die([
- 'result'=>false,
- 'reason'=>"[수비자{$idx}]".$v->errorStr()
- ]);
- }
- $defenderList[] = new General($rawDefenderGeneral, null, $rawDefenderCity, $rawAttackerNation, $year, $month, true);
-}
-
-
-$cityCheck = [
- 'required'=>[
- 'city', 'nation', 'supply', 'name',
- 'pop', 'agri', 'comm', 'secu', 'def', 'wall',
- 'trust', 'level',
- 'pop_max', 'agri_max', 'comm_max', 'secu_max', 'def_max', 'wall_max',
- 'dead', 'state', 'conflict',
- ],
- 'numeric'=>[
- 'pop', 'agri', 'comm', 'secu', 'def', 'wall', 'trust', 'dead'
- ],
- 'integer'=>[
- 'city', 'nation', 'supply',
- 'pop_max', 'agri_max', 'comm_max', 'secu_max', 'def_max', 'wall_max',
- 'state',
- ],
- 'min'=>[
- ['def', 0],
- ['wall', 0],
- ['trust', 0],
- ['pop', 0],
- ['comm', 0],
- ['secu', 0],
- ['city', 1],
- ['nation', 0]
- ],
- 'in'=>[
- ['level', array_keys(getCityLevelList())]
- ]
-];
-
-$v = new Validator($rawAttackerCity);
-$v->rules($cityCheck);
-if(!$v->validate()){
- Json::die([
- 'result'=>false,
- 'reason'=>'[출병도시]'.$v->errorStr()
- ]);
-}
-
-$v = new Validator($rawDefenderCity);
-$v->rules($cityCheck);
-if(!$v->validate()){
- Json::die([
- 'result'=>false,
- 'reason'=>'[수비도시]'.$v->errorStr()
- ]);
-}
-
-$nationCheck = [
- 'required'=>[
- 'type', 'tech', 'level', 'capital',
- 'nation', 'name', 'gold', 'rice', 'gennum'
- ],
- 'integer'=>[
- 'level', 'capital', 'nation', 'gennum',
- ],
- 'numeric'=>[
- 'tech', 'gold', 'rice'
- ],
- 'min'=>[
- ['tech', 0],
- ['gold', 0],
- ['rice', 0],
- ['gennum', 1],
- ],
- 'in'=>[
- ['type', GameConst::$availableNationType],
- ['level', array_keys(getNationLevelList())]
- ]
-];
-
-$v = new Validator($rawAttackerNation);
-$v->rules($nationCheck);
-if(!$v->validate()){
- Json::die([
- 'result'=>false,
- 'reason'=>'[출병국]'.$v->errorStr()
- ]);
-}
-
-$v = new Validator($rawDefenderNation);
-$v->rules($nationCheck);
-if(!$v->validate()){
- Json::die([
- 'result'=>false,
- 'reason'=>'[수비국]'.$v->errorStr()
- ]);
-}
-
-if($action == 'reorder'){
- usort($defenderList, function(General $lhs, General $rhs){
- return -(extractBattleOrder($lhs) <=> extractBattleOrder($rhs));
- });
-
- $order = [];
- foreach($defenderList as $defenderGeneral){
- $order[] = $defenderGeneral->getID();
- }
-
- Json::die([
- 'result'=>true,
- 'reason'=>'success',
- 'order'=>$order
- ]);
-}
-
-usort($defenderList, function(General $lhs, General $rhs){
- return -(extractBattleOrder($lhs) <=> extractBattleOrder($rhs));
-});
-
-$rawDefenderList = array_map(function(General $general){
- return $general->getRaw();
-}, $defenderList);
-unset($defenderList);
-
-$db = DB::db();
-$gameStor = KVStorage::getStorage($db, 'game_env');
-$startYear = $gameStor->startyear;
-$cityRate = Util::round(($year - $startYear) / 1.5) + 60;
-
-
-function simulateBattle(
- $rawAttacker, $rawAttackerCity, $rawAttackerNation,
- $rawDefenderList, $rawDefenderCity, $rawDefenderNation,
- $startYear, $year, $month, $cityRate
-){
- $attacker = new WarUnitGeneral(
- new General($rawAttacker, null, $rawAttackerCity, $rawAttackerNation, $year, $month),
- $rawAttackerNation,
- true
- );
- $city = new WarUnitCity($rawDefenderCity, $rawDefenderNation, $year, $month, $cityRate);
-
- $iterDefender = new \ArrayIterator($rawDefenderList);
- $iterDefender->rewind();
-
- $battleResult = [];
-
- $attackerRice = $rawAttacker['rice'];
- $defenderRice = 0;
-
- $getNextDefender = function(?WarUnit $prevDefender, bool $reqNext)
- use ($iterDefender, $rawDefenderCity, $rawDefenderNation, $year, $month, &$battleResult, &$defenderRice) {
- if($prevDefender !== null){
- $prevDefender->getLogger()->rollback();
- $battleResult[] = $prevDefender;
- if($prevDefender instanceof WarUnitGeneral){
- $defenderRice -= $prevDefender->getVar('rice');
- }
- }
-
- if(!$reqNext){
- return null;
- }
-
- if(!$iterDefender->valid()){
- return null;
- }
-
- $defenderObj = new General($iterDefender->current(), null, $rawDefenderCity, $rawDefenderNation, $year, $month);
- if(extractBattleOrder($defenderObj) <= 0){
- return null;
- }
-
- $defenderRice += $defenderObj->getVar('rice');
-
- $retVal = new WarUnitGeneral(
- $defenderObj,
- $rawDefenderNation,
- false
- );
- $iterDefender->next();
- return $retVal;
- };
-
- $conquerCity = processWar_NG($attacker, $getNextDefender, $city, $year - $startYear);
-
- $rawDefenderCity = $city->getRaw();
- $updateAttackerNation = [];
- $updateDefenderNation = [];
-
- $attackerRice -= $attacker->getVar('rice');
-
- if($city->getPhase() > 0){
- $rice = $city->getKilled() / 100 * 0.8;
- $rice *= $city->getCrewType()->rice;
- $rice *= getTechCost($rawDefenderNation['tech']);
- $rice *= $cityRate / 100 - 0.2;
- Util::setRound($rice);
-
- $defenderRice += $rice;
- }
-
- $totalDead = $attacker->getKilled() + $attacker->getDead();
- $attackerCityDead = $totalDead * 0.4;
- $defenderCityDead = $totalDead * 0.6;
-
- return [$attacker, $city, $battleResult, $conquerCity, $attackerRice, $defenderRice];
-}
-
-$lastWarLog = [];
-
-$attackerKilled = 0;
-$attackerDead = 0;
-
-$attackerMaxKilled = 0;
-$attackerMinKilled = PHP_INT_MAX;
-
-$attackerMaxDead = 0;
-$attackerMinDead = PHP_INT_MAX;
-
-
-$attackerAvgRice = 0;
-$defenderAvgRice = 0;
-
-$avgPhase = 0;
-$avgWar = 0;
-
-$attackerActivatedSkills = [];
-$defendersActivatedSkills = [];
-
-foreach(Util::range($repeatCnt) as $repeatIdx){
- /** @var WarUnit $attacker */
- [$attacker, $city, $battleResult, $conquerCity, $attackerRice, $defenderRice] = simulateBattle(
- $rawAttacker, $rawAttackerCity, $rawAttackerNation,
- $rawDefenderList, $rawDefenderCity, $rawDefenderNation,
- $startYear, $year, $month, $cityRate
- );
- $lastWarLog = Util::mapWithKey(function($key, $values){
- return ConvertLog(join('
', $values));
- }, $attacker->getLogger()->rollback());
-
- $avgPhase += $attacker->getPhase() / $repeatCnt;
-
- $killed = $attacker->getKilled();
- $dead = $attacker->getDead();
-
- $attackerKilled += $killed / $repeatCnt;
- $attackerDead += $dead / $repeatCnt;
-
- $attackerMaxKilled = max($attackerMaxKilled, $killed);
- $attackerMinKilled = min($attackerMinKilled, $killed);
-
- $attackerMaxDead = max($attackerMaxDead, $dead);
- $attackerMinDead = min($attackerMinDead, $dead);
-
- $attackerAvgRice += $attackerRice / $repeatCnt;
- $defenderAvgRice += $defenderRice / $repeatCnt;
-
- $avgWar += count($battleResult) / $repeatCnt;
-
- foreach($attacker->getActivatedSkillLog() as $skillName => $skillCnt){
- if(!key_exists($skillName, $attackerActivatedSkills)){
- $attackerActivatedSkills[$skillName] = $skillCnt / $repeatCnt;
- }
- else{
- $attackerActivatedSkills[$skillName] += $skillCnt / $repeatCnt;
- }
- }
-
- foreach($battleResult as $idx=>$defender){
- while($idx >= count($defendersActivatedSkills)){
- $defendersActivatedSkills[] = [];
- }
-
- $activatedSkills = &$defendersActivatedSkills[$idx];
- foreach($defender->getActivatedSkillLog() as $skillName => $skillCnt){
- if(!key_exists($skillName, $activatedSkills)){
- $activatedSkills[$skillName] = $skillCnt / $repeatCnt;
- }
- else{
- $activatedSkills[$skillName] += $skillCnt / $repeatCnt;
- }
- }
-
- }
-}
-
-Json::die([
- 'result'=>true,
- 'datetime'=>$rawAttacker['turntime'],
- 'reason'=>'success',
- 'lastWarLog'=>$lastWarLog,
- 'avgWar'=>$avgWar,
- 'phase'=>$avgPhase,
- 'killed'=>$attackerKilled,
- 'maxKilled'=>$attackerMaxKilled,
- 'minKilled'=>$attackerMinKilled,
- 'dead'=>$attackerDead,
- 'maxDead'=>$attackerMaxDead,
- 'minDead'=>$attackerMinDead,
- 'attackerRice'=>$attackerAvgRice,
- 'defenderRice'=>$defenderAvgRice,
- 'attackerSkills'=>$attackerActivatedSkills,
- 'defendersSkills'=>$defendersActivatedSkills,
-]);
+setReadOnly();
+$userID = Session::getUserID();
+
+increaseRefresh("시뮬레이터", 0);
+
+$query = Util::getPost('query');
+if($query === null){
+ Json::die([
+ 'result'=>false,
+ 'reason'=>'입력값이 없습니다.'
+ ]);
+}
+
+$action = Util::getPost('action');
+if($action === null || !in_array($action, ['reorder', 'battle'])){
+ Json::die([
+ 'result'=>false,
+ 'reason'=>'원하는 동작이 지정되지 않았습니다.'
+ ]);
+}
+
+$query = Json::decode($query);
+if($query === null){
+ Json::die([
+ 'result'=>false,
+ 'reason'=>'올바르지 않은 JSON입니다.'
+ ]);
+}
+
+$defaultCheck = [
+ 'required'=>[
+ 'attackerGeneral', 'attackerCity', 'attackerNation',
+ 'defenderGenerals', 'defenderCity', 'defenderNation',
+ 'year', 'month', 'repeatCnt'
+ ],
+ 'integer'=>[
+ 'year','month','repeatCnt'
+ ],
+ 'between'=>[
+ ['month', [1, 12]]
+ ],
+ 'in'=>[
+ ['repeatCnt', [1, 1000]]
+ ],
+ 'min'=>[
+ ['year', 0]
+ ],
+ 'array'=>[
+ 'attackerGeneral', 'attackerCity', 'attackerNation',
+ 'defenderGenerals', 'defenderCity', 'defenderNation'
+ ],
+];
+
+$v = new Validator($query);
+$v->rules($defaultCheck);
+if(!$v->validate()){
+ Json::die([
+ 'result'=>false,
+ 'reason'=>$v->errorStr()
+ ]);
+}
+
+$year = $query['year'];
+$month = $query['month'];
+$repeatCnt = $query['repeatCnt'];
+
+$rawAttacker = $query['attackerGeneral'];
+$rawAttacker['turntime'] = TimeUtil::now();
+$rawAttackerCity = $query['attackerCity'];
+$rawAttackerNation = $query['attackerNation'];
+
+$rawDefenderList = $query['defenderGenerals'];
+$rawDefenderCity = $query['defenderCity'];
+$rawDefenderNation = $query['defenderNation'];
+
+
+$generalCheck = [
+ 'required'=>[
+ 'no', 'name', 'nation', 'turntime', 'personal', 'special2', 'crew', 'crewtype', 'atmos', 'train',
+ 'intel', 'intel_exp', 'book', 'strength', 'strength_exp', 'weapon', 'injury', 'leadership', 'leadership_exp', 'horse', 'item',
+ 'explevel', 'experience', 'dedication', 'officer_level', 'officer_city', 'gold', 'rice', 'dex1', 'dex2', 'dex3', 'dex4', 'dex5',
+ 'recent_war', 'warnum', 'killnum', 'killcrew',
+ ],
+ 'integer'=>[
+ 'no', 'nation', 'crew', 'crewtype', 'atmos', 'train',
+ 'intel', 'intel_exp', 'strength', 'strength_exp', 'injury', 'leadership', 'leadership_exp',
+ 'explevel', 'experience', 'dedication', 'officer_level', 'officer_city', 'gold', 'rice', 'dex1', 'dex2', 'dex3', 'dex4', 'dex5',
+ 'warnum', 'killnum', 'killcrew',
+ ],
+ 'min'=>[
+ ['no', 1],
+ ['nation', 1],
+ ['crew', 0],
+ ['intel', 0],
+ ['strength', 0],
+ ['leadership', 0],
+ ['experience', 0],
+ ['gold', 0],
+ ['rice', 0],
+ ['dex1', 0],
+ ['dex2', 0],
+ ['dex3', 0],
+ ['dex4', 0],
+ ['dex5', 0],
+ ['warnum', 0],
+ ['killnum', 0],
+ ['killcrew', 0],
+ ],
+ 'between'=>[
+ ['train', [40, GameConst::$maxTrainByWar]],
+ ['atmos', [40, GameConst::$maxAtmosByWar]],
+ ['explevel', [0, 300]],
+ ['injury', [0, 80]],
+ ['officer_level', [1, 12]]
+ ],
+ 'in'=>[
+ ['personal', array_merge(GameConst::$availablePersonality, GameConst::$optionalPersonality)],
+ ['special2', array_merge(GameConst::$availableSpecialWar, GameConst::$optionalSpecialWar)],
+ ['crewtype', array_keys(GameUnitConst::all())],
+ ['horse', array_merge(array_keys(GameConst::$allItems['horse']), ['None'])],
+ ['weapon', array_merge(array_keys(GameConst::$allItems['weapon']), ['None'])],
+ ['book', array_merge(array_keys(GameConst::$allItems['book']), ['None'])],
+ ['item', array_merge(array_keys(GameConst::$allItems['item']), ['None'])],
+ ]
+];
+
+$v = new Validator($rawAttacker);
+$v->rules($generalCheck);
+if(!$v->validate()){
+ Json::die([
+ 'result'=>false,
+ 'reason'=>'[출병자]'.$v->errorStr()
+ ]);
+}
+
+$defenderList = [];
+foreach($rawDefenderList as $idx=>$rawDefenderGeneral){
+ $v = new Validator($rawDefenderGeneral);
+ $v->rules($generalCheck);
+ if(!$v->validate()){
+ $idx+=1;
+ Json::die([
+ 'result'=>false,
+ 'reason'=>"[수비자{$idx}]".$v->errorStr()
+ ]);
+ }
+
+ $defenderList[] = new General($rawDefenderGeneral, extractRankVar($rawDefenderGeneral), $rawDefenderCity, $rawAttackerNation, $year, $month, true);
+}
+
+
+$cityCheck = [
+ 'required'=>[
+ 'city', 'nation', 'supply', 'name',
+ 'pop', 'agri', 'comm', 'secu', 'def', 'wall',
+ 'trust', 'level',
+ 'pop_max', 'agri_max', 'comm_max', 'secu_max', 'def_max', 'wall_max',
+ 'dead', 'state', 'conflict',
+ ],
+ 'numeric'=>[
+ 'pop', 'agri', 'comm', 'secu', 'def', 'wall', 'trust', 'dead'
+ ],
+ 'integer'=>[
+ 'city', 'nation', 'supply',
+ 'pop_max', 'agri_max', 'comm_max', 'secu_max', 'def_max', 'wall_max',
+ 'state',
+ ],
+ 'min'=>[
+ ['def', 0],
+ ['wall', 0],
+ ['trust', 0],
+ ['pop', 0],
+ ['comm', 0],
+ ['secu', 0],
+ ['city', 1],
+ ['nation', 0]
+ ],
+ 'in'=>[
+ ['level', array_keys(getCityLevelList())]
+ ]
+];
+
+$v = new Validator($rawAttackerCity);
+$v->rules($cityCheck);
+if(!$v->validate()){
+ Json::die([
+ 'result'=>false,
+ 'reason'=>'[출병도시]'.$v->errorStr()
+ ]);
+}
+
+$v = new Validator($rawDefenderCity);
+$v->rules($cityCheck);
+if(!$v->validate()){
+ Json::die([
+ 'result'=>false,
+ 'reason'=>'[수비도시]'.$v->errorStr()
+ ]);
+}
+
+$nationCheck = [
+ 'required'=>[
+ 'type', 'tech', 'level', 'capital',
+ 'nation', 'name', 'gold', 'rice', 'gennum'
+ ],
+ 'integer'=>[
+ 'level', 'capital', 'nation', 'gennum',
+ ],
+ 'numeric'=>[
+ 'tech', 'gold', 'rice'
+ ],
+ 'min'=>[
+ ['tech', 0],
+ ['gold', 0],
+ ['rice', 0],
+ ['gennum', 1],
+ ],
+ 'in'=>[
+ ['type', GameConst::$availableNationType],
+ ['level', array_keys(getNationLevelList())]
+ ]
+];
+
+$v = new Validator($rawAttackerNation);
+$v->rules($nationCheck);
+if(!$v->validate()){
+ Json::die([
+ 'result'=>false,
+ 'reason'=>'[출병국]'.$v->errorStr()
+ ]);
+}
+
+$v = new Validator($rawDefenderNation);
+$v->rules($nationCheck);
+if(!$v->validate()){
+ Json::die([
+ 'result'=>false,
+ 'reason'=>'[수비국]'.$v->errorStr()
+ ]);
+}
+
+if($action == 'reorder'){
+ usort($defenderList, function(General $lhs, General $rhs){
+ return -(extractBattleOrder($lhs) <=> extractBattleOrder($rhs));
+ });
+
+ $order = [];
+ foreach($defenderList as $defenderGeneral){
+ $order[] = $defenderGeneral->getID();
+ }
+
+ Json::die([
+ 'result'=>true,
+ 'reason'=>'success',
+ 'order'=>$order
+ ]);
+}
+
+usort($defenderList, function(General $lhs, General $rhs){
+ return -(extractBattleOrder($lhs) <=> extractBattleOrder($rhs));
+});
+
+$rawDefenderList = array_map(function(General $general){
+ return $general->getRaw();
+}, $defenderList);
+unset($defenderList);
+
+$db = DB::db();
+$gameStor = KVStorage::getStorage($db, 'game_env');
+$startYear = $gameStor->startyear;
+$cityRate = Util::round(($year - $startYear) / 1.5) + 60;
+
+function extractRankVar(array $rawVal):array{
+ $rankVars = [];
+ foreach($rawVal as $rawKey=>$rawVal){
+ if(key_exists($rawKey, General::RANK_COLUMN)){
+ $rankVars[$rawKey] = $rawVal;
+ }
+ }
+ return $rankVars;
+}
+
+function simulateBattle(
+ $rawAttacker, $rawAttackerCity, $rawAttackerNation,
+ $rawDefenderList, $rawDefenderCity, $rawDefenderNation,
+ $startYear, $year, $month, $cityRate
+){
+ $attacker = new WarUnitGeneral(
+ new General($rawAttacker, extractRankVar($rawAttacker), $rawAttackerCity, $rawAttackerNation, $year, $month),
+ $rawAttackerNation,
+ true
+ );
+ $city = new WarUnitCity($rawDefenderCity, $rawDefenderNation, $year, $month, $cityRate);
+
+ $iterDefender = new \ArrayIterator($rawDefenderList);
+ $iterDefender->rewind();
+
+ $battleResult = [];
+
+ $attackerRice = $rawAttacker['rice'];
+ $defenderRice = 0;
+
+ $getNextDefender = function(?WarUnit $prevDefender, bool $reqNext)
+ use ($iterDefender, $rawDefenderCity, $rawDefenderNation, $year, $month, &$battleResult, &$defenderRice) {
+ if($prevDefender !== null){
+ $prevDefender->getLogger()->rollback();
+ $battleResult[] = $prevDefender;
+ if($prevDefender instanceof WarUnitGeneral){
+ $defenderRice -= $prevDefender->getVar('rice');
+ }
+ }
+
+ if(!$reqNext){
+ return null;
+ }
+
+ if(!$iterDefender->valid()){
+ return null;
+ }
+
+ $defenderObj = new General($iterDefender->current(), extractRankVar($iterDefender->current()), $rawDefenderCity, $rawDefenderNation, $year, $month);
+ if(extractBattleOrder($defenderObj) <= 0){
+ return null;
+ }
+
+ $defenderRice += $defenderObj->getVar('rice');
+
+ $retVal = new WarUnitGeneral(
+ $defenderObj,
+ $rawDefenderNation,
+ false
+ );
+ $iterDefender->next();
+ return $retVal;
+ };
+
+ $conquerCity = processWar_NG($attacker, $getNextDefender, $city, $year - $startYear);
+
+ $rawDefenderCity = $city->getRaw();
+ $updateAttackerNation = [];
+ $updateDefenderNation = [];
+
+ $attackerRice -= $attacker->getVar('rice');
+
+ if($city->getPhase() > 0){
+ $rice = $city->getKilled() / 100 * 0.8;
+ $rice *= $city->getCrewType()->rice;
+ $rice *= getTechCost($rawDefenderNation['tech']);
+ $rice *= $cityRate / 100 - 0.2;
+ Util::setRound($rice);
+
+ $defenderRice += $rice;
+ }
+
+ $totalDead = $attacker->getKilled() + $attacker->getDead();
+ $attackerCityDead = $totalDead * 0.4;
+ $defenderCityDead = $totalDead * 0.6;
+
+ return [$attacker, $city, $battleResult, $conquerCity, $attackerRice, $defenderRice];
+}
+
+$lastWarLog = [];
+
+$attackerKilled = 0;
+$attackerDead = 0;
+
+$attackerMaxKilled = 0;
+$attackerMinKilled = PHP_INT_MAX;
+
+$attackerMaxDead = 0;
+$attackerMinDead = PHP_INT_MAX;
+
+
+$attackerAvgRice = 0;
+$defenderAvgRice = 0;
+
+$avgPhase = 0;
+$avgWar = 0;
+
+$attackerActivatedSkills = [];
+$defendersActivatedSkills = [];
+
+foreach(Util::range($repeatCnt) as $repeatIdx){
+ /** @var WarUnit $attacker */
+ [$attacker, $city, $battleResult, $conquerCity, $attackerRice, $defenderRice] = simulateBattle(
+ $rawAttacker, $rawAttackerCity, $rawAttackerNation,
+ $rawDefenderList, $rawDefenderCity, $rawDefenderNation,
+ $startYear, $year, $month, $cityRate
+ );
+ $lastWarLog = Util::mapWithKey(function($key, $values){
+ return ConvertLog(join('
', $values));
+ }, $attacker->getLogger()->rollback());
+
+ $avgPhase += $attacker->getPhase() / $repeatCnt;
+
+ $killed = $attacker->getKilled();
+ $dead = $attacker->getDead();
+
+ $attackerKilled += $killed / $repeatCnt;
+ $attackerDead += $dead / $repeatCnt;
+
+ $attackerMaxKilled = max($attackerMaxKilled, $killed);
+ $attackerMinKilled = min($attackerMinKilled, $killed);
+
+ $attackerMaxDead = max($attackerMaxDead, $dead);
+ $attackerMinDead = min($attackerMinDead, $dead);
+
+ $attackerAvgRice += $attackerRice / $repeatCnt;
+ $defenderAvgRice += $defenderRice / $repeatCnt;
+
+ $avgWar += count($battleResult) / $repeatCnt;
+
+ foreach($attacker->getActivatedSkillLog() as $skillName => $skillCnt){
+ if(!key_exists($skillName, $attackerActivatedSkills)){
+ $attackerActivatedSkills[$skillName] = $skillCnt / $repeatCnt;
+ }
+ else{
+ $attackerActivatedSkills[$skillName] += $skillCnt / $repeatCnt;
+ }
+ }
+
+ foreach($battleResult as $idx=>$defender){
+ while($idx >= count($defendersActivatedSkills)){
+ $defendersActivatedSkills[] = [];
+ }
+
+ $activatedSkills = &$defendersActivatedSkills[$idx];
+ foreach($defender->getActivatedSkillLog() as $skillName => $skillCnt){
+ if(!key_exists($skillName, $activatedSkills)){
+ $activatedSkills[$skillName] = $skillCnt / $repeatCnt;
+ }
+ else{
+ $activatedSkills[$skillName] += $skillCnt / $repeatCnt;
+ }
+ }
+
+ }
+}
+
+Json::die([
+ 'result'=>true,
+ 'datetime'=>$rawAttacker['turntime'],
+ 'reason'=>'success',
+ 'lastWarLog'=>$lastWarLog,
+ 'avgWar'=>$avgWar,
+ 'phase'=>$avgPhase,
+ 'killed'=>$attackerKilled,
+ 'maxKilled'=>$attackerMaxKilled,
+ 'minKilled'=>$attackerMinKilled,
+ 'dead'=>$attackerDead,
+ 'maxDead'=>$attackerMaxDead,
+ 'minDead'=>$attackerMinDead,
+ 'attackerRice'=>$attackerAvgRice,
+ 'defenderRice'=>$defenderAvgRice,
+ 'attackerSkills'=>$attackerActivatedSkills,
+ 'defendersSkills'=>$defendersActivatedSkills,
+]);
diff --git a/hwe/js/battle_simulator.js b/hwe/js/battle_simulator.js
index d1c2e0c9..7e335b7d 100644
--- a/hwe/js/battle_simulator.js
+++ b/hwe/js/battle_simulator.js
@@ -207,6 +207,10 @@ jQuery(function($) {
setVal('.form_dex5', data.dex5);
setVal('.form_defence_train', data.defence_train);
+ setVal('.form_warnum', data.warnum);
+ setVal('.form_killnum', data.killnum);
+ setVal('.form_killcrew', data.killcrew);
+
if (!setGeneralNo($general, data.no)) {
setGeneralNo($general, generateNewGeneralNo());
}
@@ -252,7 +256,13 @@ jQuery(function($) {
dex3: getInt('.form_dex3'),
dex4: getInt('.form_dex4'),
dex5: getInt('.form_dex5'),
+
defence_train: getInt('.form_defence_train'),
+
+ warnum: getInt('.form_warnum'),
+ killnum: getInt('.form_killnum'),
+ killcrew: getInt('.form_killcrew'),
+
};
}
diff --git a/hwe/sammo/ActionItem/che_의술_청낭서.php b/hwe/sammo/ActionItem/che_의술_청낭서.php
index 5d09286e..1377515c 100644
--- a/hwe/sammo/ActionItem/che_의술_청낭서.php
+++ b/hwe/sammo/ActionItem/che_의술_청낭서.php
@@ -1,35 +1,35 @@
-[전투] 페이즈마다 20% 확률로 치료 발동(아군 피해 1/3 감소)';
- protected $cost = 200;
- protected $consumable = false;
-
- public function getPreTurnExecuteTriggerList(General $general):?GeneralTriggerCaller{
- return new GeneralTriggerCaller(
- new GeneralTrigger\che_도시치료($general)
- );
- }
-
- public function getBattlePhaseSkillTriggerList(\sammo\WarUnit $unit): ?WarUnitTriggerCaller
- {
- return new WarUnitTriggerCaller(
- new che_전투치료시도($unit, BaseWarUnitTrigger::TYPE_ITEM),
- new che_전투치료발동($unit, BaseWarUnitTrigger::TYPE_ITEM)
-
- );
- }
+[전투] 페이즈마다 40% 확률로 치료 발동(아군 피해 1/3 감소, 부상 회복)';
+ protected $cost = 200;
+ protected $consumable = false;
+
+ public function getPreTurnExecuteTriggerList(General $general):?GeneralTriggerCaller{
+ return new GeneralTriggerCaller(
+ new GeneralTrigger\che_도시치료($general)
+ );
+ }
+
+ public function getBattlePhaseSkillTriggerList(\sammo\WarUnit $unit): ?WarUnitTriggerCaller
+ {
+ return new WarUnitTriggerCaller(
+ new che_전투치료시도($unit, BaseWarUnitTrigger::TYPE_ITEM),
+ new che_전투치료발동($unit, BaseWarUnitTrigger::TYPE_ITEM)
+
+ );
+ }
}
\ No newline at end of file
diff --git a/hwe/sammo/ActionItem/che_의술_태평청령.php b/hwe/sammo/ActionItem/che_의술_태평청령.php
index 16ef926f..2724797c 100644
--- a/hwe/sammo/ActionItem/che_의술_태평청령.php
+++ b/hwe/sammo/ActionItem/che_의술_태평청령.php
@@ -1,35 +1,35 @@
-[전투] 페이즈마다 20% 확률로 치료 발동(아군 피해 1/3 감소)';
- protected $cost = 200;
- protected $consumable = false;
-
- public function getPreTurnExecuteTriggerList(General $general):?GeneralTriggerCaller{
- return new GeneralTriggerCaller(
- new GeneralTrigger\che_도시치료($general)
- );
- }
-
- public function getBattlePhaseSkillTriggerList(\sammo\WarUnit $unit): ?WarUnitTriggerCaller
- {
- return new WarUnitTriggerCaller(
- new che_전투치료시도($unit, BaseWarUnitTrigger::TYPE_ITEM),
- new che_전투치료발동($unit, BaseWarUnitTrigger::TYPE_ITEM)
-
- );
- }
+[전투] 페이즈마다 40% 확률로 치료 발동(아군 피해 1/3 감소, 부상 회복)';
+ protected $cost = 200;
+ protected $consumable = false;
+
+ public function getPreTurnExecuteTriggerList(General $general):?GeneralTriggerCaller{
+ return new GeneralTriggerCaller(
+ new GeneralTrigger\che_도시치료($general)
+ );
+ }
+
+ public function getBattlePhaseSkillTriggerList(\sammo\WarUnit $unit): ?WarUnitTriggerCaller
+ {
+ return new WarUnitTriggerCaller(
+ new che_전투치료시도($unit, BaseWarUnitTrigger::TYPE_ITEM),
+ new che_전투치료발동($unit, BaseWarUnitTrigger::TYPE_ITEM)
+
+ );
+ }
}
\ No newline at end of file
diff --git a/hwe/sammo/ActionItem/che_저격_수극.php b/hwe/sammo/ActionItem/che_저격_수극.php
index 75c5cb08..5f2cb6e8 100644
--- a/hwe/sammo/ActionItem/che_저격_수극.php
+++ b/hwe/sammo/ActionItem/che_저격_수극.php
@@ -1,28 +1,28 @@
-[전투] 성벽 공격 시 대미지 +100%';
+ protected $info = '[군사] 차병 계통 징·모병비 -10%
[전투] 성벽 공격 시 대미지 +100%,
공격시 상대 병종에/수비시 자신 병종 숙련에 차병 숙련을 가산';
protected $cost = 100;
protected $buyable = true;
protected $consumable = false;
@@ -32,4 +32,18 @@ class event_전투특기_공성 extends \sammo\BaseItem{
}
return [1, 1];
}
+
+ public function onCalcStat(General $general, string $statName, $value, $aux=null){
+ if(\sammo\Util::starts_with($statName, 'dex')){
+ $myArmType = 'dex'.GameUnitConst::T_CASTLE;
+ $opposeArmType = 'dex'.$aux['opposeType']->armType;;
+ if($aux['isAttacker'] && $opposeArmType === $statName){
+ return $value + $general->getVar($myArmType);
+ }
+ if(!$aux['isAttacker'] && $myArmType === $statName){
+ return $value + $general->getVar($myArmType);
+ }
+ }
+ return $value;
+ }
}
\ No newline at end of file
diff --git a/hwe/sammo/ActionItem/event_전투특기_궁병.php b/hwe/sammo/ActionItem/event_전투특기_궁병.php
index d90a3c9f..182c5212 100644
--- a/hwe/sammo/ActionItem/event_전투특기_궁병.php
+++ b/hwe/sammo/ActionItem/event_전투특기_궁병.php
@@ -11,7 +11,7 @@ class event_전투특기_궁병 extends \sammo\BaseItem{
protected $id = 51;
protected $rawName = '비급';
protected $name = '비급(궁병)';
- protected $info = '[군사] 궁병 계통 징·모병비 -10%
[전투] 회피 확률 +20%p';
+ protected $info = '[군사] 궁병 계통 징·모병비 -10%
[전투] 회피 확률 +20%p,
공격시 상대 병종에/수비시 자신 병종 숙련에 궁병 숙련을 가산';
protected $cost = 100;
protected $buyable = true;
protected $consumable = false;
@@ -29,6 +29,16 @@ class event_전투특기_궁병 extends \sammo\BaseItem{
if($statName === 'warAvoidRatio'){
return $value + 0.2;
}
+ if(\sammo\Util::starts_with($statName, 'dex')){
+ $myArmType = 'dex'.GameUnitConst::T_ARCHER;
+ $opposeArmType = 'dex'.$aux['opposeType']->armType;;
+ if($aux['isAttacker'] && $opposeArmType === $statName){
+ return $value + $general->getVar($myArmType);
+ }
+ if(!$aux['isAttacker'] && $myArmType === $statName){
+ return $value + $general->getVar($myArmType);
+ }
+ }
return $value;
}
}
\ No newline at end of file
diff --git a/hwe/sammo/ActionItem/event_전투특기_귀병.php b/hwe/sammo/ActionItem/event_전투특기_귀병.php
index 8fc1cf79..3bcc2201 100644
--- a/hwe/sammo/ActionItem/event_전투특기_귀병.php
+++ b/hwe/sammo/ActionItem/event_전투특기_귀병.php
@@ -11,7 +11,7 @@ class event_전투특기_귀병 extends \sammo\BaseItem{
protected $id = 40;
protected $rawName = '비급';
protected $name = '비급(귀병)';
- protected $info = '[군사] 귀병 계통 징·모병비 -10%
[전투] 계략 성공 확률 +20%p';
+ protected $info = '[군사] 귀병 계통 징·모병비 -10%
[전투] 계략 성공 확률 +20%p,
공격시 상대 병종에/수비시 자신 병종 숙련에 귀병 숙련을 가산';
protected $cost = 100;
protected $buyable = true;
protected $consumable = false;
@@ -29,6 +29,16 @@ class event_전투특기_귀병 extends \sammo\BaseItem{
if($statName === 'warMagicSuccessProb'){
return $value + 0.2;
}
+ if(\sammo\Util::starts_with($statName, 'dex')){
+ $myArmType = 'dex'.GameUnitConst::T_WIZARD;
+ $opposeArmType = 'dex'.$aux['opposeType']->armType;;
+ if($aux['isAttacker'] && $opposeArmType === $statName){
+ return $value + $general->getVar($myArmType);
+ }
+ if(!$aux['isAttacker'] && $myArmType === $statName){
+ return $value + $general->getVar($myArmType);
+ }
+ }
return $value;
}
}
\ No newline at end of file
diff --git a/hwe/sammo/ActionItem/event_전투특기_기병.php b/hwe/sammo/ActionItem/event_전투특기_기병.php
index 5f644265..36a163b4 100644
--- a/hwe/sammo/ActionItem/event_전투특기_기병.php
+++ b/hwe/sammo/ActionItem/event_전투특기_기병.php
@@ -11,7 +11,7 @@ class event_전투특기_기병 extends \sammo\BaseItem{
protected $id = 52;
protected $rawName = '비급';
protected $name = '비급(기병)';
- protected $info = '[군사] 기병 계통 징·모병비 -10%
[전투] 수비 시 대미지 +10%, 공격 시 대미지 +20%';
+ protected $info = '[군사] 기병 계통 징·모병비 -10%
[전투] 수비 시 대미지 +10%, 공격 시 대미지 +20%,
공격시 상대 병종에/수비시 자신 병종 숙련에 기병 숙련을 가산';
protected $cost = 100;
protected $buyable = true;
protected $consumable = false;
@@ -31,4 +31,18 @@ class event_전투특기_기병 extends \sammo\BaseItem{
}
return [1.1, 1];
}
+
+ public function onCalcStat(General $general, string $statName, $value, $aux=null){
+ if(\sammo\Util::starts_with($statName, 'dex')){
+ $myArmType = 'dex'.GameUnitConst::T_CAVALRY;
+ $opposeArmType = 'dex'.$aux['opposeType']->armType;;
+ if($aux['isAttacker'] && $opposeArmType === $statName){
+ return $value + $general->getVar($myArmType);
+ }
+ if(!$aux['isAttacker'] && $myArmType === $statName){
+ return $value + $general->getVar($myArmType);
+ }
+ }
+ return $value;
+ }
}
\ No newline at end of file
diff --git a/hwe/sammo/ActionItem/event_전투특기_돌격.php b/hwe/sammo/ActionItem/event_전투특기_돌격.php
index 1c52cbb2..6a1b8401 100644
--- a/hwe/sammo/ActionItem/event_전투특기_돌격.php
+++ b/hwe/sammo/ActionItem/event_전투특기_돌격.php
@@ -7,13 +7,14 @@ use \sammo\WarUnit;
use \sammo\BaseWarUnitTrigger;
use \sammo\WarUnitTriggerCaller;
use sammo\WarUnitTrigger\WarActivateSkills;
+use \sammo\WarUnitTrigger\che_돌격지속;
class event_전투특기_돌격 extends \sammo\BaseItem{
protected $id = 60;
protected $rawName = '비급';
protected $name = '비급(돌격)';
- protected $info = '[전투] 상대 회피 불가, 공격 시 전투 페이즈 +1, 공격 시 대미지 +10%';
+ protected $info = '[전투] 공격 시 대등/유리한 병종에게는 퇴각 전까지 전투, 공격 시 페이즈 + 2, 공격 시 대미지 +5%';
protected $cost = 100;
protected $buyable = true;
protected $consumable = false;
@@ -21,21 +22,20 @@ class event_전투특기_돌격 extends \sammo\BaseItem{
public function onCalcStat(General $general, string $statName, $value, $aux=null){
if($statName === 'initWarPhase'){
- return $value + 1;
+ return $value + 2;
}
return $value;
}
-
public function getWarPowerMultiplier(WarUnit $unit):array{
if($unit->isAttacker()){
- return [1.1, 1];
+ return [1.05, 1];
}
return [1, 1];
}
public function getBattlePhaseSkillTriggerList(WarUnit $unit):?WarUnitTriggerCaller{
return new WarUnitTriggerCaller(
- (new WarActivateSkills($unit, BaseWarUnitTrigger::TYPE_NONE, false, '회피불가'))->setPriority(BaseWarUnitTrigger::PRIORITY_BEGIN + 200)
+ new che_돌격지속($unit)
);
}
}
\ No newline at end of file
diff --git a/hwe/sammo/ActionItem/event_전투특기_무쌍.php b/hwe/sammo/ActionItem/event_전투특기_무쌍.php
index 256565d7..6a28d62e 100644
--- a/hwe/sammo/ActionItem/event_전투특기_무쌍.php
+++ b/hwe/sammo/ActionItem/event_전투특기_무쌍.php
@@ -4,13 +4,14 @@ use \sammo\iAction;
use \sammo\General;
use \sammo\SpecialityHelper;
use \sammo\WarUnit;
+use \sammo\Util;
class event_전투특기_무쌍 extends \sammo\BaseItem{
protected $id = 61;
protected $rawName = '비급';
protected $name = '비급(무쌍)';
- protected $info = '[전투] 대미지 +10%, 공격 시 필살 확률 +10%p';
+ protected $info = '[전투] 대미지 +10%, 공격 시 필살 확률 +10%p,
승리 수만큼 대미지 0.20%씩 추가 상승(최대40%)
승리 수만큼 피해 0.05%씩 감소(최대50%)';
protected $cost = 100;
protected $buyable = true;
protected $consumable = false;
@@ -24,6 +25,11 @@ class event_전투특기_무쌍 extends \sammo\BaseItem{
}
public function getWarPowerMultiplier(WarUnit $unit):array{
- return [1.1, 1];
+ $attackMultiplier = 1.1;
+ $defenceMultiplier = 1;
+ $killnum = $unit->getGeneral()->getRankVar('killnum');
+ $attackMultiplier += Util::valueFit($killnum * 0.01 * 0.2, null, 0.4);
+ $defenceMultiplier -= Util::valueFit($killnum * 0.01 * 0.05, null, 0.5);
+ return [$attackMultiplier, $defenceMultiplier];
}
}
\ No newline at end of file
diff --git a/hwe/sammo/ActionItem/event_전투특기_반계.php b/hwe/sammo/ActionItem/event_전투특기_반계.php
index aa5468ac..5cab0605 100644
--- a/hwe/sammo/ActionItem/event_전투특기_반계.php
+++ b/hwe/sammo/ActionItem/event_전투특기_반계.php
@@ -15,7 +15,7 @@ class event_전투특기_반계 extends \sammo\BaseItem{
protected $id = 45;
protected $rawName = '비급';
protected $name = '비급(반계)';
- protected $info = '[전투] 상대의 계략 성공 확률 -10%p, 상대의 계략을 40% 확률로 되돌림, 반목 성공시 대미지 추가(+60% → +100%)';
+ protected $info = '[전투] 상대의 계략 성공 확률 -10%p, 상대의 계략을 40% 확률로 되돌림, 반목 성공시 대미지 추가(+60% → +150%)';
protected $cost = 100;
protected $buyable = true;
protected $consumable = false;
@@ -23,15 +23,15 @@ class event_전투특기_반계 extends \sammo\BaseItem{
public function onCalcStat(General $general, string $statName, $value, $aux=null){
if($statName === 'warMagicSuccessDamage' && $aux === '반목'){
- return $value + 0.4;
+ return $value + 0.9;
}
return $value;
}
public function getBattlePhaseSkillTriggerList(WarUnit $unit):?WarUnitTriggerCaller{
return new WarUnitTriggerCaller(
- new WarActivateSkills($unit, BaseWarUnitTrigger::TYPE_ITEM, false, '계략약화'),
- new che_반계시도($unit, che_반계시도::TYPE_ITEM),
+ new WarActivateSkills($unit, BaseWarUnitTrigger::TYPE_NONE, false, '계략약화'),
+ new che_반계시도($unit),
new che_반계발동($unit)
);
}
diff --git a/hwe/sammo/ActionItem/event_전투특기_보병.php b/hwe/sammo/ActionItem/event_전투특기_보병.php
index 9966c826..d38e7e51 100644
--- a/hwe/sammo/ActionItem/event_전투특기_보병.php
+++ b/hwe/sammo/ActionItem/event_전투특기_보병.php
@@ -11,7 +11,7 @@ class event_전투특기_보병 extends \sammo\BaseItem{
protected $id = 50;
protected $rawName = '비급';
protected $name = '비급(보병)';
- protected $info = '[군사] 보병 계통 징·모병비 -10%
[전투] 공격 시 아군 피해 -10%, 수비 시 아군 피해 -20%';
+ protected $info = '[군사] 보병 계통 징·모병비 -10%
[전투] 공격 시 아군 피해 -10%, 수비 시 아군 피해 -20%,
공격시 상대 병종에/수비시 자신 병종 숙련에 보병 숙련을 가산';
protected $cost = 100;
protected $buyable = true;
protected $consumable = false;
@@ -32,4 +32,18 @@ class event_전투특기_보병 extends \sammo\BaseItem{
}
return [1, 0.8];
}
+
+ public function onCalcStat(General $general, string $statName, $value, $aux=null){
+ if(\sammo\Util::starts_with($statName, 'dex')){
+ $myArmType = 'dex'.GameUnitConst::T_FOOTMAN;
+ $opposeArmType = 'dex'.$aux['opposeType']->armType;;
+ if($aux['isAttacker'] && $opposeArmType === $statName){
+ return $value + $general->getVar($myArmType);
+ }
+ if(!$aux['isAttacker'] && $myArmType === $statName){
+ return $value + $general->getVar($myArmType);
+ }
+ }
+ return $value;
+ }
}
\ No newline at end of file
diff --git a/hwe/sammo/ActionItem/event_전투특기_위압.php b/hwe/sammo/ActionItem/event_전투특기_위압.php
index 2956e1ab..ecd506b4 100644
--- a/hwe/sammo/ActionItem/event_전투특기_위압.php
+++ b/hwe/sammo/ActionItem/event_전투특기_위압.php
@@ -13,7 +13,7 @@ class event_전투특기_위압 extends \sammo\BaseItem{
protected $id = 63;
protected $rawName = '비급';
protected $name = '비급(위압)';
- protected $info = '[전투] 훈련/사기≥90, 병력≥1,000 일 때 첫 페이즈 위압 발동(적 공격 불가)';
+ protected $info = '[전투] 첫 페이즈 위압 발동(적 공격, 회피 불가, 사기 5 감소)';
protected $cost = 100;
protected $buyable = true;
protected $consumable = false;
diff --git a/hwe/sammo/ActionItem/event_전투특기_의술.php b/hwe/sammo/ActionItem/event_전투특기_의술.php
index a7f361bf..654c08cd 100644
--- a/hwe/sammo/ActionItem/event_전투특기_의술.php
+++ b/hwe/sammo/ActionItem/event_전투특기_의술.php
@@ -16,7 +16,7 @@ class event_전투특기_의술 extends \sammo\BaseItem{
protected $id = 73;
protected $rawName = '비급';
protected $name = '비급(의술)';
- protected $info = '[군사] 매 턴마다 자신(100%)과 소속 도시 장수(적 포함 50%) 부상 회복
[전투] 페이즈마다 20% 확률로 치료 발동(아군 피해 1/3 감소)';
+ protected $info = '[군사] 매 턴마다 자신(100%)과 소속 도시 장수(적 포함 50%) 부상 회복
[전투] 페이즈마다 40% 확률로 치료 발동(아군 피해 1/3 감소, 부상 회복)';
protected $cost = 100;
protected $buyable = true;
protected $consumable = false;
diff --git a/hwe/sammo/ActionItem/event_전투특기_저격.php b/hwe/sammo/ActionItem/event_전투특기_저격.php
index 29d11ab2..c52ffe49 100644
--- a/hwe/sammo/ActionItem/event_전투특기_저격.php
+++ b/hwe/sammo/ActionItem/event_전투특기_저격.php
@@ -13,7 +13,7 @@ class event_전투특기_저격 extends \sammo\BaseItem{
protected $id = 70;
protected $rawName = '비급';
protected $name = '비급(저격)';
- protected $info = '[전투] 새로운 상대와 전투 시 1/3 확률로 저격 발동, 성공 시 사기+10';
+ protected $info = '[전투] 새로운 상대와 전투 시 50% 확률로 저격 발동, 성공 시 사기+10';
protected $cost = 100;
protected $buyable = true;
protected $consumable = false;
@@ -21,7 +21,7 @@ class event_전투특기_저격 extends \sammo\BaseItem{
public function getBattlePhaseSkillTriggerList(WarUnit $unit):?WarUnitTriggerCaller{
return new WarUnitTriggerCaller(
- new che_저격시도($unit, che_저격시도::TYPE_ITEM, 1/3, 20, 60),
+ new che_저격시도($unit, che_저격시도::TYPE_ITEM, 0.5, 10, 40),
new che_저격발동($unit, che_저격발동::TYPE_ITEM)
);
}
diff --git a/hwe/sammo/ActionItem/event_전투특기_필살.php b/hwe/sammo/ActionItem/event_전투특기_필살.php
index 7ce2fe1c..27f3fe99 100644
--- a/hwe/sammo/ActionItem/event_전투특기_필살.php
+++ b/hwe/sammo/ActionItem/event_전투특기_필살.php
@@ -12,7 +12,7 @@ class event_전투특기_필살 extends \sammo\BaseItem{
protected $id = 71;
protected $rawName = '비급';
protected $name = '비급(필살)';
- protected $info = '[전투] 필살 확률 +20%p, 필살 발동시 대상 회피 불가';
+ protected $info = '[전투] 필살 확률 +25%p, 필살 발동시 대상 회피 불가';
protected $cost = 100;
protected $buyable = true;
protected $consumable = false;
@@ -20,7 +20,7 @@ class event_전투특기_필살 extends \sammo\BaseItem{
public function onCalcStat(General $general, string $statName, $value, $aux=null){
if($statName === 'warCriticalRatio'){
- return $value + 0.2;
+ return $value + 0.25;
}
return $value;
}
diff --git a/hwe/sammo/ActionSpecialWar/che_격노.php b/hwe/sammo/ActionSpecialWar/che_격노.php
index 116e86e1..8f01139f 100644
--- a/hwe/sammo/ActionSpecialWar/che_격노.php
+++ b/hwe/sammo/ActionSpecialWar/che_격노.php
@@ -1,29 +1,29 @@
-[전투] 성벽 공격 시 대미지 +100%';
-
- static $selectWeightType = SpecialityHelper::WEIGHT_NORM;
- static $selectWeight = 1;
- static $type = [
- SpecialityHelper::STAT_LEADERSHIP | SpecialityHelper::REQ_DEXTERITY | SpecialityHelper::ARMY_SIEGE,
- ];
-
- public function onCalcDomestic(string $turnType, string $varType, float $value, $aux=null):float{
- if(in_array($turnType, ['징병', '모병'])){
- if($varType == 'cost' && $aux['armType'] == GameUnitConst::T_SIEGE) return $value * 0.9;
- }
-
- return $value;
- }
-
- public function getWarPowerMultiplier(WarUnit $unit):array{
- if($unit->getOppose() instanceof WarUnitCity){
- return [2, 1];
- }
- return [1, 1];
- }
+[전투] 성벽 공격 시 대미지 +100%,
공격시 상대 병종에/수비시 자신 병종 숙련에 차병 숙련을 가산';
+
+ static $selectWeightType = SpecialityHelper::WEIGHT_NORM;
+ static $selectWeight = 1;
+ static $type = [
+ SpecialityHelper::STAT_LEADERSHIP | SpecialityHelper::REQ_DEXTERITY | SpecialityHelper::ARMY_SIEGE,
+ ];
+
+ public function onCalcDomestic(string $turnType, string $varType, float $value, $aux=null):float{
+ if(in_array($turnType, ['징병', '모병'])){
+ if($varType == 'cost' && $aux['armType'] == GameUnitConst::T_SIEGE) return $value * 0.9;
+ }
+
+ return $value;
+ }
+
+ public function getWarPowerMultiplier(WarUnit $unit):array{
+ if($unit->getOppose() instanceof WarUnitCity){
+ return [2, 1];
+ }
+ return [1, 1];
+ }
+
+ public function onCalcStat(General $general, string $statName, $value, $aux=null){
+ if(\sammo\Util::starts_with($statName, 'dex')){
+ $myArmType = 'dex'.GameUnitConst::T_CASTLE;
+ $opposeArmType = 'dex'.$aux['opposeType']->armType;;
+ if($aux['isAttacker'] && $opposeArmType === $statName){
+ return $value + $general->getVar($myArmType);
+ }
+ if(!$aux['isAttacker'] && $myArmType === $statName){
+ return $value + $general->getVar($myArmType);
+ }
+ }
+ return $value;
+ }
}
\ No newline at end of file
diff --git a/hwe/sammo/ActionSpecialWar/che_궁병.php b/hwe/sammo/ActionSpecialWar/che_궁병.php
index 1abece8e..c64b0645 100644
--- a/hwe/sammo/ActionSpecialWar/che_궁병.php
+++ b/hwe/sammo/ActionSpecialWar/che_궁병.php
@@ -10,7 +10,7 @@ class che_궁병 extends \sammo\BaseSpecial{
protected $id = 51;
protected $name = '궁병';
- protected $info = '[군사] 궁병 계통 징·모병비 -10%
[전투] 회피 확률 +20%p';
+ protected $info = '[군사] 궁병 계통 징·모병비 -10%
[전투] 회피 확률 +20%p,
공격시 상대 병종에/수비시 자신 병종 숙련에 궁병 숙련을 가산';
static $selectWeightType = SpecialityHelper::WEIGHT_NORM;
static $selectWeight = 1;
@@ -31,6 +31,16 @@ class che_궁병 extends \sammo\BaseSpecial{
if($statName === 'warAvoidRatio'){
return $value + 0.2;
}
+ if(\sammo\Util::starts_with($statName, 'dex')){
+ $myArmType = 'dex'.GameUnitConst::T_ARCHER;
+ $opposeArmType = 'dex'.$aux['opposeType']->armType;;
+ if($aux['isAttacker'] && $opposeArmType === $statName){
+ return $value + $general->getVar($myArmType);
+ }
+ if(!$aux['isAttacker'] && $myArmType === $statName){
+ return $value + $general->getVar($myArmType);
+ }
+ }
return $value;
}
}
\ No newline at end of file
diff --git a/hwe/sammo/ActionSpecialWar/che_귀병.php b/hwe/sammo/ActionSpecialWar/che_귀병.php
index 1c4813fc..aa99c869 100644
--- a/hwe/sammo/ActionSpecialWar/che_귀병.php
+++ b/hwe/sammo/ActionSpecialWar/che_귀병.php
@@ -1,35 +1,45 @@
-[전투] 계략 성공 확률 +20%p';
-
- static $selectWeightType = SpecialityHelper::WEIGHT_NORM;
- static $selectWeight = 1;
- static $type = [
- SpecialityHelper::STAT_INTEL | SpecialityHelper::ARMY_WIZARD | SpecialityHelper::REQ_DEXTERITY | SpecialityHelper::STAT_NOT_STRENGTH
- ];
-
- public function onCalcDomestic(string $turnType, string $varType, float $value, $aux=null):float{
- if(in_array($turnType, ['징병', '모병'])){
- if($varType == 'cost' && $aux['armType'] == GameUnitConst::T_WIZARD) return $value * 0.9;
- }
-
- return $value;
- }
-
- public function onCalcStat(General $general, string $statName, $value, $aux=null){
- if($statName === 'warMagicSuccessProb'){
- return $value + 0.2;
- }
- return $value;
- }
+[전투] 계략 성공 확률 +20%p,
공격시 상대 병종에/수비시 자신 병종 숙련에 귀병 숙련을 가산';
+
+ static $selectWeightType = SpecialityHelper::WEIGHT_NORM;
+ static $selectWeight = 1;
+ static $type = [
+ SpecialityHelper::STAT_INTEL | SpecialityHelper::ARMY_WIZARD | SpecialityHelper::REQ_DEXTERITY | SpecialityHelper::STAT_NOT_STRENGTH
+ ];
+
+ public function onCalcDomestic(string $turnType, string $varType, float $value, $aux=null):float{
+ if(in_array($turnType, ['징병', '모병'])){
+ if($varType == 'cost' && $aux['armType'] == GameUnitConst::T_WIZARD) return $value * 0.9;
+ }
+
+ return $value;
+ }
+
+ public function onCalcStat(General $general, string $statName, $value, $aux=null){
+ if($statName === 'warMagicSuccessProb'){
+ return $value + 0.2;
+ }
+ if(\sammo\Util::starts_with($statName, 'dex')){
+ $myArmType = 'dex'.GameUnitConst::T_WIZARD;
+ $opposeArmType = 'dex'.$aux['opposeType']->armType;;
+ if($aux['isAttacker'] && $opposeArmType === $statName){
+ return $value + $general->getVar($myArmType);
+ }
+ if(!$aux['isAttacker'] && $myArmType === $statName){
+ return $value + $general->getVar($myArmType);
+ }
+ }
+ return $value;
+ }
}
\ No newline at end of file
diff --git a/hwe/sammo/ActionSpecialWar/che_기병.php b/hwe/sammo/ActionSpecialWar/che_기병.php
index 7943224e..683db233 100644
--- a/hwe/sammo/ActionSpecialWar/che_기병.php
+++ b/hwe/sammo/ActionSpecialWar/che_기병.php
@@ -1,36 +1,50 @@
-[전투] 수비 시 대미지 +10%, 공격 시 대미지 +20%';
-
- static $selectWeightType = SpecialityHelper::WEIGHT_NORM;
- static $selectWeight = 1;
- static $type = [
- SpecialityHelper::STAT_LEADERSHIP | SpecialityHelper::REQ_DEXTERITY | SpecialityHelper::ARMY_CAVALRY | SpecialityHelper::STAT_NOT_INTEL,
- SpecialityHelper::STAT_STRENGTH | SpecialityHelper::REQ_DEXTERITY | SpecialityHelper::ARMY_CAVALRY
- ];
-
- public function onCalcDomestic(string $turnType, string $varType, float $value, $aux=null):float{
- if(in_array($turnType, ['징병', '모병'])){
- if($varType == 'cost' && $aux['armType'] == GameUnitConst::T_CAVALRY) return $value * 0.9;
- }
-
- return $value;
- }
-
- public function getWarPowerMultiplier(WarUnit $unit):array{
- if($unit->isAttacker()){
- return [1.2, 1];
- }
- return [1.1, 1];
- }
+[전투] 수비 시 대미지 +10%, 공격 시 대미지 +20%,
공격시 상대 병종에/수비시 자신 병종 숙련에 기병 숙련을 가산';
+
+ static $selectWeightType = SpecialityHelper::WEIGHT_NORM;
+ static $selectWeight = 1;
+ static $type = [
+ SpecialityHelper::STAT_LEADERSHIP | SpecialityHelper::REQ_DEXTERITY | SpecialityHelper::ARMY_CAVALRY | SpecialityHelper::STAT_NOT_INTEL,
+ SpecialityHelper::STAT_STRENGTH | SpecialityHelper::REQ_DEXTERITY | SpecialityHelper::ARMY_CAVALRY
+ ];
+
+ public function onCalcDomestic(string $turnType, string $varType, float $value, $aux=null):float{
+ if(in_array($turnType, ['징병', '모병'])){
+ if($varType == 'cost' && $aux['armType'] == GameUnitConst::T_CAVALRY) return $value * 0.9;
+ }
+
+ return $value;
+ }
+
+ public function getWarPowerMultiplier(WarUnit $unit):array{
+ if($unit->isAttacker()){
+ return [1.2, 1];
+ }
+ return [1.1, 1];
+ }
+
+ public function onCalcStat(General $general, string $statName, $value, $aux=null){
+ if(\sammo\Util::starts_with($statName, 'dex')){
+ $myArmType = 'dex'.GameUnitConst::T_CAVALRY;
+ $opposeArmType = 'dex'.$aux['opposeType']->armType;;
+ if($aux['isAttacker'] && $opposeArmType === $statName){
+ return $value + $general->getVar($myArmType);
+ }
+ if(!$aux['isAttacker'] && $myArmType === $statName){
+ return $value + $general->getVar($myArmType);
+ }
+ }
+ return $value;
+ }
}
\ No newline at end of file
diff --git a/hwe/sammo/ActionSpecialWar/che_돌격.php b/hwe/sammo/ActionSpecialWar/che_돌격.php
index b13f896e..956ba7d1 100644
--- a/hwe/sammo/ActionSpecialWar/che_돌격.php
+++ b/hwe/sammo/ActionSpecialWar/che_돌격.php
@@ -1,42 +1,42 @@
-isAttacker()){
- return [1.1, 1];
- }
- return [1, 1];
- }
-
- public function getBattlePhaseSkillTriggerList(WarUnit $unit):?WarUnitTriggerCaller{
- return new WarUnitTriggerCaller(
- (new WarActivateSkills($unit, BaseWarUnitTrigger::TYPE_NONE, false, '회피불가'))->setPriority(BaseWarUnitTrigger::PRIORITY_BEGIN + 200)
- );
- }
+isAttacker()){
+ return [1.05, 1];
+ }
+ return [1, 1];
+ }
+
+ public function getBattlePhaseSkillTriggerList(WarUnit $unit):?WarUnitTriggerCaller{
+ return new WarUnitTriggerCaller(
+ new che_돌격지속($unit)
+ );
+ }
}
\ No newline at end of file
diff --git a/hwe/sammo/ActionSpecialWar/che_무쌍.php b/hwe/sammo/ActionSpecialWar/che_무쌍.php
index d2852e11..4aa331e6 100644
--- a/hwe/sammo/ActionSpecialWar/che_무쌍.php
+++ b/hwe/sammo/ActionSpecialWar/che_무쌍.php
@@ -1,30 +1,36 @@
-승리 수만큼 대미지 0.20%씩 추가 상승(최대40%)
승리 수만큼 피해 0.05%씩 감소(최대50%)';
+
+ static $selectWeightType = SpecialityHelper::WEIGHT_NORM;
+ static $selectWeight = 1;
+ static $type = [
+ SpecialityHelper::STAT_STRENGTH
+ ];
+
+ public function onCalcStat(General $general, string $statName, $value, $aux=null){
+ if($statName === 'warCriticalRatio' && $aux['isAttacker']??false){
+ return $value += 0.1;
+ }
+ return $value;
+ }
+
+ public function getWarPowerMultiplier(WarUnit $unit):array{
+ $attackMultiplier = 1.1;
+ $defenceMultiplier = 1;
+ $killnum = $unit->getGeneral()->getRankVar('killnum');
+ $attackMultiplier += Util::valueFit($killnum * 0.01 * 0.2, null, 0.4);
+ $defenceMultiplier -= Util::valueFit($killnum * 0.01 * 0.05, null, 0.5);
+ return [$attackMultiplier, $defenceMultiplier];
+ }
}
\ No newline at end of file
diff --git a/hwe/sammo/ActionSpecialWar/che_반계.php b/hwe/sammo/ActionSpecialWar/che_반계.php
index 09d94928..9657fc3c 100644
--- a/hwe/sammo/ActionSpecialWar/che_반계.php
+++ b/hwe/sammo/ActionSpecialWar/che_반계.php
@@ -1,39 +1,39 @@
-[전투] 공격 시 아군 피해 -10%, 수비 시 아군 피해 -20%';
-
- static $selectWeightType = SpecialityHelper::WEIGHT_NORM;
- static $selectWeight = 1;
- static $type = [
- SpecialityHelper::STAT_LEADERSHIP | SpecialityHelper::REQ_DEXTERITY | SpecialityHelper::ARMY_FOOTMAN | SpecialityHelper::STAT_NOT_INTEL,
- SpecialityHelper::STAT_STRENGTH | SpecialityHelper::REQ_DEXTERITY | SpecialityHelper::ARMY_FOOTMAN
- ];
-
- public function onCalcDomestic(string $turnType, string $varType, float $value, $aux=null):float{
- if(in_array($turnType, ['징병', '모병'])){
- if($varType == 'cost' && $aux['armType'] == GameUnitConst::T_FOOTMAN) return $value * 0.9;
- }
-
- return $value;
- }
-
- public function getWarPowerMultiplier(WarUnit $unit):array{
- if($unit->isAttacker()){
- return [1, 0.9];
- }
- return [1, 0.8];
- }
+[전투] 공격 시 아군 피해 -10%, 수비 시 아군 피해 -20%,
공격시 상대 병종에/수비시 자신 병종 숙련에 보병 숙련을 가산';
+
+ static $selectWeightType = SpecialityHelper::WEIGHT_NORM;
+ static $selectWeight = 1;
+ static $type = [
+ SpecialityHelper::STAT_LEADERSHIP | SpecialityHelper::REQ_DEXTERITY | SpecialityHelper::ARMY_FOOTMAN | SpecialityHelper::STAT_NOT_INTEL,
+ SpecialityHelper::STAT_STRENGTH | SpecialityHelper::REQ_DEXTERITY | SpecialityHelper::ARMY_FOOTMAN
+ ];
+
+ public function onCalcDomestic(string $turnType, string $varType, float $value, $aux=null):float{
+ if(in_array($turnType, ['징병', '모병'])){
+ if($varType == 'cost' && $aux['armType'] == GameUnitConst::T_FOOTMAN) return $value * 0.9;
+ }
+
+ return $value;
+ }
+
+ public function getWarPowerMultiplier(WarUnit $unit):array{
+ if($unit->isAttacker()){
+ return [1, 0.9];
+ }
+ return [1, 0.8];
+ }
+
+ public function onCalcStat(General $general, string $statName, $value, $aux=null){
+ if(\sammo\Util::starts_with($statName, 'dex')){
+ $myArmType = 'dex'.GameUnitConst::T_FOOTMAN;
+ $opposeArmType = 'dex'.$aux['opposeType']->armType;;
+ if($aux['isAttacker'] && $opposeArmType === $statName){
+ return $value + $general->getVar($myArmType);
+ }
+ if(!$aux['isAttacker'] && $myArmType === $statName){
+ return $value + $general->getVar($myArmType);
+ }
+ }
+ return $value;
+ }
}
\ No newline at end of file
diff --git a/hwe/sammo/ActionSpecialWar/che_위압.php b/hwe/sammo/ActionSpecialWar/che_위압.php
index 4f3b0408..474f2b25 100644
--- a/hwe/sammo/ActionSpecialWar/che_위압.php
+++ b/hwe/sammo/ActionSpecialWar/che_위압.php
@@ -1,32 +1,29 @@
-getPhase() != 0){
- return null;
- }
- return new WarUnitTriggerCaller(
- new che_위압시도($unit),
- new che_위압발동($unit)
- );
- }
+[전투] 페이즈마다 20% 확률로 치료 발동(아군 피해 1/3 감소)';
-
- static $selectWeightType = SpecialityHelper::WEIGHT_PERCENT;
- static $selectWeight = 2;
- static $type = [
- SpecialityHelper::STAT_LEADERSHIP,
- SpecialityHelper::STAT_STRENGTH,
- SpecialityHelper::STAT_INTEL
- ];
-
- public function getPreTurnExecuteTriggerList(General $general):?GeneralTriggerCaller{
- return new GeneralTriggerCaller(
- new GeneralTrigger\che_도시치료($general)
- );
- }
-
- public function getBattlePhaseSkillTriggerList(\sammo\WarUnit $unit): ?WarUnitTriggerCaller
- {
- return new WarUnitTriggerCaller(
- new che_전투치료시도($unit),
- new che_전투치료발동($unit)
- );
- }
-}
+[전투] 페이즈마다 40% 확률로 치료 발동(아군 피해 1/3 감소, 부상 회복)';
+
+ static $selectWeightType = SpecialityHelper::WEIGHT_PERCENT;
+ static $selectWeight = 2;
+ static $type = [
+ SpecialityHelper::STAT_LEADERSHIP,
+ SpecialityHelper::STAT_STRENGTH,
+ SpecialityHelper::STAT_INTEL
+ ];
+
+ public function getPreTurnExecuteTriggerList(General $general):?GeneralTriggerCaller{
+ return new GeneralTriggerCaller(
+ new GeneralTrigger\che_도시치료($general)
+ );
+ }
+
+ public function getBattlePhaseSkillTriggerList(\sammo\WarUnit $unit): ?WarUnitTriggerCaller
+ {
+ return new WarUnitTriggerCaller(
+ new che_전투치료시도($unit),
+ new che_전투치료발동($unit)
+ );
+ }
+}
diff --git a/hwe/sammo/ActionSpecialWar/che_저격.php b/hwe/sammo/ActionSpecialWar/che_저격.php
index 1754dc4a..96445bdf 100644
--- a/hwe/sammo/ActionSpecialWar/che_저격.php
+++ b/hwe/sammo/ActionSpecialWar/che_저격.php
@@ -1,31 +1,31 @@
-getVar('leadership') * 0.25;
+ }
+ return $value;
+ }
}
\ No newline at end of file
diff --git a/hwe/sammo/ActionSpecialWar/che_척사.php b/hwe/sammo/ActionSpecialWar/che_척사.php
index ee7efc40..e7943194 100644
--- a/hwe/sammo/ActionSpecialWar/che_척사.php
+++ b/hwe/sammo/ActionSpecialWar/che_척사.php
@@ -1,29 +1,29 @@
-getOppose()->getCrewType();
- if($opposeCrewType->reqCities || $opposeCrewType->reqRegions){
- return [1.1, 0.9];
- }
- return [1, 1];
- }
+getOppose()->getCrewType();
+ if($opposeCrewType->reqCities || $opposeCrewType->reqRegions){
+ return [1.15, 0.85];
+ }
+ return [1, 1];
+ }
}
\ No newline at end of file
diff --git a/hwe/sammo/ActionSpecialWar/che_필살.php b/hwe/sammo/ActionSpecialWar/che_필살.php
index 74c2042a..b1f2824e 100644
--- a/hwe/sammo/ActionSpecialWar/che_필살.php
+++ b/hwe/sammo/ActionSpecialWar/che_필살.php
@@ -1,36 +1,36 @@
-getComputedAtmos();
$warPower /= $oppose->getComputedTrain();
- $genDexAtt = $this->getDex($this->getCrewType());
+ $genDexAtt = $this->getDex($this->getCrewType(), true);
- $oppDexDef = $oppose->getDex($this->getCrewType());
+ $oppDexDef = $oppose->getDex($this->getCrewType(), false);
$warPower *= getDexLog($genDexAtt, $oppDexDef);
diff --git a/hwe/sammo/WarUnitCity.php b/hwe/sammo/WarUnitCity.php
index edc4971d..6744ae80 100644
--- a/hwe/sammo/WarUnitCity.php
+++ b/hwe/sammo/WarUnitCity.php
@@ -1,165 +1,165 @@
-setVar('city', $raw['city']);
- $general->setVar('nation', $raw['nation']);
- $general->initLogger($year, $month);
- $this->general = $general;
- $this->raw = $raw;
- $this->rawNation = $rawNation;
-
- $this->isAttacker = false;
- $this->cityRate = $cityRate;
-
- $this->logger = $general->getLogger();
- $this->crewType = GameUnitConst::byID(GameUnitConst::CREWTYPE_CASTLE);
-
- $this->hp = $this->getCityVar('def') * 10;
-
- //수비자 보정
- if($this->getCityVar('level') == 1){
- $this->trainBonus += 5;
- }
- else if($this->getCityVar('level') == 3){
- $this->trainBonus += 5;
- }
- }
-
- function getName():string{
- return $this->getVar('name');
- }
-
- function getCityVar(string $key){
- return $this->raw[$key];
- }
-
- function getComputedAttack(){
- return ($this->raw['def'] + $this->raw['wall'] * 9) / 500 + 200;
- }
-
- function getComputedDefence(){
- return ($this->raw['def'] + $this->raw['wall'] * 9) / 500 + 200;
- }
-
- function increaseKilled(int $damage):int{
- $this->killed += $damage;
- $this->killedCurr += $damage;
- return $this->killed;
- }
-
- function getComputedTrain(){
- return $this->cityRate + $this->trainBonus;
- }
-
- function getComputedAtmos(){
- return $this->cityRate + $this->atmosBonus;
- }
-
- function getHP():int{
- return $this->hp;
- }
-
- function getDex(GameUnitDetail $crewType){
- return ($this->cityRate - 60) * 7200;
- }
-
- function decreaseHP(int $damage):int{
- $damage = min($damage, $this->hp);
- $this->dead += $damage;
- $this->deadCurr += $damage;
- $this->hp -= $damage;
- $this->increaseVarWithLimit('wall', -$damage/20, 0);
-
- return $this->hp;
- }
-
- function continueWar(&$noRice):bool{
- //전투가 가능하면 true
- $noRice = false;
- if($this->getHP() <= 0){
- return false;
- }
-
- //도시 성벽은 쌀이 소모된다고 항복하지 않음
- return true;
- }
-
- function heavyDecreaseWealth(){
- $this->multiplyVar('agri', 0.5);
- $this->multiplyVar('comm', 0.5);
- $this->multiplyVar('secu', 0.5);
- }
-
- function finishBattle(){
- if($this->isFinished){
- return;
- }
- $this->clearActivatedSkill();
- $this->isFinished = true;
-
- $this->updateVar('def', Util::round($this->getHP() / 10));
- $this->updateVar('wall', Util::round($this->getVar('wall')));
-
- //NOTE: 전투로 인한 사망자는 여기서 처리하지 않음
-
- $decWealth = $this->getKilled() / 20;
- $this->increaseVarWithLimit('agri', -$decWealth, 0);
- $this->increaseVarWithLimit('comm', -$decWealth, 0);
- $this->increaseVarWithLimit('secu', -$decWealth, 0);
- }
-
- function addConflict():bool{
- $conflict = Json::decode($this->getVar('conflict'));
- $oppose = $this->getOppose();
-
- $nationID = $oppose->getNationVar('nation');
- $newConflict = false;
-
- $dead = max(1, $this->dead);
-
- if(!$conflict || $this->getHP() == 0){ // 선타, 막타 보너스
- $dead *= 1.05;
- }
-
- if(!$conflict){
- $conflict = [$nationID => $dead];
- }
- else if(key_exists($nationID, $conflict)){
- $conflict[$nationID] += $dead;
- arsort($conflict);
- }
- else{
- $conflict[$nationID] = $dead;
- arsort($conflict);
- $newConflict = true;
- }
-
- $this->updateVar('conflict', Json::encode($conflict));
-
- return $newConflict;
- }
-
-
- function applyDB(\MeekroDB $db):bool{
- $updateVals = $this->getUpdatedValues();
- $this->getLogger()->rollback(); //수비 도시의 로그는 기록하지 않음
-
- if(!$updateVals){
- return false;
- }
-
- $db->update('city', $updateVals, 'city=%i', $this->raw['city']);
- $this->flushUpdateValues();
- return $db->affectedRows() > 0;
- }
-
+setVar('city', $raw['city']);
+ $general->setVar('nation', $raw['nation']);
+ $general->initLogger($year, $month);
+ $this->general = $general;
+ $this->raw = $raw;
+ $this->rawNation = $rawNation;
+
+ $this->isAttacker = false;
+ $this->cityRate = $cityRate;
+
+ $this->logger = $general->getLogger();
+ $this->crewType = GameUnitConst::byID(GameUnitConst::CREWTYPE_CASTLE);
+
+ $this->hp = $this->getCityVar('def') * 10;
+
+ //수비자 보정
+ if($this->getCityVar('level') == 1){
+ $this->trainBonus += 5;
+ }
+ else if($this->getCityVar('level') == 3){
+ $this->trainBonus += 5;
+ }
+ }
+
+ function getName():string{
+ return $this->getVar('name');
+ }
+
+ function getCityVar(string $key){
+ return $this->raw[$key];
+ }
+
+ function getComputedAttack(){
+ return ($this->raw['def'] + $this->raw['wall'] * 9) / 500 + 200;
+ }
+
+ function getComputedDefence(){
+ return ($this->raw['def'] + $this->raw['wall'] * 9) / 500 + 200;
+ }
+
+ function increaseKilled(int $damage):int{
+ $this->killed += $damage;
+ $this->killedCurr += $damage;
+ return $this->killed;
+ }
+
+ function getComputedTrain(){
+ return $this->cityRate + $this->trainBonus;
+ }
+
+ function getComputedAtmos(){
+ return $this->cityRate + $this->atmosBonus;
+ }
+
+ function getHP():int{
+ return $this->hp;
+ }
+
+ function getDex(GameUnitDetail $crewType){
+ return ($this->cityRate - 60) * 7200;
+ }
+
+ function decreaseHP(int $damage):int{
+ $damage = min($damage, $this->hp);
+ $this->dead += $damage;
+ $this->deadCurr += $damage;
+ $this->hp -= $damage;
+ $this->increaseVarWithLimit('wall', -$damage/20, 0);
+
+ return $this->hp;
+ }
+
+ function continueWar(&$noRice):bool{
+ //전투가 가능하면 true
+ $noRice = false;
+ if($this->getHP() <= 0){
+ return false;
+ }
+
+ //도시 성벽은 쌀이 소모된다고 항복하지 않음
+ return true;
+ }
+
+ function heavyDecreaseWealth(){
+ $this->multiplyVar('agri', 0.5);
+ $this->multiplyVar('comm', 0.5);
+ $this->multiplyVar('secu', 0.5);
+ }
+
+ function finishBattle(){
+ if($this->isFinished){
+ return;
+ }
+ $this->clearActivatedSkill();
+ $this->isFinished = true;
+
+ $this->updateVar('def', Util::round($this->getHP() / 10));
+ $this->updateVar('wall', Util::round($this->getVar('wall')));
+
+ //NOTE: 전투로 인한 사망자는 여기서 처리하지 않음
+
+ $decWealth = $this->getKilled() / 20;
+ $this->increaseVarWithLimit('agri', -$decWealth, 0);
+ $this->increaseVarWithLimit('comm', -$decWealth, 0);
+ $this->increaseVarWithLimit('secu', -$decWealth, 0);
+ }
+
+ function addConflict():bool{
+ $conflict = Json::decode($this->getVar('conflict'));
+ $oppose = $this->getOppose();
+
+ $nationID = $oppose->getNationVar('nation');
+ $newConflict = false;
+
+ $dead = max(1, $this->dead);
+
+ if(!$conflict || $this->getHP() == 0){ // 선타, 막타 보너스
+ $dead *= 1.05;
+ }
+
+ if(!$conflict){
+ $conflict = [$nationID => $dead];
+ }
+ else if(key_exists($nationID, $conflict)){
+ $conflict[$nationID] += $dead;
+ arsort($conflict);
+ }
+ else{
+ $conflict[$nationID] = $dead;
+ arsort($conflict);
+ $newConflict = true;
+ }
+
+ $this->updateVar('conflict', Json::encode($conflict));
+
+ return $newConflict;
+ }
+
+
+ function applyDB(\MeekroDB $db):bool{
+ $updateVals = $this->getUpdatedValues();
+ $this->getLogger()->rollback(); //수비 도시의 로그는 기록하지 않음
+
+ if(!$updateVals){
+ return false;
+ }
+
+ $db->update('city', $updateVals, 'city=%i', $this->raw['city']);
+ $this->flushUpdateValues();
+ return $db->affectedRows() > 0;
+ }
+
}
\ No newline at end of file
diff --git a/hwe/sammo/WarUnitGeneral.php b/hwe/sammo/WarUnitGeneral.php
index 4271ef9d..e1b8aed3 100644
--- a/hwe/sammo/WarUnitGeneral.php
+++ b/hwe/sammo/WarUnitGeneral.php
@@ -62,7 +62,7 @@ class WarUnitGeneral extends WarUnit{
function getMaxPhase():int{
$phase = $this->getCrewType()->speed;
- $phase = $this->general->onCalcStat($this->general, 'initWarPhase', $phase);
+ $phase = $this->general->onCalcStat($this->general, 'initWarPhase', $phase, ['isAttacker'=>$this->isAttacker]);
return $phase + $this->bonusPhase;
}
@@ -75,12 +75,16 @@ class WarUnitGeneral extends WarUnit{
}
function getDex(GameUnitDetail $crewType){
- return $this->general->getDex($crewType);
+ $rawDex = $this->general->getDex($crewType);
+ return $this->general->onCalcStat($this->general, 'dex'.$crewType->armType, $rawDex, [
+ 'isAttacker'=>$this->isAttacker,
+ 'opposeType'=>$this->oppose->getCrewType()
+ ]);
}
function getComputedTrain(){
$train = $this->general->getVar('train');
- $train = $this->general->onCalcStat($this->general, 'bonusTrain', $train);
+ $train = $this->general->onCalcStat($this->general, 'bonusTrain', $train, ['isAttacker'=>$this->isAttacker]);
$train += $this->trainBonus;
return $train;
@@ -88,7 +92,7 @@ class WarUnitGeneral extends WarUnit{
function getComputedAtmos(){
$atmos = $this->general->getVar('atmos');
- $atmos = $this->general->onCalcStat($this->general, 'bonusAtmos', $atmos);
+ $atmos = $this->general->onCalcStat($this->general, 'bonusAtmos', $atmos, ['isAttacker'=>$this->isAttacker]);
$atmos += $this->atmosBonus;
return $atmos;
diff --git a/hwe/sammo/WarUnitTrigger/che_격노시도.php b/hwe/sammo/WarUnitTrigger/che_격노시도.php
index 6b0b3264..d4b39d42 100644
--- a/hwe/sammo/WarUnitTrigger/che_격노시도.php
+++ b/hwe/sammo/WarUnitTrigger/che_격노시도.php
@@ -1,41 +1,38 @@
-hasActivatedSkill('필살') && !$oppose->hasActivatedSkill('회피')){
- return true;
- }
- if($self->hasActivatedSkill('격노불가')){
- return true;
- }
-
- if($self->isAttacker()){
- if(Util::randBool(1/3)){
- $self->activateSkill('진노', '격노');
- $oppose->deactivateSkill('회피');
- }
- else if(Util::randBool(1/4)){
- $self->activateSkill('격노');
- $oppose->deactivateSkill('회피');
- }
- }
- else{
- if(Util::randBool(1/2)){
- $self->activateSkill('격노');
- $oppose->deactivateSkill('회피');
- }
- }
-
- return true;
- }
+hasActivatedSkill('필살') && !$oppose->hasActivatedSkill('회피')){
+ return true;
+ }
+ if($self->hasActivatedSkill('격노불가')){
+ return true;
+ }
+
+ if($oppose->hasActivatedSkill('필살')){
+ $self->activateSkill('격노');
+ $oppose->deactivateSkill('회피');
+ if($self->isAttacker() && Util::randBool(1/3)){
+ $self->activateSkill('진노');
+ }
+ }
+ else if(Util::randBool(1/4)){
+ $self->activateSkill('격노');
+ $oppose->deactivateSkill('회피');
+ if($self->isAttacker() && Util::randBool(1/3)){
+ $self->activateSkill('진노');
+ }
+ }
+ return true;
+ }
}
\ No newline at end of file
diff --git a/hwe/sammo/WarUnitTrigger/che_돌격지속.php b/hwe/sammo/WarUnitTrigger/che_돌격지속.php
new file mode 100644
index 00000000..74ffa719
--- /dev/null
+++ b/hwe/sammo/WarUnitTrigger/che_돌격지속.php
@@ -0,0 +1,33 @@
+isAttacker()){
+ return true;
+ }
+ if($self->getPhase() < $self->getMaxPhase() - 1){
+ return true;
+ }
+ $attackCoef = $self->getCrewType()->getAttackCoef($oppose->getCrewType());
+ if($attackCoef < 1){
+ return true;
+ }
+ $self->addBonusPhase(1);
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/hwe/sammo/WarUnitTrigger/che_위압발동.php b/hwe/sammo/WarUnitTrigger/che_위압발동.php
index ce0dcce5..44817dac 100644
--- a/hwe/sammo/WarUnitTrigger/che_위압발동.php
+++ b/hwe/sammo/WarUnitTrigger/che_위압발동.php
@@ -1,26 +1,27 @@
-hasActivatedSkill('위압')){
- return true;
- }
-
- $oppose->getLogger()->pushGeneralBattleDetailLog('상대에게 위압>받았다!>', ActionLogger::PLAIN);
- $self->getLogger()->pushGeneralBattleDetailLog('상대에게 위압>을 줬다!>', ActionLogger::PLAIN);
- $oppose->setWarPowerMultiply(0);
-
- return true;
- }
+hasActivatedSkill('위압')){
+ return true;
+ }
+
+ $oppose->getLogger()->pushGeneralBattleDetailLog('상대에게 위압>받았다!>', ActionLogger::PLAIN);
+ $self->getLogger()->pushGeneralBattleDetailLog('상대에게 위압>을 줬다!>', ActionLogger::PLAIN);
+ $oppose->setWarPowerMultiply(0);
+ $oppose->increaseVarWithLimit('atmos', -5, 40);
+
+ return true;
+ }
}
\ No newline at end of file
diff --git a/hwe/sammo/WarUnitTrigger/che_위압시도.php b/hwe/sammo/WarUnitTrigger/che_위압시도.php
index 5327e3b0..fbf73e99 100644
--- a/hwe/sammo/WarUnitTrigger/che_위압시도.php
+++ b/hwe/sammo/WarUnitTrigger/che_위압시도.php
@@ -1,38 +1,30 @@
-getPhase() != 0){
- return true;
- }
- if($self->getHP() < 1000){
- return true;
- }
- if($self->getComputedAtmos() < 90){
- return true;
- }
- if($self->getComputedTrain() < 90){
- return true;
- }
- if($self->hasActivatedSkill('위압불가')){
- return true;
- }
-
- $self->activateSkill('위압');
- return true;
- }
+getPhase() !== 0 && $oppose->getPhase() !== 0){
+ return true;
+ }
+ if($self->hasActivatedSkill('위압불가')){
+ return true;
+ }
+
+ $self->activateSkill('위압');
+ $oppose->activateSkill('회피불가', '필살불가', '계략불가');
+ return true;
+ }
}
\ No newline at end of file
diff --git a/hwe/sammo/WarUnitTrigger/che_전투치료발동.php b/hwe/sammo/WarUnitTrigger/che_전투치료발동.php
index cd95631c..b693bf39 100644
--- a/hwe/sammo/WarUnitTrigger/che_전투치료발동.php
+++ b/hwe/sammo/WarUnitTrigger/che_전투치료발동.php
@@ -1,34 +1,35 @@
-hasActivatedSkill('치료')){
- return true;
- }
-
- if($selfEnv['치료발동']??false){
- return true;
- }
- $selfEnv['치료발동'] = true;
-
- $oppose->getLogger()->pushGeneralBattleDetailLog("상대가 치료>했다!", ActionLogger::PLAIN);
- $self->getLogger()->pushGeneralBattleDetailLog("치료>했다!", ActionLogger::PLAIN);
-
- $oppose->multiplyWarPowerMultiply(1/1.5);
-
- $this->processConsumableItem();
-
- return true;
- }
+hasActivatedSkill('치료')){
+ return true;
+ }
+
+ if($selfEnv['치료발동']??false){
+ return true;
+ }
+ $selfEnv['치료발동'] = true;
+
+ $oppose->getLogger()->pushGeneralBattleDetailLog("상대가 치료>했다!", ActionLogger::PLAIN);
+ $self->getLogger()->pushGeneralBattleDetailLog("치료>했다!", ActionLogger::PLAIN);
+
+ $oppose->multiplyWarPowerMultiply(0.5);
+ $self->getGeneral()->setVar('injury', 0);
+
+ $this->processConsumableItem();
+
+ return true;
+ }
}
\ No newline at end of file
diff --git a/hwe/sammo/WarUnitTrigger/che_전투치료시도.php b/hwe/sammo/WarUnitTrigger/che_전투치료시도.php
index 421b6478..199b533e 100644
--- a/hwe/sammo/WarUnitTrigger/che_전투치료시도.php
+++ b/hwe/sammo/WarUnitTrigger/che_전투치료시도.php
@@ -1,31 +1,31 @@
-hasActivatedSkill('치료')){
- return true;
- }
- if($self->hasActivatedSkill('치료불가')){
- return true;
- }
- if(!Util::randBool(1/5)){
- return true;
- }
-
- $self->activateSkill('치료');
-
-
- return true;
- }
+hasActivatedSkill('치료')){
+ return true;
+ }
+ if($self->hasActivatedSkill('치료불가')){
+ return true;
+ }
+ if(!Util::randBool(0.4)){
+ return true;
+ }
+
+ $self->activateSkill('치료');
+
+
+ return true;
+ }
}
\ No newline at end of file