feat(world): add persistent citizen simulation with needs, jobs, and social features
- Adds Character model with survival needs (hunger, thirst, energy) and state machine - Implements city:tick world ticker for offline progression every minute - Adds city territory expansion and building construction system - Introduces job economy, inventory, and transaction audit trail - Adds social graph (friends, marriage, children) and character chat - Implements admin approval workflow for new registrations - Adds hospital admission system for traffic accidents - Includes comprehensive feature tests and world seeders
This commit is contained in:
Executable
+109
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\City;
|
||||
use App\Models\LegalDocument;
|
||||
use App\Models\User;
|
||||
use App\Models\UserConsent;
|
||||
use Database\Seeders\CityWorldSeeder;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Admin territory expansion and multi-city creation.
|
||||
*/
|
||||
class CityExpansionTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
/**
|
||||
* Seed world fixtures.
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->seed(CityWorldSeeder::class);
|
||||
$this->admin = User::factory()->admin()->create();
|
||||
foreach (LegalDocument::where('is_current', true)->pluck('id') as $docId) {
|
||||
UserConsent::create([
|
||||
'user_id' => $this->admin->id,
|
||||
'legal_document_id' => $docId,
|
||||
'accepted_at' => now(),
|
||||
'ip_address' => '127.0.0.1',
|
||||
'user_agent' => 'phpunit',
|
||||
'source' => 'web_form',
|
||||
]);
|
||||
}
|
||||
$this->actingAs($this->admin);
|
||||
}
|
||||
|
||||
protected User $admin;
|
||||
|
||||
/**
|
||||
* Admin can create a new city with its own bounds.
|
||||
*/
|
||||
public function test_create_city(): void
|
||||
{
|
||||
$before = City::count();
|
||||
$this->post(route('admin.cities.store'), ['name' => 'Nova Cidade'])
|
||||
->assertRedirect(route('admin.cities.index'));
|
||||
|
||||
$this->assertSame($before + 1, City::count());
|
||||
$city = City::where('name', 'Nova Cidade')->first();
|
||||
$this->assertNotNull($city);
|
||||
$this->assertFalse($city->is_active);
|
||||
$this->assertSame(-80.0, $city->bounds_min_x);
|
||||
$this->assertSame(80.0, $city->bounds_max_x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expanding a city grows its maximum bounds by one grid step.
|
||||
*/
|
||||
public function test_expand_city_bounds(): void
|
||||
{
|
||||
$city = City::where('slug', 'city10')->firstOrFail();
|
||||
$beforeMax = $city->bounds_max_x;
|
||||
|
||||
$this->post(route('admin.cities.expand', $city))
|
||||
->assertRedirect(route('admin.cities.index'));
|
||||
|
||||
$city->refresh();
|
||||
$this->assertSame($beforeMax + 40, $city->bounds_max_x);
|
||||
$this->assertSame(1, $city->expansions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expansion is capped to protect map generation.
|
||||
*/
|
||||
public function test_expand_capped(): void
|
||||
{
|
||||
$city = City::where('slug', 'city10')->firstOrFail();
|
||||
$city->forceFill(['expansions' => City::MAX_EXPANSIONS])->save();
|
||||
|
||||
$this->from(route('admin.cities.index'))
|
||||
->post(route('admin.cities.expand', $city))
|
||||
->assertSessionHasErrors('city');
|
||||
}
|
||||
|
||||
/**
|
||||
* City layout for a second city uses its own bounds.
|
||||
*/
|
||||
public function test_city_data_for_second_city(): void
|
||||
{
|
||||
$other = City::create([
|
||||
'name' => 'Cidade B',
|
||||
'slug' => 'cidade-b',
|
||||
'bounds_min_x' => 0,
|
||||
'bounds_max_x' => 120,
|
||||
'bounds_min_z' => 0,
|
||||
'bounds_max_z' => 80,
|
||||
'is_active' => false,
|
||||
'expansions' => 0,
|
||||
]);
|
||||
|
||||
$response = $this->getJson('/city/data?city=cidade-b');
|
||||
$response->assertOk()->assertJsonPath('city.slug', 'cidade-b');
|
||||
$this->assertNotEmpty($response->json('layout.roads'));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user