- Player-to-player marketplace with listings, buying and selling - Mission system with progress tracking and money rewards - Character appearance customization (skin, hair, face, beard, accessory) - Multiplayer presence feed showing nearby citizens in near-real-time - Admin world dashboard with live platform metrics - Bus and train transportation with temporary ride state - Mobile joystick controls and minimap HUD - Education bonus applied to work salary
140 lines
4.3 KiB
PHP
Executable File
140 lines
4.3 KiB
PHP
Executable File
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\Character;
|
|
use App\Models\User;
|
|
use App\Support\CityPlazas;
|
|
use Database\Seeders\CityWorldSeeder;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Tests\TestCase;
|
|
|
|
/**
|
|
* Public plaza gym equipment and health gains from exercise.
|
|
*/
|
|
class PlazaExerciseTest 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);
|
|
}
|
|
|
|
/**
|
|
* City payload includes plazas with gym equipment.
|
|
*/
|
|
public function test_city_data_includes_plazas(): void
|
|
{
|
|
$this->getJson('/city/data')
|
|
->assertOk()
|
|
->assertJsonStructure([
|
|
'plazas' => [
|
|
['id', 'position', 'size', 'equipment' => [['id', 'kind', 'position']]],
|
|
],
|
|
]);
|
|
|
|
$this->assertNotEmpty(CityPlazas::forCity($this->character->city));
|
|
}
|
|
|
|
/**
|
|
* Exercising near equipment raises health and increments the counter.
|
|
*/
|
|
public function test_exercise_gains_health_near_equipment(): void
|
|
{
|
|
$eq = CityPlazas::forCity($this->character->city)[0]['equipment'][0];
|
|
$this->character->forceFill([
|
|
'pos_x' => $eq['position'][0],
|
|
'pos_z' => $eq['position'][1],
|
|
'health' => 80,
|
|
'energy' => 90,
|
|
'state' => Character::STATE_AWAKE,
|
|
])->save();
|
|
|
|
$this->postJson('/city/actions/exercise', [
|
|
'pos_x' => $eq['position'][0],
|
|
'pos_z' => $eq['position'][1],
|
|
])->assertOk()
|
|
->assertJsonPath('character.health', 82)
|
|
->assertJsonPath('character.state', Character::STATE_EXERCISING);
|
|
|
|
$this->character->refresh();
|
|
$this->assertSame(82, (int) $this->character->health);
|
|
$this->assertSame(1, (int) $this->character->exercise_count);
|
|
$this->assertSame(88, (int) $this->character->energy);
|
|
}
|
|
|
|
/**
|
|
* Far from equipment the action is rejected.
|
|
*/
|
|
public function test_exercise_requires_nearby_equipment(): void
|
|
{
|
|
$eq = CityPlazas::forCity($this->character->city)[0]['equipment'][0];
|
|
$this->character->forceFill([
|
|
'pos_x' => $eq['position'][0] + 50,
|
|
'pos_z' => $eq['position'][1] + 50,
|
|
'health' => 50,
|
|
])->save();
|
|
|
|
$this->postJson('/city/actions/exercise', [
|
|
'pos_x' => $eq['position'][0] + 50,
|
|
'pos_z' => $eq['position'][1] + 50,
|
|
])->assertStatus(422);
|
|
}
|
|
|
|
/**
|
|
* Max health refuses further gains but keeps the character healthy.
|
|
*/
|
|
public function test_exercise_stops_at_max_health(): void
|
|
{
|
|
$eq = CityPlazas::forCity($this->character->city)[0]['equipment'][0];
|
|
$this->character->forceFill([
|
|
'pos_x' => $eq['position'][0],
|
|
'pos_z' => $eq['position'][1],
|
|
'health' => 100,
|
|
])->save();
|
|
|
|
$this->postJson('/city/actions/exercise', [
|
|
'pos_x' => $eq['position'][0],
|
|
'pos_z' => $eq['position'][1],
|
|
])->assertOk()
|
|
->assertJsonPath('character.health', 100);
|
|
|
|
$this->assertSame(0, (int) $this->character->fresh()->exercise_count);
|
|
}
|
|
|
|
/**
|
|
* stop-exercise returns the character to awake.
|
|
*/
|
|
public function test_stop_exercise_returns_awake(): void
|
|
{
|
|
$eq = CityPlazas::forCity($this->character->city)[0]['equipment'][0];
|
|
$this->character->forceFill([
|
|
'pos_x' => $eq['position'][0],
|
|
'pos_z' => $eq['position'][1],
|
|
'health' => 70,
|
|
'state' => Character::STATE_AWAKE,
|
|
])->save();
|
|
|
|
$this->postJson('/city/actions/exercise', [
|
|
'pos_x' => $eq['position'][0],
|
|
'pos_z' => $eq['position'][1],
|
|
])->assertOk();
|
|
|
|
$this->postJson('/city/actions/stop-exercise')
|
|
->assertOk()
|
|
->assertJsonPath('character.state', Character::STATE_AWAKE);
|
|
}
|
|
}
|