0, 'name'=>'재야', 'color'=>'#000000', 'type'=>GameConst::$neutralNationType, 'level'=>0, 'capital'=>0, 'gold'=>0, 'rice'=>2000, 'tech'=>0, 'gennum'=>1 ]; } if($nationList === null){ $nationAll = DB::db()->query("select nation, name, color, type, level, capital, gennum, power from nation"); $nationList = Util::convertArrayToDict($nationAll, "nation"); } if($nationID === -1){ return $nationList; } if(isset($nationList[$nationID])){ return $nationList[$nationID]; } return null; } /** * getNationStaticInfo() 함수의 국가 캐시를 초기화 */ function refreshNationStaticInfo(){ getNationStaticInfo(null, true); } /** * getNationStaticInfo(-1) 의 단축형 */ function getAllNationStaticInfo(){ return getNationStaticInfo(-1); } function GetImageURL($imgsvr, $filepath='') { if($imgsvr == 0) { return ServConfig::getSharedIconPath($filepath); } else { return AppConf::getUserIconPathWeb($filepath); } } /** * @param null|int $con 장수의 벌점 * @param null|int $conlimit 최대 벌점 */ function checkLimit($con = null) { $session = Session::getInstance(); if($session->userGrade>=4){ return 0; } $db = DB::db(); $gameStor = KVStorage::getStorage($db, 'game_env'); if($con === null){ $con = $db->queryFirstField('SELECT con FROM general WHERE `owner`=%i', Session::getUserID()); } $conlimit = $gameStor->conlimit; if($con > $conlimit) { return 2; //접속제한 90%이면 경고문구 } elseif($con > $conlimit * 0.9) { return 1; } else { return 0; } } function getBlockLevel() { return DB::db()->queryFirstField('select block from general where no = %i', Session::getInstance()->generalID); } function getRandGenName() { $firstname = Util::choiceRandom(GameConst::$randGenFirstName); $middlename = Util::choiceRandom(GameConst::$randGenMiddleName); $lastname = Util::choiceRandom(GameConst::$randGenLastName); return "{$firstname}{$middlename}{$lastname}"; } function cityInfo(General $generalObj) { $db = DB::db(); // 도시 정보 $city = $generalObj->getRawCity(); $nation = getNationStaticInfo($city['nation']); if(!$nation){ $nation = getNationStaticInfo(0); } $city['nationName'] = $nation['name']; $city['nationTextColor'] = newColor($nation['color']); $city['nationColor'] = $nation['color']; $city['region'] = CityConst::$regionMap[$city['region']]; $city['levelText'] = CityConst::$levelMap[$city['level']]; $officerQuery = []; $officerName = [ 2=>'-', 3=>'-', 4=>'-' ]; if ($city['officer4'] > 0) { $officerQuery[] = $city['officer4']; } if ($city['officer3'] > 0) { $officerQuery[] = $city['officer3']; } if ($city['officer2'] > 0) { $officerQuery[] = $city['officer2']; } if($officerQuery){ foreach($db->query('SELECT `level`, `name` FROM general WHERE `no` IN %li', $officerQuery) as $genOfficer){ $officerName[$genOfficer['level']] = $genOfficer['name']; } } $city['officerName'] = $officerName; $templates = new \League\Plates\Engine('templates'); $templates->registerFunction('bar', '\sammo\bar'); return $templates->render('mainCityInfo', $city); } function myNationInfo() { $db = DB::db(); $gameStor = KVStorage::getStorage($db, 'game_env'); $connect=$db->get(); $userID = Session::getUserID(); $admin = $gameStor->getValues(['startyear','year']); $query = "select no,nation from general where owner='{$userID}'"; $result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); $me = MYDB_fetch_array($result); $query = "select nation,name,color,power,msg,gold,rice,bill,rate,scout,war,strategic_cmd_limit,surlimit,tech,level,type from nation where nation='{$me['nation']}'"; $result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); $nation = MYDB_fetch_array($result); $query = "select COUNT(*) as cnt, SUM(pop) as totpop, SUM(pop_max) as maxpop from city where nation='{$nation['nation']}'"; // 도시 이름 목록 $result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); $city = MYDB_fetch_array($result); $query = "select COUNT(*) as cnt, SUM(crew) as totcrew,SUM(leadership)*100 as maxcrew from general where nation='{$nation['nation']}'"; // 장수 목록 $result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); $general = MYDB_fetch_array($result); $query = "select name from general where nation='{$nation['nation']}' and level='12'"; $genresult = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); $level12 = MYDB_fetch_array($genresult); $query = "select name from general where nation='{$nation['nation']}' and level='11'"; $genresult = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); $level11 = MYDB_fetch_array($genresult); echo "
【재 야】"; } else { echo "style='color:".newColor($nation['color']).";background-color:{$nation['color']};font-weight:bold;font-size:13px;text-align:center'>국가【 {$nation['name']} 】"; } echo "
성 향 ".getNationType($nation['type'])." (".getNationType2($nation['type']).")
".getLevelText(12, $nation['level'])." ";echo $level12?$level12['name']:"-"; echo " ".getLevelText(11, $nation['level'])." ";echo $level11?$level11['name']:"-"; echo "
총주민 ";echo $me['nation']==0?"해당 없음":"{$city['totpop']}/{$city['maxpop']}";echo " 총병사 ";echo $me['nation']==0?"해당 없음":"{$general['totcrew']}/{$general['maxcrew']}"; echo "
국 고 ";echo $me['nation']==0?"해당 없음":"{$nation['gold']}";echo " 병 량 ";echo $me['nation']==0?"해당 없음":"{$nation['rice']}";echo "
지급률 "; if($me['nation'] == 0) { echo "해당 없음"; } else { echo $nation['bill']==0?"0 %":"{$nation['bill']} %"; } echo " 세 율 "; if($me['nation'] == 0) { echo "해당 없음"; } else { echo $nation['rate']==0?"0 %":"{$nation['rate']} %"; } $techCall = getTechCall($nation['tech']); if(TechLimit($admin['startyear'], $admin['year'], $nation['tech'])) { $nation['tech'] = "".floor($nation['tech']).""; } else { $nation['tech'] = "".floor($nation['tech']).""; } $nation['tech'] = "$techCall / {$nation['tech']}"; if($me['nation']==0){ $nation['strategic_cmd_limit'] = "해당 없음"; $nation['surlimit'] = "해당 없음"; $nation['scout'] = "해당 없음"; $nation['war'] = "해당 없음"; $nation['power'] = "해당 없음"; } else { if($nation['strategic_cmd_limit'] != 0) { $nation['strategic_cmd_limit'] = "{$nation['strategic_cmd_limit']}턴"; } else { $nation['strategic_cmd_limit'] = "가 능"; } if($nation['surlimit'] != 0) { $nation['surlimit'] = "{$nation['surlimit']}턴"; } else { $nation['surlimit'] = "가 능"; } if($nation['scout'] != 0) { $nation['scout'] = "금 지"; } else { $nation['scout'] = "허 가"; } if($nation['war'] != 0) { $nation['war'] = "금 지"; } else { $nation['war'] = "허 가"; } } echo "
속 령 ";echo $me['nation']==0?"-":"{$city['cnt']}"; echo " 장 수 ";echo $me['nation']==0?"-":"{$general['cnt']}"; echo "
국 력 {$nation['power']} 기술력 ";echo $me['nation']==0?"-":"{$nation['tech']}"; echo "
전 략 {$nation['strategic_cmd_limit']} 외 교 {$nation['surlimit']}
임 관 {$nation['scout']} 전 쟁 {$nation['war']}
"; } function checkSecretMaxPermission($penalty){ $secretMax = 4; if($penalty['noTopSecret']??false){ $secretMax = 1; } else if($penalty['noChief']??false){ $secretMax = 1; } else if($penalty['noAmbassador']??false){ $secretMax = 2; } return $secretMax; } function checkSecretPermission($me, $checkSecretLimit=true){ if(!key_exists('penalty', $me) || !key_exists('permission', $me)){ trigger_error ('canAccessSecret() 함수에 필요한 인자가 부족'); } $penalty = Json::decode($me['penalty'])??[]; $permission = $me['permission']; if(!$me['nation']){ return -1; } if($me['level'] == 0){ return -1; } if($penalty['noSecret']??false){ return 0; } $secretMin = 0; $secretMax = checkSecretMaxPermission($me, $penalty); if($me['level'] == 12){ $secretMin = 4; } else if($me['permission'] == 'ambassador'){ $secretMin = 4; } else if($me['permission'] == 'auditor'){ $secretMin = 3; } else if($me['level'] >= 5){ $secretMin = 2; } else if($me['level'] > 1){ $secretMin = 1; } else if($checkSecretLimit){ $db = DB::db(); $secretLimit = $db->queryFirstField('SELECT secretlimit FROM nation WHERE nation = %i', $me['nation']); if ($me['belong'] >= $secretLimit) { $secretMin = 1; } } return min($secretMin, $secretMax); } function addCommand($typename, $value, $valid = 1, $color=0) { if($valid == 1) { switch($color) { case 0: echo " "; break; case 1: echo " "; break; case 2: echo " "; break; } } else { echo " "; } } function commandGroup($typename, $type=0) { if($type == 0) { echo " "; } else { echo " "; } } function printCommandTable(General $generalObj) { $db = DB::db(); $gameStor = KVStorage::getStorage($db, 'game_env'); $userID = Session::getUserID(); $gameStor->turnOnCache(); $env = $gameStor->getAll(); ?> turnOnCache(); $env = $gameStor->getAll(); ?> develcost; $me = $db->queryFirstRow('select no,nation,city,level from general where owner=%i', $userID); $nation = $db->queryFirstRow('SELECT level,can_change_flag,gennum FROM nation WHERE nation=%i', $me['nation']); $genCount = $nation['gennum']; $citySupply = $db->queryFirstField('SELECT supply FROM city WHERE city=%i', $me['city']); if($nation['level'] > 0) { $valid = 1; } else { $valid = 0; } if($citySupply == 0) { $valid = 0; } echo " "; } function generalInfo(General $generalObj) { $db = DB::db(); $gameStor = KVStorage::getStorage($db, 'game_env'); $show_img_level = $gameStor->show_img_level; $nation = getNationStaticInfo($generalObj->getNationID()); $lbonus = calcLeadershipBonus($generalObj->getVar('level'), $nation['level']); if($lbonus > 0) { $lbonus = "+{$lbonus}"; } else { $lbonus = ""; } if($generalObj->getVar('troop') == 0){ $troopInfo = '-'; } else{ $troopCity = $db->queryFirstField('SELECT city FROM general WHERE no=%i', $generalObj->getVar('troop')); $troopTurn = $db->queryFirstField('SELECT `brief` FROM general_turn WHERE general_id = %i AND turn_idx = 0', $generalObj->getVar('troop')); $troopInfo = $db->queryFirstField('SELECT name FROM troop WHERE troop_leader = %i', $generalObj->getVar('troop')); if($troopTurn == '집합'){ $troopInfo = "{$troopInfo}"; } else if($troopCity != $generalObj->getCityID()){ $troopCityName = CityConst::byID($troopCity)->name; $troopInfo = "{$troopInfo}({$troopCityName})"; } } $generalLevel = $generalObj->getVar('level'); $levelText = getLevelText($generalLevel, $nation['level']); if(2 <= $generalLevel && $generalLevel <= 4){ $cityOfficerKey = 'officer'.$generalLevel; $cityOfficerName = $db->queryFirstField('SELECT name FROM city where %b = %i',$cityOfficerKey, $generalObj->getID()); $levelText = "{$cityOfficerName} {$levelText}"; } $call = getCall(...$generalObj->getVars('leadership', 'strength', 'intel')); $crewTypeInfo = displayiActionObjInfo($generalObj->getCrewTypeObj()); $weaponInfo = displayiActionObjInfo($generalObj->getItem('weapon')); $bookInfo = displayiActionObjInfo($generalObj->getItem('book')); $horseInfo = displayiActionObjInfo($generalObj->getItem('horse')); $itemInfo = displayiActionObjInfo($generalObj->getItem('item')); $leadership = $generalObj->getLeadership(true, false, false); $strength = $generalObj->getStrength(true, false, false); $intel = $generalObj->getIntel(true, false, false); $injury = $generalObj->getVar('injury'); if($injury > 60) { $color = ""; $injury = "위독"; } elseif($injury > 40) { $color = ""; $injury = "심각"; } elseif($injury > 20) { $color = ""; $injury = "중상"; } elseif($injury > 0) { $color = ""; $injury = "경상"; } else { $color = ""; $injury = "건강"; } $remaining = (new \DateTimeImmutable($generalObj->getTurnTime()))->diff(new \DateTimeImmutable())->i; if($nation['color'] == "") { $nation['color'] = "#000000"; } $age = $generalObj->getVar('age'); if($age < GameConst::$retirementYear*0.75) {$age = "{$age} 세"; } elseif($age < GameConst::$retirementYear) { $age = "{$age} 세"; } else { $age = "{$age} 세"; } $connectCnt = round($generalObj->getVar('connect'), -1); $specialDomestic = $generalObj->getVar('special')===GameConst::$defaultSpecialDomestic ?"{$generalObj->getVar('specage')}세" : "".displayiActionObjInfo($generalObj->getSpecialDomestic()).""; $specialWar = $generalObj->getVar('special2')===GameConst::$defaultSpecialDomestic ?"{$generalObj->getVar('specage2')}세" : "".displayiActionObjInfo($generalObj->getSpecialWar()).""; $atmos = $generalObj->getVar('atmos'); $atmosBonus = $generalObj->onCalcStat($generalObj, 'bonusAtmos', $atmos) - $atmos; if($atmosBonus > 0){ $atmos = "{$atmos} (+{$atmosBonus})"; } else if($atmosBonus < 0){ $atmos = "{$atmos} ({$atmosBonus})"; } else{ $atmos = "$atmos"; } $train = $generalObj->getVar('train'); $trainBonus = $generalObj->onCalcStat($generalObj, 'bonusTrain', $train) - $train; if($trainBonus > 0){ $train = "{$train} (+{$trainBonus})"; } else if($trainBonus < 0){ $train = "{$train} ({$trainBonus})"; } else{ $train = "$train"; } if($generalObj->getVar('defence_train') === 999){ $defenceTrain = "수비 안함"; } else{ $defenceTrain = "수비 함(훈사{$generalObj->getVar('defence_train')})"; } $crewType = $generalObj->getCrewTypeObj(); $weapImage = ServConfig::$gameImagePath."/weap{$crewType->id}.png"; if($show_img_level < 2) { $weapImage = ServConfig::$sharedIconPath."/default.jpg"; }; $imagePath = GetImageURL(...$generalObj->getVars('imgsvr', 'picture')); echo "
  {$generalObj->getName()} 【 {$levelText} | {$call} | {$color}{$injury} 】 ".$generalObj->getTurnTime($generalObj::TURNTIME_HMS)."
통솔  {$color}{$leadership}{$lbonus}  ".bar(expStatus($generalObj->getVar('leadership_exp')), 20)." 무력  {$color}{$strength}  ".bar(expStatus($generalObj->getVar('strength_exp')), 20)." 지력  {$color}{$intel}  ".bar(expStatus($generalObj->getVar('intel_exp')), 20)."
명마 $horseInfo 무기 $weaponInfo 서적 $bookInfo
자금 {$generalObj->getVar('gold')} 군량 {$generalObj->getVar('rice')} 도구 $itemInfo
병종 {$crewTypeInfo} 병사 {$generalObj->getVar('crew')} 성격 ".displayiActionObjInfo($generalObj->getPersonality())."
훈련 $train 사기 $atmos 특기 $specialDomestic / $specialWar
Lv  {$generalObj->getVar('explevel')}  ".bar(getLevelPer(...$generalObj->getVars('experience', 'explevel')), 20)." 연령 {$age}
수비 {$defenceTrain} 삭턴 {$generalObj->getVar('killturn')} 턴 실행 $remaining 분 남음
부대 {$troopInfo} 벌점 ".getConnect($connectCnt)." {$connectCnt}({$generalObj->getVar('con')})
"; } function generalInfo2(General $generalObj) { $general = $generalObj->getRaw(); $winRate = round($generalObj->getRankVar('killnum')/max($generalObj->getRankVar('warnum'),1), 2); $killRate = round($generalObj->getRankVar('killcrew')/max($generalObj->getRankVar('deathcrew'),1), 2); $experienceBonus = $generalObj->onCalcStat($generalObj, 'experience', 10000) - 10000; if($experienceBonus > 0){ $experience = "".getHonor($general['experience'])." ({$general['experience']})"; } else if($experienceBonus < 0){ $experience = "".getHonor($general['experience'])." ({$general['experience']})"; } else{ $experience = getHonor($general['experience'])." ({$general['experience']})"; } $dedicationBonus = $generalObj->onCalcStat($generalObj, 'dedication', 10000) - 10000; if($dedicationBonus > 0){ $dedication = "".getHonor($general['dedication'])." ({$general['dedication']})"; } else if($dedicationBonus < 0){ $dedication = "".getHonor($general['dedication'])." ({$general['dedication']})"; } else{ $dedication = getHonor($general['dedication'])." ({$general['dedication']})"; } $dex0 = $general['dex0'] / GameConst::$dexLimit * 100; $dex10 = $general['dex10'] / GameConst::$dexLimit * 100; $dex20 = $general['dex20'] / GameConst::$dexLimit * 100; $dex30 = $general['dex30'] / GameConst::$dexLimit * 100; $dex40 = $general['dex40'] / GameConst::$dexLimit * 100; if($dex0 > 100) { $dex0 = 100; } if($dex10 > 100) { $dex10 = 100; } if($dex20 > 100) { $dex20 = 100; } if($dex30 > 100) { $dex30 = 100; } if($dex40 > 100) { $dex40 = 100; } $general['dex0_text'] = getDexCall($general['dex0']); $general['dex10_text'] = getDexCall($general['dex10']); $general['dex20_text'] = getDexCall($general['dex20']); $general['dex30_text'] = getDexCall($general['dex30']); $general['dex40_text'] = getDexCall($general['dex40']); $general['dex0_short'] = sprintf('%.1fK', $general['dex0']/1000); $general['dex10_short'] = sprintf('%.1fK', $general['dex10']/1000); $general['dex20_short'] = sprintf('%.1fK', $general['dex20']/1000); $general['dex30_short'] = sprintf('%.1fK', $general['dex30']/1000); $general['dex40_short'] = sprintf('%.1fK', $general['dex40']/1000); echo "
추 가 정 보
명성 $experience 계급 $dedication
전투 {$generalObj->getRankVar('warnum')} 계략 {$generalObj->getRankVar('firenum')} 사관 {$general['belong']}년
승률 {$winRate} % 승리 {$generalObj->getRankVar('killnum')} 패배 {$generalObj->getRankVar('deathnum')}
살상률 {$killRate} % 사살 {$generalObj->getRankVar('killcrew')} 피살 {$generalObj->getRankVar('deathcrew')}
숙 련 도
보병  {$general['dex0_text']} {$general['dex0_short']}  ".bar($dex0, 16)."
궁병  {$general['dex10_text']} {$general['dex10_short']}  ".bar($dex10, 16)."
기병  {$general['dex20_text']} {$general['dex20_short']}  ".bar($dex20, 16)."
귀병  {$general['dex30_text']} {$general['dex30_short']}  ".bar($dex30, 16)."
차병  {$general['dex40_text']} {$general['dex40_short']}  ".bar($dex40, 16)."
"; } function getOnlineNum() { return KVStorage::getStorage(DB::db(), 'game_env')->online; } function onlinegen() { $db = DB::db(); $gameStor = KVStorage::getStorage($db, 'game_env'); $onlinegen = ""; $generalID = Session::getInstance()->generalID; $nationID = DB::db()->queryFirstField('select `nation` from `general` where `no` = %i', $generalID); if($nationID === null || Util::toInt($nationID) === 0) { $onlinegen = $gameStor->onlinegen; } else { $onlinegen = DB::db()->queryFirstField('select onlinegen from nation where nation=%i',$nationID); } return $onlinegen; } function nationMsg(General $general) { $db = DB::db(); $msg = $db->queryFirstField( 'SELECT msg FROM nation WHERE nation = %i', $general->getNationID() ); return $msg?:''; } function banner() { return sprintf( '%s %s / %s', GameConst::$title, VersionGit::$version, GameConst::$banner); } function addTurn($date, int $turnterm, int $turn=1, bool $withFraction=true) { $date = new \DateTime($date); $target = $turnterm*$turn; $date->add(new \DateInterval("PT{$target}M")); if($withFraction){ return $date->format('Y-m-d H:i:s.u'); } return $date->format('Y-m-d H:i:s'); } function subTurn($date, int $turnterm, int $turn=1, bool $withFraction=true) { $date = new \DateTime($date); $target = $turnterm*$turn; $date->sub(new \DateInterval("PT{$target}M")); if($withFraction){ return $date->format('Y-m-d H:i:s.u'); } return $date->format('Y-m-d H:i:s'); } function cutTurn($date, int $turnterm, bool $withFraction=true) { $date = new \DateTime($date); $baseDate = new \DateTime($date->format('Y-m-d')); $baseDate->sub(new \DateInterval("P1D")); $baseDate->add(new \DateInterval("PT1H")); $diffMin = intdiv($date->getTimeStamp() - $baseDate->getTimeStamp(), 60); $diffMin -= $diffMin % $turnterm; $baseDate->add(new \DateInterval("PT{$diffMin}M")); if($withFraction){ return $baseDate->format('Y-m-d H:i:s.u'); } return $baseDate->format('Y-m-d H:i:s'); } function cutDay($date, int $turnterm, bool $withFraction=true) { $date = new \DateTime($date); $baseDate = new \DateTime($date->format('Y-m-d')); $baseDate->sub(new \DateInterval("P1D")); $baseDate->add(new \DateInterval("PT1H")); $baseGap = 12 * $turnterm; $diffMin = intdiv($date->getTimeStamp() - $baseDate->getTimeStamp(), 60); $timeAdjust = $diffMin % $baseGap; $newMonth = intdiv($timeAdjust, $turnterm) + 1; $yearPulled = false; if($newMonth > 3){//3월 이후일때는 $yearPulled = true; $diffMin += $baseGap; } $diffMin -= $timeAdjust; $baseDate->add(new \DateInterval("PT{$diffMin}M")); if($withFraction){ $dateTimeString = $baseDate->format('Y-m-d H:i:s.u'); } else{ $dateTimeString = $baseDate->format('Y-m-d H:i:s'); } return [$dateTimeString, $yearPulled, $newMonth]; } function increaseRefresh($type="", $cnt=1) { //FIXME: 로그인, 비로그인 시 처리가 명확하지 않음 $session = Session::getInstance(); $userID = $session->userID; $generalID = $session->generalID; $userGrade = $session->userGrade; $date = TimeUtil::now(); $db = DB::db(); $gameStor = KVStorage::getStorage($db, 'game_env'); $gameStor->refresh = $gameStor->refresh+$cnt; //TODO: +로 증가하는 값은 별도로 분리 $isunited = $gameStor->isunited; if($isunited != 2 && $generalID && $userGrade < 6) { $db->update('general', [ 'lastrefresh'=>$date, 'con'=>$db->sqleval('con + %i', $cnt), 'connect'=>$db->sqleval('connect + %i', $cnt), 'refcnt'=>$db->sqleval('refcnt + %i', $cnt), 'refresh'=>$db->sqleval('refresh + %i', $cnt) ], 'owner=%i', $userID); } $date = date('Y_m_d H:i:s'); $date2 = substr($date, 0, 10); $online = getOnlineNum(); file_put_contents( __DIR__."/logs/".UniqueConst::$serverID."/_{$date2}_refresh.txt", sprintf( "%s, %s, %s, %s, %s, %d\n", $date, $session->userName, $session->generalName, $session->ip, $type, $online ), FILE_APPEND ); $proxy_headers = array( 'HTTP_VIA', 'HTTP_X_FORWARDED_FOR', 'HTTP_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_FORWARDED', 'HTTP_CLIENT_IP', 'HTTP_FORWARDED_FOR_IP', 'VIA', 'X_FORWARDED_FOR', 'FORWARDED_FOR', 'X_FORWARDED', 'FORWARDED', 'CLIENT_IP', 'FORWARDED_FOR_IP', 'HTTP_PROXY_CONNECTION' ); $str = ""; foreach($proxy_headers as $x) { if(isset($_SERVER[$x])) $str .= "//{$x}:{$_SERVER[$x]}"; } if($str != "") { file_put_contents( __DIR__."/logs/".UniqueConst::$serverID."/_{$date2}_ipcheck.txt", sprintf( "%s, %s, %s%s\n", $session->userName, $session->generalName, $_SERVER['REMOTE_ADDR'], $str ), FILE_APPEND); } } function updateTraffic() { $online = getOnlineNum(); $db = DB::db(); $gameStor = KVStorage::getStorage($db, 'game_env'); $admin = $gameStor->getValues(['year','month','refresh','maxonline','maxrefresh']); //최다갱신자 $user = $db->queryFirstRow('select name,refresh from general order by refresh desc limit 1'); if($admin['maxrefresh'] < $admin['refresh']) { $admin['maxrefresh'] = $admin['refresh']; } if($admin['maxonline'] < $online) { $admin['maxonline'] = $online; } $gameStor->refresh = 0; $gameStor->maxrefresh = $admin['maxrefresh']; $gameStor->maxonline = $admin['maxonline']; $db->update('general', ['refresh'=>0], true); $date = TimeUtil::now(); //일시|년|월|총갱신|접속자|최다갱신자 file_put_contents(__DIR__."/logs/".UniqueConst::$serverID."/_traffic.txt", Json::encode([ $date, $admin['year'], $admin['month'], $admin['refresh'], $online, $user['name']."(".$user['refresh'].")" ])."\n" , FILE_APPEND); } function CheckOverhead() { //서버정보 $db = DB::db(); $gameStor = KVStorage::getStorage($db, 'game_env'); list($turnterm, $conlimit) = $gameStor->getValuesAsArray(['turnterm', 'conlimit']); $con = Util::round(pow($turnterm, 0.6) * 3) * 10; if($con != $conlimit){ $gameStor->conlimit = $con; } } function isLock() { return DB::db()->queryFirstField("SELECT plock from plock limit 1") != 0; } function tryLock():bool{ //NOTE: 게임 로직과 관련한 모든 insert, update 함수들은 lock을 거칠것을 권장함. $db = DB::db(); // 잠금 $db->update('plock', [ 'plock'=>1, 'locktime'=>TimeUtil::now(true) ], 'plock=0'); return $db->affectedRows() > 0; } function unlock():bool{ // 풀림 $db = DB::db(); $db->update('plock', [ 'plock'=>0 ], true); return $db->affectedRows() > 0; } function timeover():bool { $db = DB::db(); $gameStor = KVStorage::getStorage($db, 'game_env'); list($turnterm, $turntime) = $gameStor->getValuesAsArray(['turnterm', 'turntime']); $diff = (new \DateTime())->getTimestamp() - (new \DateTime($turntime))->getTimestamp(); $t = min($turnterm, 5); $term = $diff; if($term >= $t || $term < 0) { return true; } else { return false; } } function checkDelay() { $db = DB::db(); $gameStor = KVStorage::getStorage($db, 'game_env'); //서버정보 $now = new \DateTimeImmutable(); $turntime = new \DateTimeImmutable($gameStor->turntime); $timeMinDiff = intdiv($now->getTimestamp() - $turntime->getTimestamp(), 60); // 1턴이상 갱신 없었으면 서버 지연 $term = $gameStor->turnterm; if($term >= 20){ $threshold = 1; } else if($term >= 10){ $threshold = 3; } else{ $threshold = 6; } //지연 해야할 밀린 턴 횟수 $iter = intdiv($timeMinDiff, $term); if($iter > $threshold) { $minute = $iter * $term; $newTurntime = $turntime->add(new \DateInterval("PT{$minute}M")); $newNextTurntime = $turntime->add(new \DateInterval("PT{$term}M")); $gameStor->turntime = $newTurntime->format('Y-m-d H:i:s'); $gameStor->starttime = (new \DateTimeImmutable($gameStor->starttime)) ->add(new \DateInterval("PT{$minute}M")) ->format('Y-m-d H:i:s'); $db->update('general', [ 'turntime'=> $db->sqleval('DATE_ADD(turntime, INTERVAL %i MINUTE)', $minute) ], 'turntime<=DATE_ADD(turntime, INTERVAL %i MINUTE)', $term); $db->update('auction', [ 'expire'=> $db->sqleval('DATE_ADD(expire, INTERVAL %i MINUTE)', $minute) ], true); } } function updateOnline() { $db = DB::db(); $gameStor = KVStorage::getStorage($db, 'game_env'); $connect=$db->get(); $nationname = ["재야"]; //국가별 이름 매핑 foreach(getAllNationStaticInfo() as $nation) { $nationname[$nation['nation']] = $nation['name']; } //동접수 $query = "select no,name,nation from general where lastrefresh > DATE_SUB(NOW(), INTERVAL 5 MINUTE)"; $result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); $onlinenum = MYDB_num_rows($result); $onnation = array(); $onnationstr = ""; //국가별 접속중인 장수 for($i=0; $i < $onlinenum; $i++) { $general = MYDB_fetch_array($result); if(isset($onnation[$general['nation']])){ $onnation[$general['nation']] .= $general['name'].', '; }else { $onnation[$general['nation']] = $general['name'].', '; } } //$onnation이 empty라면 굳이 foreach를 수행 할 이유가 없음. if(!empty($onnation)){ foreach($onnation as $key => $val) { $onnationstr .= "【{$nationname[$key]}】, "; if($key == 0) { $gameStor->onlinegen = $onnation[0]; } else { $query = "update nation set onlinegen='$onnation[$key]' where nation='$key'"; MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); } } } //접속중인 국가 $gameStor->online = $onlinenum; $gameStor->onlinenation = $onnationstr; } function addAge() { $db = DB::db(); $gameStor = KVStorage::getStorage($db, 'game_env'); $connect=$db->get(); //나이와 호봉 증가 $query = "update general set age=age+1,belong=belong+1"; MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); $admin = $gameStor->getValues(['startyear', 'year', 'month']); if($admin['year'] >= $admin['startyear']+3) { foreach($db->query('SELECT no,name,nation,leadership,strength,intel from general where specage<=age and special=%s', GameConst::$defaultSpecialDomestic) as $general){ $special = SpecialityConst::pickSpecialDomestic($general); $specialClass = buildGeneralSpecialDomesticClass($special); $specialText = $specialClass->getName(); $db->update('general', [ 'special'=>$special ], 'no=%i',$general['no']); $josaUl = JosaUtil::pick($specialText, '을'); pushGeneralHistory($general, "●{$admin['year']}년 {$admin['month']}월:특기 【{$specialText}】{$josaUl} 습득"); pushGenLog($general, "●특기 【{$specialText}】{$josaUl} 익혔습니다!"); } foreach($db->query('SELECT no,name,nation,leadership,strength,intel,npc,dex0,dex10,dex20,dex30,dex40 from general where specage2<=age and special2=%s', GameConst::$defaultSpecialWar) as $general){ $special2 = SpecialityConst::pickSpecialWar($general); $specialClass = buildGeneralSpecialWarClass($special2); $specialText = $specialClass->getName(); $db->update('general', [ 'special2'=>$special2 ], 'no=%i',$general['no']); $josaUl = JosaUtil::pick($specialText, '을'); pushGeneralHistory($general, "●{$admin['year']}년 {$admin['month']}월:특기 【{$specialText}】{$josaUl} 습득"); pushGenLog($general, "●특기 【{$specialText}】{$josaUl} 익혔습니다!"); } } } function turnDate($curtime) { $db = DB::db(); $gameStor = KVStorage::getStorage($db, 'game_env'); $admin = $gameStor->getValues(['startyear', 'starttime', 'turnterm', 'year', 'month']); $turn = $admin['starttime']; $curturn = cutTurn($curtime, $admin['turnterm']); $term = $admin['turnterm']; $num = intdiv((strtotime($curturn) - strtotime($turn)), $term*60); $date = $admin['startyear'] * 12; $date += $num; $year = intdiv($date, 12); $month = 1 + $date % 12; // 바뀐 경우만 업데이트 if($admin['month'] != $month || $admin['year'] != $year) { $gameStor->year = $year; $gameStor->month = $month; } return [$year, $month]; } function triggerTournament() { $db = DB::db(); $gameStor = KVStorage::getStorage($db, 'game_env'); $admin = $gameStor->getValues(['tournament', 'tnmt_trig']); //현재 토너먼트 없고, 자동개시 걸려있을때, 40%확률 if($admin['tournament'] == 0 && $admin['tnmt_trig'] > 0 && rand() % 100 < 40) { $type = rand() % 5; // 0 : 전력전, 1 : 통솔전, 2 : 일기토, 3 : 설전 //전력전 40%, 통, 일, 설 각 20% if($type > 3) { $type = 0; } startTournament($admin['tnmt_trig'], $type); } } function CheckHall($no) { $db = DB::db(); $gameStor = KVStorage::getStorage($db, 'game_env'); $types = [ ["experience", 'natural'], ["dedication", 'natural'], ["firenum", 'rank'], ["warnum", 'rank'], ["killnum", 'rank'], ["winrate", 'rank'], ["killcrew", 'rank'], ["killrate", 'calc'], ["dex0", 'natural'], ["dex10", 'natural'], ["dex20", 'natural'], ["dex30", 'natural'], ["dex40", 'natural'], ["ttrate", 'calc'], ["tlrate", 'calc'], ["tprate", 'calc'], ["tirate", 'calc'], ["betgold", 'rank'], ["betwin", 'rank'], ["betwingold", 'rank'], ["betrate", 'calc'], ];//XXX: 순서가 DB에 박혀있다 ㅜㅜ $generalObj = General::createGeneralObjFromDB($no, null, 2); $ttw = $generalObj->getRankVar('ttw'); $ttd = $generalObj->getRankVar('ttd'); $ttl = $generalObj->getRankVar('ttl'); $tlw = $generalObj->getRankVar('tlw'); $tld = $generalObj->getRankVar('tld'); $tll = $generalObj->getRankVar('tll'); $tsw = $generalObj->getRankVar('tsw'); $tsd = $generalObj->getRankVar('tsd'); $tsl = $generalObj->getRankVar('tsl'); $tiw = $generalObj->getRankVar('tiw'); $tid = $generalObj->getRankVar('tid'); $til = $generalObj->getRankVar('til'); $tt = max($ttw+$ttd+$ttl, 1); $tl = max($tlw+$tld+$tll, 1); $ts = max($tsw+$tsd+$tsl, 1); $ti = max($tiw+$tid+$til, 1); $calcVar = []; $calcVar['ttrate'] = $ttw/$tt; $calcVar['tlrate'] = $tlw/$tl; $calcVar['tsrate'] = $tsw/$ts; $calcVar['tirate'] = $tiw/$ti; if($generalObj instanceof DummyGeneral){ return; } $unitedDate = TimeUtil::now(); $nation = $generalObj->getStaticNation(); $serverCnt = $db->queryFirstField('SELECT count(*) FROM ng_games'); [$scenarioIdx, $scenarioName, $startTime] = $gameStor->getValuesAsArray(['scenario', 'scenario_text', 'starttime']); $ownerName = $generalObj->getVar('name2'); if($generalObj->getVar('owner')){ $ownerName = RootDB::db()->queryFirstField('SELECT name FROM member WHERE no = %i', $generalObj->getVar('owner')); } foreach($types as $idx=>[$typeName, $valueType]) { if($valueType === 'natural'){ $value = $generalObj->getVar($typeName); } else if($valueType === 'rank'){ $value = $generalObj->getRankVar($typeName); } else if($valueType === 'calc'){ $value = $calcVar[$typeName]; } //승률,살상률인데 10회 미만 전투시 스킵 if(($typeName === 'winrate' || $typeName === 'killrate') && $generalObj->getRankVar('warnum')<10) { continue; } //토너승률인데 50회 미만시 스킵 if($typeName === 'ttrate' && $tt < 50) { continue; } //토너승률인데 50회 미만시 스킵 if($typeName === 'tlrate' && $tl < 50) { continue; } //토너승률인데 50회 미만시 스킵 if($typeName === 'tsrate' && $ts < 50) { continue; } //토너승률인데 50회 미만시 스킵 if($typeName === 'tirate' && $ti < 50) { continue; } //수익률인데 1000미만시 스킵 if($typeName === 'betrate' && $generalObj->getRankVar('betgold') < 1000) { continue; } if($value<=0){ continue; } $aux = [ 'name'=>$generalObj->getName(), 'nationName'=>$nation['name'], 'bgColor'=>$nation['color'], 'fgColor'=>newColor($nation['color']), 'picture'=>$generalObj->getVar('picture'), 'imgsvr'=>$generalObj->getVar('imgsvr'), 'startTime'=>$startTime, 'unitedTime'=>$unitedDate, 'owner_name'=>$ownerName, 'serverID'=>UniqueConst::$serverID, 'serverIdx'=>$serverCnt, 'serverName'=>UniqueConst::$serverName, 'scenarioName'=>$scenarioName, ]; $jsonAux = Json::encode($aux); $db->insertIgnore('ng_hall', [ 'server_id'=>UniqueConst::$serverID, 'season'=>UniqueConst::$seasonIdx, 'scenario'=>$scenarioIdx, 'general_no'=>$no, 'type'=>$idx, 'value'=>$value, 'owner'=>$generalObj->getVar('owner'), 'aux'=>$jsonAux ]); if($db->affectedRows() == 0){ $db->update('ng_hall', [ 'value'=>$value, 'aux'=>$jsonAux ], 'server_id = %s AND scenario = %i AND general_no = %i AND type = %i AND value < %d', UniqueConst::$serverID, $scenarioIdx, $no, $idx, $value); } } } function tryUniqueItemLottery(General $general, string $acquireType='아이템'):bool{ $db = DB::db(); $gameStor = KVStorage::getStorage($db, 'game_env'); if($general->getVar('npc') >= 2){ return false; } if($general->getVar('npc') > 6){ return false; } foreach($general->getItems() as $item){ if(!$item){ continue; } if(!$item->isBuyable()){ return false; } } $scenario = $gameStor->scenario; $genCount = $db->queryFirstField('SELECT count(*) FROM general WHERE npc<2'); if ($scenario < 100) { $prob = 1 / ($genCount * 5); // 5~6개월에 하나씩 등장 } else { $prob = 1 / $genCount; // 1~2개월에 하나씩 등장 } if($acquireType == '투표'){ $prob = 1 / ($genCount * 0.7 / 3); // 투표율 70%, 투표 한번에 2~3개 등장 } else if($acquireType == '랜덤 임관'){ $prob = 1 / ($genCount / 10/ 2); // 랜임시 2개(10%) 등장(200명중 20명 랜임시도?) } else if($acquireType == '건국'){ $prob = 1 / ($genCount / 10/ 4); // 건국시 4개(20%) 등장(200명시 20국 정도 됨) } $prob = Util::valueFit($prob, 1/3, 1); if(!Util::randBool($prob)){ return false; } //아이템 습득 상황 $availableUnique = []; //TODO: 너무 바보 같다. 장기적으로는 유니크 아이템 테이블 같은게 필요하지 않을까? //일단은 '획득' 시에만 동작하므로 이대로 사용하기로... $occupiedUnique = []; foreach (array_keys(GameConst::$allItems) as $itemType) { foreach($db->queryAllLists('SELECT %b, count(*) as cnt FROM general GROUP BY %b', $itemType, $itemType) as [$itemCode, $cnt]){ $itemClass = buildItemClass($itemCode); if(!$itemClass){ continue; } if($itemClass->isBuyable()){ continue; } $occupiedUnique[$itemCode] = $cnt; } } foreach(GameConst::$allItems as $itemType=>$itemCategories){ foreach($itemCategories as $itemCode => $cnt){ if(!key_exists($itemCode, $occupiedUnique)){ $availableUnique[] = [[$itemType, $itemCode], $cnt]; continue; } $remain = $cnt - $occupiedUnique[$itemCode]; if($remain > 0){ $availableUnique[] = [[$itemType, $itemCode], $cnt]; } } } if(!$availableUnique){ return false; } [$itemType, $itemCode] = Util::choiceRandomUsingWeightPair($availableUnique); $nationName = $general->getStaticNation()['name']; $generalName = $general->getName(); $josaYi = JosaUtil::pick($generalName, '이'); $itemObj = buildItemClass($itemCode); $itemName = $itemObj->getName(); $itemRawName = $itemObj->getRawName(); $josaUl = JosaUtil::pick($itemRawName, '을'); $general->setVar($itemType, $itemCode); $logger = $general->getLogger(); $logger->pushGeneralActionLog("{$itemName}{$josaUl} 습득했습니다!"); $logger->pushGlobalActionLog("{$generalName}{$josaYi} {$itemName}{$josaUl} 습득했습니다!"); $logger->pushGlobalHistoryLog("【{$acquireType}】{$nationName}{$generalName}{$josaYi} {$itemName}{$josaUl} 습득했습니다!"); return true; } function checkDedication($general, $log) { $db = DB::db(); $connect=$db->get(); $dedlevel = getDedLevel($general['dedication']); $query = "update general set dedlevel='$dedlevel' where no='{$general['no']}'"; MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); // 승급했다면 $josaRoDed = JosaUtil::pick(getDed($general['dedication']), '로'); $josaRoBill = JosaUtil::pick(getBill($general['dedication']), '로'); if($general['dedlevel'] < $dedlevel) { $log[] = "".getDed($general['dedication'])."{$josaRoDed} 승급하여 봉록이 ".getBill($general['dedication'])."{$josaRoBill} 상승했습니다!"; // 강등했다면 } elseif($general['dedlevel'] > $dedlevel) { $log[] = "".getDed($general['dedication'])."{$josaRoDed} 강등되어 봉록이 ".getBill($general['dedication'])."{$josaRoBill} 하락했습니다!"; } return $log; } function checkExperience($general, $log) { $db = DB::db(); $connect=$db->get(); $explevel = getExpLevel($general['experience']); $query = "update general set explevel='$explevel' where no='{$general['no']}'"; MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); // 승급했다면 if($general['explevel'] < $explevel) { $log[] = "Lv $explevel로 레벨업!"; // 강등했다면 } elseif($general['explevel'] > $explevel) { $log[] = "Lv $explevel로 레벨다운!"; } return $log; } function getAdmin() { $db = DB::db(); $gameStor = KVStorage::getStorage($db, 'game_env'); return $gameStor->getAll(); } function getCity($city, $sel="*") { $db = DB::db(); $connect=$db->get(); $query = "select {$sel} from city where city='$city'"; $result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); $city = MYDB_fetch_array($result); return $city; } function deleteNation(General $general) { $db = DB::db(); $gameStor = KVStorage::getStorage($db, 'game_env'); [$year, $month] = $gameStor->getValuesAsArray(['year', 'month']); $nation = $general->getStaticNation(); $nationName = $nation['name']; $nationID = $nation['nation']; $logger = $general->getLogger(); $josaUn = JosaUtil::pick($nationName, '은'); $logger->pushGlobalHistoryLog("【멸망】{$nationName}{$josaUn} 멸망했습니다."); $oldNation = $db->queryFirstRow('SELECT * FROM nation WHERE nation=%i', $nationID); $oldNationGenerals = $db->queryFirstColumn('SELECT `no` FROM general WHERE nation=%i', $nationID); $oldNation['generals'] = $oldNationGenerals; $oldNation['aux'] = Json::decode($oldNation['aux']); $general->setVar('belong', 0); $general->setVar('troop', 0); $general->setVar('level', 0); $general->setVar('nation', 0); $general->setVar('makelimit', 12); // 전 장수 재야로 // 전 장수 소속 무소속으로 $db->update('general', [ 'belong'=>0, 'troop'=>0, 'level'=>0, 'nation'=>0, 'makelimit'=>12, 'permission'=>'normal', ], 'nation=%i', $nationID); // 도시 공백지로 $db->update('city', [ 'nation'=>0, 'front'=>0, 'officer4'=>0, 'officer3'=>0, 'officer2'=>0, ], 'nation=%i', $nationID); // 부대 삭제 $db->delete('troop', 'nation=%i', $nationID); // 국가 삭제 $db->insert('ng_old_nations', [ 'server_id'=>UniqueConst::$serverID, 'nation'=>$nationID, 'data'=>Json::encode($oldNation) ]); $db->delete('nation', 'nation=%i', $nationID); $db->delete('nation_turn', 'nation_id=%i', $nationID); // 외교 삭제 $db->delete('diplomacy', 'me = %i OR you = %i', $nationID, $nationID); refreshNationStaticInfo(); } function nextRuler(General $general) { $db = DB::db(); $gameStor = KVStorage::getStorage($db, 'game_env'); [$year, $month] = $gameStor->getValuesAsArray(['year', 'month']); $nation = $general->getStaticNation(); $nationName = $nation['name']; $nationID = $nation['nation']; $candidate = null; //npc or npc유저인 경우 후계 찾기 if($general->getVar('npc') > 0) { $candidate = $db->queryFirstRow( 'SELECT no,name,nation,level,IF(ABS(affinity-%i)>75,150-ABS(affinity-%i),ABS(affinity-%i)) as npcmatch2 from general where nation=%i and level!=12 and npc>0 order by npcmatch2,rand() LIMIT 1', $general->getVar('affinity'), $general->getVar('affinity'), $nationID ); } if(!$candidate){ $candidate = $db->queryFirstRow( 'SELECT no,name,npc,level FROM general WHERE nation=%i and level!= 12 AND level >= 9 ORDER BY level DESC LIMIT 1', $nationID ); } if(!$candidate){ $candidate = $db->queryFirstRow( 'SELECT no,name,npc,level FROM general WHERE nation=%i and level!= 12 ORDER BY dedication DESC LIMIT 1', $nationID ); } if(!$candidate){ DeleteConflict($general['nation']); deleteNation($general); return; } $nextRulerID = $candidate['no']; $nextRulerName = $candidate['name']; $general->setVar('level', 1); $db->update('general', [ 'level'=>12 ], 'no=%i', $nextRulerID); if(2 <= $candidate['level'] && $candidate['level'] <= 4){ $db->update('city', [ 'officer'.$candidate['level']=>0 ], "officer{$candidate['level']}=%i", $nextRulerID); } $josaYi = JosaUtil::pick($nextRulerName, '이'); $logger = $general->getLogger(); $logger->pushGlobalHistoryLog("【유지】{$nextRulerName}{$josaYi} {$nationName}의 유지를 이어 받았습니다"); $logger->pushGlobalHistoryLog("【유지】{$nextRulerName}{$josaYi} {$nationName}의 유지를 이어 받음."); // 장수 삭제 및 부대처리는 checkTurn에서 } /** * $maxDist 이내의 도시를 검색하는 함수 * @param $from 기준 도시 코드 * @param $maxDist 검색하고자 하는 최대 거리 * @param $distForm 리턴 타입. true일 경우 $result[$dist] = [...$city] 이며, false일 경우 $result[$city] = $dist 임 */ function searchDistance(int $from, int $maxDist=99, bool $distForm = false) { $queue = new \SplQueue(); $cities = []; $distanceList = []; $queue->enqueue([$from, 0]); while(!$queue->isEmpty()){ list($cityID, $dist) = $queue->dequeue(); if(key_exists($cityID, $cities)){ continue; } if(!key_exists($dist, $distanceList)){ $distanceList[$dist] = []; } $distanceList[$dist][] = $cityID; $cities[$cityID] = $dist; if($dist >= $maxDist){ continue; } foreach(array_keys(CityConst::byID($cityID)->path) as $connCityID){ if(key_exists($connCityID, $cities)){ continue; } $queue->enqueue([$connCityID, $dist+1]); } } if($distForm){ unset($distanceList[0]); return $distanceList; } else{ return $cities; } } /** * $from 으로 지정한 도시의 인접 도시와 $to 도시의 최단 거리를 계산해 줌 * @param $from 기준 도시 코드 * @param $to 대상 도시 코드 * @param $linkNationList 경로에 해당하는 국가 리스트 * @return array $dist=>[cityID, $nation] 배열 가장 앞이 가장 가까움 */ function searchDistanceListToDest(int $from, int $to, array $linkNationList) { $queue = new \SplQueue(); $cities = []; $db = DB::db(); //TODO: 도시-국가 캐싱이 있으면 쓸모 있지 않을까 $allowedCityList = []; foreach($db->queryAllLists( 'SELECT city, nation FROM city WHERE nation IN %li', $linkNationList ) as [$cityID, $nationID]){ $allowedCityList[$cityID] = $nationID; } $remainFromCities = []; foreach(array_keys(CityConst::byID($from)->path) as $fromCityID){ if(key_exists($fromCityID, $allowedCityList)){ $remainFromCities[$fromCityID] = true; } } if(!key_exists($to, $allowedCityList)){ return []; } $result = []; $queue->enqueue([$to, 0]); while(!empty($remainFromCities) && !$queue->isEmpty()){ list($cityID, $dist) = $queue->dequeue(); if(key_exists($cityID, $cities)){ continue; } $cities[$cityID] = $dist; if(key_exists($cityID, $remainFromCities)){ unset($remainFromCities[$cityID]); $result[$dist][] = [$cityID, $allowedCityList[$cityID]]; } foreach(array_keys(CityConst::byID($cityID)->path) as $connCityID){ if($allowedCityList !== null && !key_exists($connCityID, $allowedCityList)){ continue; } if(key_exists($connCityID, $cities)){ continue; } $queue->enqueue([$connCityID, $dist+1]); } } return $result; } function isNeighbor(int $nation1, int $nation2, bool $includeNoSupply=true) { if($nation1 === $nation2){ return false; } $db = DB::db(); $nation1Cities = []; if($includeNoSupply){ $supplySql = ''; } else{ $supplySql = 'AND supply = 1'; } foreach($db->queryFirstColumn('SELECT city FROM city WHERE nation = %i %l', $nation1, $supplySql) as $city){ $nation1Cities[$city] = $city; } foreach($db->queryFirstColumn('SELECT city FROM city WHERE nation = %i %l', $nation2, $supplySql) as $city){ foreach(array_keys(CityConst::byID($city)->path) as $adjCity){ if(key_exists($adjCity, $nation1Cities)){ return true; } } } return false; } function SabotageInjury(array $cityGeneralList, string $reason):int{ $injuryCount = 0; $josaRo = JosaUtil::pick($reason, '로'); $text = "{$reason}{$josaRo} 인해 부상을 당했습니다."; $db = DB::db(); foreach($cityGeneralList as $general){ /** @var General $general */ if(!Util::randBool(0.3)){ continue; } $general->getLogger()->pushGeneralActionLog($text); $general->increaseVarWithLimit('injury', Util::randRangeInt(1, 16), 0, 80); $general->multiplyVar('crew', 0.98); $general->multiplyVar('atmos', 0.98); $general->multiplyVar('train', 0.98); $general->applyDB($db); $injuryCount += 1; } return $injuryCount; } function getRandTurn($term, ?\DateTimeInterface $baseDateTime = null) { if($baseDateTime === null){ $baseDateTime = new \DateTimeImmutable(); } else if($baseDateTime instanceof \DateTime){ $baseDateTime = DateTimeImmutable::createFromMutable($baseDateTime); } $randSecond = Util::randRangeInt(0, 60 * $term - 1); $randFraction = Util::randRangeInt(0, 999999) / 1000000;//6자리 소수 return $baseDateTime->add(TimeUtil::secondsToDateInterval($randSecond + $randFraction))->format('Y-m-d H:i:s'); } function getRandTurn2($term, ?\DateTimeInterface $baseDateTime = null) { if($baseDateTime === null){ $baseDateTime = new \DateTimeImmutable(); } else if($baseDateTime instanceof \DateTime){ $baseDateTime = DateTimeImmutable::createFromMutable($baseDateTime); } $randSecond = Util::randRangeInt(0, 60 * $term - 1); $randFraction = Util::randRangeInt(0, 999999) / 1000000;//6자리 소수 return $baseDateTime->sub(TimeUtil::secondsToDateInterval($randSecond + $randFraction))->format('Y-m-d H:i:s'); }