Files
core/hwe/sammo/SpecialityHelper.php

329 lines
9.4 KiB
PHP

<?php
namespace sammo;
class SpecialityHelper{
//const GENERIC = 0x0;
const DISABLED = 0x1;
const STAT_LEADERSHIP = 0x2;
const STAT_STRENGTH = 0x4;
const STAT_INTEL = 0x8;
const ARMY_FOOTMAN = 0x100;
const ARMY_ARCHER = 0x200;
const ARMY_CAVALRY = 0x400;
const ARMY_WIZARD = 0x800;
const ARMY_SIEGE = 0x1000;
const REQ_DEXTERITY = 0x4000;
const STAT_NOT_LEADERSHIP = 0x20000;
const STAT_NOT_STRENGTH = 0x40000;
const STAT_NOT_INTEL = 0x80000;
const WEIGHT_NORM = 1;
const WEIGHT_PERCENT = 2;
static $domesticInv = null;
static $warInv = null;
private function __construct(){
}
public static function getDomesticClassByName(?string $type):string{
if($type === null || $type === '' || $type == 'None'){
return GameConst::$defaultSpecialDomestic;
}
if(static::$domesticInv){
if(!key_exists($type, static::$domesticInv)){
throw new \InvalidArgumentException('올바르지 않은 특기명:'.$type);
}
return static::$domesticInv[$type];
}
$cache = [];
foreach(static::getSpecialDomesticList(false) as $className=>$classObj){
$cache[$classObj->getName()] = $className;
}
static::$domesticInv = $cache;
if(!key_exists($type, $cache)){
throw new \InvalidArgumentException('올바르지 않은 특기명:'.$type);
}
return $cache[$type];
}
public static function getWarClassByName(?string $type):string{
if($type === null || $type === '' || $type == 'None'){
return GameConst::$defaultSpecialWar;
}
if(static::$warInv){
if(!key_exists($type, static::$warInv)){
throw new \InvalidArgumentException('올바르지 않은 특기명:'.$type);
}
return static::$warInv[$type];
}
$cache = [];
foreach(static::getSpecialWarList(false) as $className=>$classObj){
$cache[$classObj->getName()] = $className;
}
if(!key_exists($type, $cache)){
throw new \InvalidArgumentException('올바르지 않은 특기명:'.$type);
}
static::$warInv = $cache;
return $cache[$type];
}
private static function calcCondGeneric(array $general) : int {
$myCond = 0;
$leadership = $general['leadership']??50;
$strength = $general['strength']??50;
$intel = $general['intel']??50;
if($leadership > GameConst::$chiefStatMin){
$myCond |= self::STAT_LEADERSHIP;
}
if($strength >= $intel * 0.95 && $strength > GameConst::$chiefStatMin){
$myCond |= self::STAT_STRENGTH;
}
if($intel >= $strength * 0.95 && $intel > GameConst::$chiefStatMin){
$myCond |= self::STAT_INTEL;
}
if($myCond !== 0){
if($leadership < GameConst::$chiefStatMin){
$myCond |= self::STAT_NOT_LEADERSHIP;
}
if($strength < GameConst::$chiefStatMin){
$myCond |= self::STAT_NOT_STRENGTH;
}
if($intel < GameConst::$chiefStatMin){
$myCond |= self::STAT_NOT_INTEL;
}
}
if($myCond === 0){
if ($leadership * 0.9 > $strength && $leadership * 0.9 > $intel) {
$myCond |= self::STAT_LEADERSHIP;
}
else if($strength >= $intel){
$myCond |= self::STAT_STRENGTH;
}
else {
$myCond |= self::STAT_INTEL;
}
}
return $myCond;
}
private static function calcCondDexterity(RandUtil $rng, array $general) : int {
$dex = [
static::ARMY_FOOTMAN => $general['dex1']??0,
static::ARMY_ARCHER => $general['dex2']??0,
static::ARMY_CAVALRY => $general['dex3']??0,
static::ARMY_WIZARD => $general['dex4']??0,
static::ARMY_SIEGE => $general['dex5']??0,
];
$dexSum = array_sum($dex);
$dexBase = Util::round(sqrt($dexSum) / 4);
if($rng->nextBool(0.8)){
return 0;
}
if($rng->nextRangeInt(0, 99) < $dexBase){
return 0;
}
if(!$dexSum){
return $rng->choice($dex);
}
return $rng->choice(array_keys($dex, max($dex)));
}
/** @return BaseSpecial[] */
public static function getSpecialDomesticList(bool $onlyAvailable=true):array{
$result = [];
if(!$onlyAvailable){
$specialObj = buildGeneralSpecialDomesticClass(GameConst::$defaultSpecialDomestic);
$result[GameConst::$defaultSpecialDomestic] = $specialObj;
}
foreach(GameConst::$availableSpecialDomestic as $specialID){
$specialObj = buildGeneralSpecialDomesticClass($specialID);
$result[$specialID] = $specialObj;
}
if(!$onlyAvailable){
foreach(GameConst::$optionalSpecialDomestic as $specialID){
$specialObj = buildGeneralSpecialDomesticClass($specialID);
$result[$specialID] = $specialObj;
}
}
return $result;
}
/** @return BaseSpecial[] */
public static function getSpecialWarList(bool $onlyAvailable=true):array{
$result = [];
if(!$onlyAvailable){
$specialObj = buildGeneralSpecialWarClass(GameConst::$defaultSpecialWar);
$result[GameConst::$defaultSpecialWar] = $specialObj;
}
foreach(GameConst::$availableSpecialWar as $specialID){
$specialObj = buildGeneralSpecialWarClass($specialID);
$result[$specialID] = $specialObj;
}
if(!$onlyAvailable){
foreach(GameConst::$optionalSpecialWar as $specialID){
$specialObj = buildGeneralSpecialWarClass($specialID);
$result[$specialID] = $specialObj;
}
}
return $result;
}
public static function pickSpecialDomestic(RandUtil $rng, array $general, array $prevSpecials=[]) : string{
$pAbs = [];
$pRel = [];
$id = null;
$myCond = static::calcCondGeneric($general);
foreach(static::getSpecialDomesticList() as $specialID=>$specialObj){
$conds = $specialObj::$type;
$name = $specialObj->getName();
$weightType = $specialObj::$selectWeightType;
$weight = $specialObj::$selectWeight;
$valid = false;
foreach($conds as $cond){
if($cond === ($cond & $myCond)){
$valid = true;
break;
}
}
if(!$valid){
continue;
}
if(in_array($specialID, $prevSpecials)){
continue;
}
if($weightType === static::WEIGHT_PERCENT){
$pAbs[$specialID] = $weight;
}
else{
$pRel[$specialID] = $weight;
}
}
if($pAbs){
if($pRel){
$pAbs[0] = max(0, 100 - array_sum($pAbs));
}
$id = $rng->choiceUsingWeight($pAbs);
if($id){
return $id;
}
}
if($pRel){
$id = $rng->choiceUsingWeight($pRel);
}
if($id){
return $id;
}
if($prevSpecials){
return static::pickSpecialDomestic($rng, $general, []);
}
throw new MustNotBeReachedException("{$general['name']}, {$myCond}");
}
public static function pickSpecialWar(RandUtil $rng, array $general, array $prevSpecials=[]) : string{
$reqDex = [];
$pAbs = [];
$pRel = [];
$id = null;
$myCond = static::calcCondGeneric($general);
$myCond |= static::calcCondDexterity($rng, $general);
$myCond |= static::REQ_DEXTERITY;
foreach(static::getSpecialWarList() as $specialID=>$specialObj){
$conds = $specialObj::$type;
$name = $specialObj->getName();
$weightType = $specialObj::$selectWeightType;
$weight = $specialObj::$selectWeight;
$valid = false;
foreach($conds as $cond){
if($cond === ($cond & $myCond)){
$valid = true;
break;
}
}
if(!$valid){
continue;
}
if(in_array($specialID, $prevSpecials)){
continue;
}
if($cond & self::REQ_DEXTERITY){
$reqDex[$specialID] = $weight;
}
else if($weightType === static::WEIGHT_PERCENT){
$pAbs[$specialID] = $weight;
}
else{
$pRel[$specialID] = $weight;
}
}
if($reqDex){
return $rng->choiceUsingWeight($reqDex);
}
if($pAbs){
if($pRel){
$pAbs[0] = max(0, 100 - array_sum($pAbs));
}
$id = $rng->choiceUsingWeight($pAbs);
if($id){
return $id;
}
}
if($pRel){
$id = $rng->choiceUsingWeight($pRel);
}
if($id){
return $id;
}
if($prevSpecials){
return static::pickSpecialWar($rng, $general, []);
}
throw new MustNotBeReachedException();
}
}