*/ use HasFactory; /** * Maximum number of territory expansions allowed per city. */ public const MAX_EXPANSIONS = 10; /** * Characters living in this city. * * @return HasMany */ public function characters(): HasMany { return $this->hasMany(Character::class); } /** * Buildings placed inside this city. * * @return HasMany */ public function buildings(): HasMany { return $this->hasMany(Building::class); } /** * Job positions offered in this city. * * @return HasMany */ public function jobs(): HasMany { return $this->hasMany(Job::class); } /** * Find the active city used as the default world. */ public static function active(): ?self { return self::query()->where('is_active', true)->orderBy('id')->first(); } /** * Expand territory eastward and northward by one grid step (40 units). * * Returns false when the expansion cap has been reached. */ public function expand(): bool { if ($this->expansions >= self::MAX_EXPANSIONS) { return false; } $this->bounds_max_x = (float) $this->bounds_max_x + 40; $this->bounds_max_z = (float) $this->bounds_max_z + 40; $this->expansions = (int) $this->expansions + 1; $this->save(); return true; } /** * Determine whether a world coordinate lies inside the city bounds. */ public function contains(float $x, float $z): bool { return $x >= (float) $this->bounds_min_x && $x <= (float) $this->bounds_max_x && $z >= (float) $this->bounds_min_z && $z <= (float) $this->bounds_max_z; } /** * Get the attributes that should be cast. * * @return array */ protected function casts(): array { return [ 'bounds_min_x' => 'float', 'bounds_max_x' => 'float', 'bounds_min_z' => 'float', 'bounds_max_z' => 'float', 'is_active' => 'boolean', 'expansions' => 'integer', ]; } }