Characters can now relax on the beach sand to recover energy and health. The server clamps position to the shoreline, exposes beach geometry in the city payload, and the client renders animated waves, sand, stalls, chairs, and ambient NPCs. Includes Beach support class, new beach action endpoint, localization strings, and feature tests.
118 lines
3.8 KiB
PHP
118 lines
3.8 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\Character;
|
|
use App\Models\User;
|
|
use Database\Seeders\CityWorldSeeder;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Tests\TestCase;
|
|
|
|
/**
|
|
* East-beach leisure action and its geometry in the world payload.
|
|
*/
|
|
class BeachLeisureTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
protected User $user;
|
|
|
|
protected Character $character;
|
|
|
|
/**
|
|
* Boot world fixtures.
|
|
*/
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
$this->seed(CityWorldSeeder::class);
|
|
$this->user = User::factory()->create(['status' => User::STATUS_ACTIVE, 'is_active' => true]);
|
|
$this->character = Character::ensureForUser($this->user);
|
|
$this->actingAs($this->user);
|
|
}
|
|
|
|
/**
|
|
* The world payload carries the beach geometry derived from the grid.
|
|
*/
|
|
public function test_city_data_includes_beach_geometry(): void
|
|
{
|
|
$payload = $this->getJson('/city/data')->assertOk()->json();
|
|
|
|
$this->assertArrayHasKey('beach', $payload);
|
|
// Layout max X (200 for these bounds) + approach → sand starts at 204.
|
|
$this->assertEquals(204.0, $payload['beach']['sand_from']);
|
|
$this->assertEquals(264.0, $payload['beach']['sand_to']);
|
|
$this->assertEquals(354.0, $payload['beach']['sea_to']);
|
|
$this->assertEquals(-160.0, $payload['beach']['z_min']);
|
|
$this->assertEquals(160.0, $payload['beach']['z_max']);
|
|
}
|
|
|
|
/**
|
|
* Relaxing on the sand recovers one point of energy and health.
|
|
*/
|
|
public function test_relax_on_sand_recovers_energy_and_health(): void
|
|
{
|
|
$this->character->forceFill(['energy' => 40, 'health' => 50])->save();
|
|
|
|
$this->postJson('/city/actions/beach', ['pos_x' => 210, 'pos_z' => -40])
|
|
->assertOk()
|
|
->assertJsonPath('message', __('city.beach_ok'));
|
|
|
|
$this->character->refresh();
|
|
$this->assertSame(41, (int) $this->character->energy);
|
|
$this->assertSame(51, (int) $this->character->health);
|
|
}
|
|
|
|
/**
|
|
* The action is rejected away from the beach.
|
|
*/
|
|
public function test_relax_requires_being_on_the_beach(): void
|
|
{
|
|
$this->postJson('/city/actions/beach', ['pos_x' => 0, 'pos_z' => 0])
|
|
->assertStatus(422)
|
|
->assertJsonPath('message', __('city.beach_need'));
|
|
|
|
$this->postJson('/city/actions/beach', ['pos_x' => 300, 'pos_z' => 0])
|
|
->assertStatus(422)
|
|
->assertJsonPath('message', __('city.beach_need'));
|
|
}
|
|
|
|
/**
|
|
* Fully restored characters are told they are already at max.
|
|
*/
|
|
public function test_relax_at_max_stats_reports_full(): void
|
|
{
|
|
$this->character->forceFill(['energy' => 100, 'health' => 100])->save();
|
|
|
|
$this->postJson('/city/actions/beach', ['pos_x' => 220, 'pos_z' => 30])
|
|
->assertOk()
|
|
->assertJsonPath('message', __('city.beach_full'));
|
|
}
|
|
|
|
/**
|
|
* Sleeping characters cannot relax.
|
|
*/
|
|
public function test_sleeping_character_cannot_relax(): void
|
|
{
|
|
$this->character->forceFill(['state' => Character::STATE_SLEEPING])->save();
|
|
|
|
$this->postJson('/city/actions/beach', ['pos_x' => 210, 'pos_z' => 0])
|
|
->assertStatus(422)
|
|
->assertJsonPath('message', __('city.exercise_need_awake'));
|
|
}
|
|
|
|
/**
|
|
* Position sync persists up to the shoreline (and no further).
|
|
*/
|
|
public function test_position_persists_onto_the_beach(): void
|
|
{
|
|
$onSand = $this->postJson('/city/actions/position', ['pos_x' => 250, 'pos_z' => 0])
|
|
->assertOk();
|
|
$this->assertEquals(250, $onSand->json('pos_x'));
|
|
|
|
$pastShore = $this->postJson('/city/actions/position', ['pos_x' => 400, 'pos_z' => 0])
|
|
->assertOk();
|
|
$this->assertEquals(266, $pastShore->json('pos_x'));
|
|
}
|
|
}
|