TypePHP 编译器 https://swoole.com/aot/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

287 lines
9.5 KiB

<?php
declare(strict_types=1);
const KEY_W = 0x57;
const KEY_A = 0x41;
const KEY_S = 0x53;
const KEY_D = 0x44;
const KEY_ESCAPE = 0x1B;
const WEATHER_SUNNY = 0;
const WEATHER_CLOUDY = 1;
const WEATHER_RAIN = 2;
const WORLD_CHUNK_SIZE = 96;
const RENDER_CHUNK_RADIUS = 4;
const KEEP_CHUNK_RADIUS = 5;
const CHUNKS_PER_FRAME = 1;
function weather_name(int $weather): string
{
if ($weather === WEATHER_CLOUDY) {
return 'cloudy';
}
if ($weather === WEATHER_RAIN) {
return 'rainy';
}
return 'sunny';
}
function choose_next_weather(int $current): int
{
$roll = mt_rand(0, 99);
if ($current === WEATHER_SUNNY) {
return $roll < 58 ? WEATHER_CLOUDY : ($roll < 78 ? WEATHER_RAIN : WEATHER_SUNNY);
}
if ($current === WEATHER_CLOUDY) {
return $roll < 42 ? WEATHER_SUNNY : ($roll < 76 ? WEATHER_RAIN : WEATHER_CLOUDY);
}
return $roll < 52 ? WEATHER_CLOUDY : ($roll < 76 ? WEATHER_SUNNY : WEATHER_RAIN);
}
function floor_chunk(float $value): int
{
$v = (int) floor($value);
if ($v >= 0) {
return intdiv($v, WORLD_CHUNK_SIZE);
}
return -intdiv(-$v + WORLD_CHUNK_SIZE - 1, WORLD_CHUNK_SIZE);
}
function chunk_key(int $chunkX, int $chunkZ): string
{
return (string) $chunkX . ':' . (string) $chunkZ;
}
function hash01(int $x, int $z, int $salt): float
{
$n = sin($x * 127.1 + $z * 311.7 + $salt * 74.7) * 43758.5453123;
return $n - floor($n);
}
function sort_pending_chunks(array $pendingChunks, int $centerChunkX, int $centerChunkZ): array
{
uasort($pendingChunks, static function (array $a, array $b) use ($centerChunkX, $centerChunkZ): int {
$adx = (int) $a[0] - $centerChunkX;
$adz = (int) $a[1] - $centerChunkZ;
$bdx = (int) $b[0] - $centerChunkX;
$bdz = (int) $b[1] - $centerChunkZ;
return ($adx * $adx + $adz * $adz) <=> ($bdx * $bdx + $bdz * $bdz);
});
return $pendingChunks;
}
function enqueue_visible_chunks(int $centerChunkX, int $centerChunkZ, array $loadedChunks, array $pendingChunks): array
{
$queued = 0;
for ($cx = $centerChunkX - RENDER_CHUNK_RADIUS; $cx <= $centerChunkX + RENDER_CHUNK_RADIUS; $cx++) {
for ($cz = $centerChunkZ - RENDER_CHUNK_RADIUS; $cz <= $centerChunkZ + RENDER_CHUNK_RADIUS; $cz++) {
$key = chunk_key($cx, $cz);
if (!isset($loadedChunks[$key]) && !isset($pendingChunks[$key])) {
$pendingChunks[$key] = [$cx, $cz];
$queued++;
}
}
}
return $queued > 0 ? sort_pending_chunks($pendingChunks, $centerChunkX, $centerChunkZ) : $pendingChunks;
}
function generate_world_chunk(int $chunkX, int $chunkZ): void
{
ocean_begin_chunk($chunkX, $chunkZ);
$baseX = $chunkX * WORLD_CHUNK_SIZE;
$baseZ = $chunkZ * WORLD_CHUNK_SIZE;
$chunkRoll = hash01($chunkX, $chunkZ, 101);
if (($chunkX === 0 && $chunkZ === -2) || $chunkRoll > 0.86) {
$x = $baseX + WORLD_CHUNK_SIZE * (0.35 + hash01($chunkX, $chunkZ, 102) * 0.30);
$z = $baseZ + WORLD_CHUNK_SIZE * (0.30 + hash01($chunkX, $chunkZ, 103) * 0.38);
ocean_add_marker($x, $z, 3, 8.0 + hash01($chunkX, $chunkZ, 104) * 5.0);
}
if (($chunkX === -1 && $chunkZ === -1) || $chunkRoll < 0.08) {
$x = $baseX + WORLD_CHUNK_SIZE * (0.42 + hash01($chunkX, $chunkZ, 105) * 0.24);
$z = $baseZ + WORLD_CHUNK_SIZE * (0.36 + hash01($chunkX, $chunkZ, 106) * 0.30);
ocean_add_marker($x, $z, 4, 5.5 + hash01($chunkX, $chunkZ, 107) * 2.4);
}
for ($i = 0; $i < 5; $i++) {
$chance = hash01($chunkX * 17 + $i, $chunkZ * 19 - $i, 3);
if ($chance < 0.48) {
continue;
}
$x = $baseX + hash01($chunkX, $chunkZ, 20 + $i) * WORLD_CHUNK_SIZE;
$z = $baseZ + hash01($chunkX, $chunkZ, 40 + $i) * WORLD_CHUNK_SIZE;
$typeRoll = hash01($chunkX, $chunkZ, 60 + $i);
$type = $typeRoll > 0.78 ? 2 : ($typeRoll > 0.52 ? 1 : 0);
$size = 0.8 + hash01($chunkX, $chunkZ, 80 + $i) * 1.8;
ocean_add_marker($x, $z, $type, $size);
}
ocean_commit_chunk($chunkX, $chunkZ);
}
function process_chunk_queue(array $loadedChunks, array $pendingChunks, int $maxChunks): array
{
$loaded = 0;
foreach ($pendingChunks as $key => $chunk) {
generate_world_chunk((int) $chunk[0], (int) $chunk[1]);
$loadedChunks[$key] = [(int) $chunk[0], (int) $chunk[1]];
unset($pendingChunks[$key]);
$loaded++;
if ($loaded >= $maxChunks) {
break;
}
}
return [$loadedChunks, $pendingChunks];
}
function unload_far_chunks(int $centerChunkX, int $centerChunkZ, array $loadedChunks): array
{
foreach ($loadedChunks as $key => $chunk) {
$dx = abs((int) $chunk[0] - $centerChunkX);
$dz = abs((int) $chunk[1] - $centerChunkZ);
if ($dx > KEEP_CHUNK_RADIUS || $dz > KEEP_CHUNK_RADIUS) {
ocean_remove_chunk((int) $chunk[0], (int) $chunk[1]);
unset($loadedChunks[$key]);
}
}
return $loadedChunks;
}
function main(): void
{
if (!ocean_init('TypePHP Ocean Demo - OpenGL', 1280, 720)) {
echo "OpenGL init failed\n";
return;
}
mt_srand((int) (ocean_get_time() * 1000000.0));
$x = 0.0;
$z = 0.0;
$yaw = 0.0;
$velocityX = 0.0;
$velocityZ = 0.0;
$cameraYaw = 0.0;
$cameraPitch = 0.24;
$cameraDistance = 24.0;
$last = ocean_get_time();
$escapeWasDown = false;
$dayTime = 0.28;
$weather = WEATHER_SUNNY;
$targetWeather = WEATHER_CLOUDY;
$weatherMix = 0.0;
$nextWeatherAt = $last + 18.0;
$centerChunkX = floor_chunk($x);
$centerChunkZ = floor_chunk($z);
$loadedChunks = [];
$pendingChunks = enqueue_visible_chunks($centerChunkX, $centerChunkZ, [], []);
while (count($pendingChunks) > 0) {
$queueState = process_chunk_queue($loadedChunks, $pendingChunks, 4);
$loadedChunks = $queueState[0];
$pendingChunks = $queueState[1];
ocean_poll_events();
}
while (!ocean_should_close()) {
$now = ocean_get_time();
$dt = max(0.001, min(0.05, $now - $last));
$last = $now;
ocean_poll_events();
$escapeDown = ocean_key_pressed(KEY_ESCAPE);
if ($escapeDown && !$escapeWasDown) {
if (ocean_confirm_exit()) {
break;
}
$last = ocean_get_time();
}
$escapeWasDown = $escapeDown;
$cameraYaw -= ocean_mouse_delta_x() * 0.0025;
$cameraPitch += ocean_mouse_delta_y() * 0.0020;
$cameraPitch = max(-0.20, min(0.85, $cameraPitch));
$inputX = 0.0;
$inputZ = 0.0;
if (ocean_key_pressed(KEY_W)) {
$inputZ -= 1.0;
}
if (ocean_key_pressed(KEY_S)) {
$inputZ += 1.0;
}
if (ocean_key_pressed(KEY_A)) {
$inputX -= 1.0;
}
if (ocean_key_pressed(KEY_D)) {
$inputX += 1.0;
}
$len = sqrt($inputX * $inputX + $inputZ * $inputZ);
if ($len > 0.0) {
$inputX /= $len;
$inputZ /= $len;
}
$accel = 18.0;
$drag = pow(0.08, $dt);
$forwardX = -sin($cameraYaw);
$forwardZ = -cos($cameraYaw);
$rightX = cos($cameraYaw);
$rightZ = -sin($cameraYaw);
$worldInputX = $rightX * $inputX + $forwardX * -$inputZ;
$worldInputZ = $rightZ * $inputX + $forwardZ * -$inputZ;
if ($len > 0.0) {
$yaw = atan2($worldInputX, -$worldInputZ);
}
$velocityX = ($velocityX + $worldInputX * $accel * $dt) * $drag;
$velocityZ = ($velocityZ + $worldInputZ * $accel * $dt) * $drag;
$x += $velocityX * $dt;
$z += $velocityZ * $dt;
$speed = sqrt($velocityX * $velocityX + $velocityZ * $velocityZ);
$currentChunkX = floor_chunk($x);
$currentChunkZ = floor_chunk($z);
if ($currentChunkX !== $centerChunkX || $currentChunkZ !== $centerChunkZ) {
$centerChunkX = $currentChunkX;
$centerChunkZ = $currentChunkZ;
$pendingChunks = enqueue_visible_chunks($centerChunkX, $centerChunkZ, $loadedChunks, $pendingChunks);
$loadedChunks = unload_far_chunks($centerChunkX, $centerChunkZ, $loadedChunks);
}
$queueState = process_chunk_queue($loadedChunks, $pendingChunks, CHUNKS_PER_FRAME);
$loadedChunks = $queueState[0];
$pendingChunks = $queueState[1];
$dayTime += $dt / 96.0;
if ($dayTime >= 1.0) {
$dayTime -= 1.0;
}
if ($now >= $nextWeatherAt && $targetWeather === $weather) {
$targetWeather = choose_next_weather($weather);
$weatherMix = 0.0;
$nextWeatherAt = $now + 16.0 + mt_rand(0, 16);
echo 'Weather changed: ' . weather_name($targetWeather) . PHP_EOL;
}
if ($targetWeather !== $weather) {
$weatherMix = min(1.0, $weatherMix + $dt / 7.0);
if ($weatherMix >= 1.0) {
$weather = $targetWeather;
$weatherMix = 0.0;
}
}
$rainAmount = $weather === WEATHER_RAIN ? 1.0 : 0.0;
if ($targetWeather === WEATHER_RAIN) {
$rainAmount = max($rainAmount, $weatherMix);
} elseif ($weather === WEATHER_RAIN) {
$rainAmount = 1.0 - $weatherMix;
}
ocean_set_boat($x, $z, $yaw, $speed);
ocean_set_camera($cameraYaw, $cameraPitch, $cameraDistance);
ocean_set_environment($dayTime, $targetWeather, $weatherMix, max(0.0, min(1.0, $rainAmount)));
ocean_render_frame();
ocean_sleep(1);
}
ocean_shutdown();
}