JA

Slave Rift

作者:tueb2024 · シューティング
5.0 (1)
▶ 遊ぶ 💬 フィードバック ソース 🕘 バージョン 編集 GitHub
games/slave-rift / src / Game / CampaignRun.php · commit 1e9ac38
<?phpnamespace App\Game;/** Four-stage continuous run; daily seeds are fixed before gameplay starts. */final class CampaignRun{    public int $stage = 1;    public Run $run;    public AssembledLevel $level;    public float $baseArea = 0;    public array $geometry = [];    public function __construct(public readonly int $seed, public readonly XpCurve $xpCurve = new XpCurve(XpCurve::ACCESSIBLE, XpCurve::CAMPAIGN_MILESTONES), public readonly float $healMissing = 0.35, public readonly ?string $dailyId = null, public readonly ?array $stageSeeds = null)    {        if($seed<1 || $seed>65520)throw new \InvalidArgumentException('Campaign seed must be 1..65520');        if($stageSeeds!==null && (count($stageSeeds)!==4 || count(array_unique($stageSeeds))!==4 || array_filter($stageSeeds,static fn($s)=>!is_int($s)||$s<1||$s>65520)!==[]))throw new \InvalidArgumentException('Four distinct valid seeds required');        $this->build(null);    }    public function state(): string    {        return $this->run->state === 'won' ? ($this->stage === 4 ? 'won' : 'level_complete') : $this->run->state;    }    public function advance(): bool    {        if ($this->state() !== 'level_complete') return false;        $saved=$this->run->progressionSnapshot();        $this->stage++;$this->build($saved);return true;    }    public static function stageSeed(int $seed, int $stage): int    {        return (($seed-1+($stage-1)*7919)%65520)+1;    }    private function build(?array $saved): void    {        $seed=$this->stageSeeds[$this->stage-1]??self::stageSeed($this->seed,$this->stage);        $mode=$this->dailyId===null?GameMode::campaignStage($seed,$this->stage):GameMode::daily(substr($this->dailyId,0,10),$this->stage,$seed,$this->dailyId);        $graph=new LevelGraph($seed,$mode->depth);        $minimum=$this->geometry['footprint']??0;        $promotions=0;        do{            $this->level=(new LevelAssembler)->assemble($graph);            $area=0.0;foreach($this->level->rooms as$room){$b=$room['bounds'];$area+=($b['max'][0]-$b['min'][0])*($b['max'][2]-$b['min'][2]);}            if($area>$minimum)break;            // A longer random route can still contain more narrow corridors. Promote            // fixed-size modules in graph order; never scale rooms, stairs or doors.            $changed=false;            foreach($graph->nodes as$id=>$node)if($node['mainPath']&&in_array($node['type'],[LevelGraph::CORRIDOR_SHORT,LevelGraph::CORRIDOR_LONG,LevelGraph::SMALL_ROOM],true)){                $graph->nodes[$id]['type']=LevelGraph::LARGE_ROOM;$changed=true;$promotions++;break;            }            if(!$changed)throw new \RuntimeException('Cannot build a strictly larger campaign stage');        }while($promotions<=48);        if($this->stage===1)$this->baseArea=$area;        $this->geometry=['depth'=>$mode->depth,'mainRooms'=>count(array_filter($this->level->rooms,static fn($r)=>$r['mainPath'])),            'footprint'=>$area,'surfaceRatio'=>$area/$this->baseArea,'horizontalScale'=>1.0,'roomPromotions'=>$promotions,'solids'=>count($this->level->map->solids)];        $this->run=new Run($this->level->map,$mode->seed,$mode,$this->xpCurve);        $this->run->setObjective($this->level,$mode->finalWaveSize);        if($saved!==null)$this->run->restoreProgression($saved,$this->healMissing);    }    public static function daily(string $date,?array $serverSeeds=null,?string $id=null): self    {        $seeds=$serverSeeds??array_map(static fn($stage)=>GameMode::daily($date,$stage)->seed,range(1,4));        return new self($seeds[0],new XpCurve(XpCurve::ACCESSIBLE,XpCurve::CAMPAIGN_MILESTONES),.35,$date.($id===null?'':'/'.$id),$seeds);    }}