get(['id', 'name', 'type', 'pos_x', 'pos_y', 'pos_z', 'width', 'height', 'depth', 'is_open']) ->map(fn (Store $store): array => [ 'id' => $store->id, 'name' => $store->name, 'type' => $store->type, 'position' => [(float) $store->pos_x, (float) $store->pos_y, (float) $store->pos_z], 'size' => [(float) $store->width, (float) $store->height, (float) $store->depth], 'is_open' => $store->is_open, ]); $agents = AiAgent::query() ->where('status', '!=', 'paused') ->get(['id', 'name', 'type', 'status', 'pos_x', 'pos_y', 'pos_z', 'rotation_y', 'last_decision']) ->map(fn (AiAgent $agent): array => [ 'id' => $agent->id, 'name' => $agent->name, 'type' => $agent->type, 'status' => $agent->status, 'position' => [(float) $agent->pos_x, (float) $agent->pos_y, (float) $agent->pos_z], 'rotation_y' => (float) $agent->rotation_y, 'last_decision' => $agent->last_decision, ]); return response()->json([ 'generated_at' => now()->toIso8601String(), 'stores' => $stores, 'agents' => $agents, 'layout' => self::cityLayout($stores->all()), ])->header('Cache-Control', 'no-store'); } /** * Derive a deterministic street layout from store positions. * * Returns road segments and block rectangles so the frontend can build * a consistent grid-based city without additional database tables. * * @param array> $stores * @return array */ private static function cityLayout(array $stores): array { if ($stores === []) { return ['roads' => [], 'blocks' => []]; } $xs = array_map(fn ($s) => $s['position'][0], $stores); $zs = array_map(fn ($s) => $s['position'][2], $stores); $minX = floor(min($xs) / 40) * 40 - 40; $maxX = ceil(max($xs) / 40) * 40 + 40; $minZ = floor(min($zs) / 40) * 40 - 40; $maxZ = ceil(max($zs) / 40) * 40 + 40; $roads = []; // Vertical roads every 40 units. for ($x = $minX; $x <= $maxX; $x += 40) { $roads[] = [ 'from' => [(float) $x, 0.0, (float) $minZ], 'to' => [(float) $x, 0.0, (float) $maxZ], 'width' => 8.0, 'axis' => 'z', ]; } // Horizontal roads every 40 units. for ($z = $minZ; $z <= $maxZ; $z += 40) { $roads[] = [ 'from' => [(float) $minX, 0.0, (float) $z], 'to' => [(float) $maxX, 0.0, (float) $z], 'width' => 8.0, 'axis' => 'x', ]; } // Blocks = cells between roads. $blocks = []; for ($x = $minX; $x < $maxX; $x += 40) { for ($z = $minZ; $z < $maxZ; $z += 40) { $blocks[] = [ 'min' => [(float) $x + 5.0, 0.0, (float) $z + 5.0], 'max' => [(float) $x + 35.0, 0.0, (float) $z + 35.0], ]; } } return ['roads' => $roads, 'blocks' => $blocks]; } }