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 = ; -
+
귀병숙련
@@ -460,11 +460,27 @@ var 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