setReadOnly(); $userID = Session::getUserID(); $query = Util::getReq('query'); if($query === null){ Json::die([ 'result'=>false, 'reason'=>'입력값이 없습니다.' ]); } $action = Util::getReq('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'] = date('Y-m-d H:i:s'); $rawAttackerCity = $query['attackerCity']; $rawAttackerNation = $query['attackerNation']; $defenderList = $query['defenderGenerals']; $rawDefenderCity = $query['defenderCity']; $rawDefenderNation = $query['defenderNation']; $generalCheck = [ 'required'=>[ 'no', 'name', 'nation', 'turntime', 'personal', 'special2', 'crew', 'crewtype', 'atmos', 'train', 'intel', 'intel2', 'book', 'power', 'power2', 'weap', 'injury', 'leader', 'leader2', 'horse', 'item', 'explevel', 'experience', 'dedication', 'level', 'gold', 'rice', 'dex0', 'dex10', 'dex20', 'dex30', 'dex40', 'warnum', 'killnum', 'deathnum', 'killcrew', 'deathcrew', 'recwar' ], 'integer'=>[ 'no', 'nation', 'personal', 'special2', 'crew', 'crewtype', 'atmos', 'train', 'intel', 'intel2', 'book', 'power', 'power2', 'weap', 'injury', 'leader', 'leader2', 'horse', 'item', 'explevel', 'experience', 'dedication', 'level', 'gold', 'rice', 'dex0', 'dex10', 'dex20', 'dex30', 'dex40', 'warnum', 'killnum', 'deathnum', 'killcrew', 'deathcrew' ], 'min'=>[ ['no', 1], ['nation', 1], ['crew', 0], ['intel', 0], ['power', 0], ['leader', 0], ['experience', 0], ['gold', 0], ['rice', 0], ['dex0', 0], ['dex10', 0], ['dex20', 0], ['dex30', 0], ['dex40', 0], ], 'between'=>[ ['train', [40, GameConst::$maxTrainByWar]], ['atmos', [40, GameConst::$maxAtmosByWar]], ['book', [0, 26]], ['weap', [0, 26]], ['horse', [0, 26]], ['item', [0, 26]], ['explevel', [0, 300]], ['injury', [0, 80]], ['level', [1, 12]] ], 'in'=>[ ['personal', array_keys(getCharacterList())], ['special2', array_merge(array_keys(SpecialityConst::WAR), [0])], ['crewtype', array_keys(GameUnitConst::all())], ] ]; $v = new Validator($rawAttacker); $v->rules($generalCheck); if(!$v->validate()){ Json::die([ 'result'=>false, 'reason'=>'[출병자]'.$v->errorStr() ]); } foreach($defenderList as $idx=>$rawDefenderGeneral){ $v = new Validator($rawDefenderGeneral); $v->rules($generalCheck); if(!$v->validate()){ $idx+=1; Json::die([ 'result'=>false, 'reason'=>"[수비자{$idx}]".$v->errorStr() ]); } } $cityCheck = [ 'required'=>[ 'city', 'nation', 'supply', 'name', 'pop', 'agri', 'comm', 'secu', 'def', 'wall', 'rate', 'level', 'pop2', 'agri2', 'comm2', 'secu2', 'def2', 'wall2', 'dead', 'state', 'gen1', 'gen2', 'gen3', 'conflict', ], 'numeric'=>[ 'pop', 'agri', 'comm', 'secu', 'def', 'wall', 'rate', 'dead' ], 'integer'=>[ 'city', 'nation', 'supply', 'pop2', 'agri2', 'comm2', 'secu2', 'def2', 'wall2', 'state', 'gen1', 'gen2', 'gen3' ], 'min'=>[ ['def', 0], ['wall', 0], ['rate', 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', 'totaltech', 'gennum' ], 'integer'=>[ 'type', 'level', 'capital', 'nation', 'gennum', ], 'numeric'=>[ 'tech', 'gold', 'rice', 'totaltech' ], 'min'=>[ ['tech', 0], ['totaltech', 0], ['gold', 0], ['rice', 0], ['gennum', 1], ['gen1', 0], ['gen2', 0], ['gen3', 0], ], 'in'=>[ ['type', array_keys(getNationTypeList())], ['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($lhs, $rhs){ return -(extractBattleOrder($lhs) <=> extractBattleOrder($rhs)); }); $order = []; foreach($defenderList as $rawDefenderGeneral){ $order[] = $rawDefenderGeneral['no']; } Json::die([ 'result'=>true, 'reason'=>'success', 'order'=>$order ]); } usort($defenderList, function($lhs, $rhs){ return -(extractBattleOrder($lhs) <=> extractBattleOrder($rhs)); }); $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, $defenderList, $rawDefenderCity, $rawDefenderNation, $startYear, $year, $month, $cityRate ){ $attacker = new WarUnitGeneral($rawAttacker, $rawAttackerCity, $rawAttackerNation, true, $year, $month); $city = new WarUnitCity($rawDefenderCity, $rawDefenderNation, $year, $month, $cityRate); $iterDefender = new \ArrayIterator($defenderList); $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; } $rawGeneral = $iterDefender->current(); if(extractBattleOrder($rawGeneral) <= 0){ return null; } $defenderRice += $rawGeneral['rice']; $retVal = new WarUnitGeneral($rawGeneral, $rawDefenderCity, $rawDefenderNation, false, $year, $month); $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(range(1, $repeatCnt) as $repeatIdx){ [$attacker, $city, $battleResult, $conquerCity, $attackerRice, $defenderRice] = simulateBattle( $rawAttacker, $rawAttackerCity, $rawAttackerNation, $defenderList, $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, ]);