CalculePaletteAttenuee(mob3D + 1, coeff, 33) by (int)(256 * coeff) */ private array $attenuees = []; /** @var array */ private array $TabEtoiles = []; private int $CoulPulse = 0; private int $meteore = 0; private int $xMeteore = 0; private int $yMeteore = 0; /** @var array star bands by magnitude / variant: rows and masks */ private array $bandesEtoiles = []; /** @var array */ private array $TabLigneSol = []; private int $lastDzeta = 0; private int $SolRandSeed = 0x2548795A; /** @var array scaled rows of blit3d */ private array $cache3d = []; /** @var array rows of the storm pattern (even / odd) and their masks */ private array $trameOrage = []; public function sinshort(int $angle): int { return (int)(255 * sin($angle * 3.1415925 / 512.0)); } public function cosshort(int $angle): int { return (int)(255 * cos($angle * 3.1415925 / 512.0)); } /** distance: max + 2/5 min, at most 32700 */ public function distance(int $dx, int $dy): int { $dx = abs($dx); $dy = abs($dy); if ($dx > $dy) { $dx = $dx + intdiv($dy * 2, 5); } else { $dx = $dy + intdiv($dx * 2, 5); } return $dx > 32700 ? 32700 : $dx; } /** * CalcPoint3D: a point of the world on the screen. * @return array{0:int,1:int,2:int}|null x, y, distance; null when not visible */ private function CalcPoint3D(int $xj, int $yj, int $zj, float $ca, float $sa, int $xp, int $yp, int $zp): ?array { $xp -= $xj; $yp -= $yj; $zp -= $zj; $tmp = (int)($xp * $ca - $yp * $sa); $yp = (int)($xp * $sa + $yp * $ca); $xp = $tmp; if ($xp <= 2) { return null; } $dist = $this->distance($xp, $yp); if ($dist === 0) { return null; } $xl = intdiv(self::LARGEUR_ECRAN, 2) - intdiv(intdiv(self::LARGEUR_ECRAN, 2) * $yp, $xp); $yl = intdiv(self::HAUTEUR_ECRAN, 2) - intdiv(intdiv(self::HAUTEUR_ECRAN, 2) * $zp, $xp); $xl = max(-32760, min(32760, $xl)); $yl = max(-32760, min(32760, $yl)); return [$xl, $yl, $dist]; } /** SapDep: the player walks with the angle and the speed */ public function sapdep(int $angle, int $vitesse): void { $tmp = $this->xj + (($this->cosshort($angle) * $vitesse) >> 8); $this->xj = max(0, min(65535, $tmp)); $tmp = $this->yj + (($this->sinshort($angle) * $vitesse) >> 8); $this->yj = max(0, min(65535, $tmp)); $this->AvanceSol($vitesse); } /** f32: the value as a C float */ private static function f32(float $v): float { return unpack('g', pack('g', $v))[1]; } /** CalculeDegradeDuCiel: the 128 colours of the sky from an ST palette of 8 colours */ private function CalculeDegradeDuCiel(array $palette): array { $out = []; $dep = [0, 0, 0]; $inc = [0, 0, 0]; for ($i = $j = 0; $i < self::NB_LIGNES_CIEL; $i++) { if ($i === self::TAB_LIGN[$j]) { $dep = $this->stRgb3d($palette[$j++]); $dest = $this->stRgb3d($palette[$j]); $dist = self::TAB_LIGN[$j] - self::TAB_LIGN[$j - 1]; for ($k = 0; $k < 3; $k++) { $inc[$k] = intdiv($dest[$k] - $dep[$k], $dist); } } for ($k = 0; $k < 3; $k++) { $dep[$k] = ($dep[$k] + $inc[$k]) & 0xFFFF; } $out[$i] = Mac::color2Index($dep[0], $dep[1], $dep[2]); } return $out; } /** CvtCouleurStEnRgb */ private function stRgb3d(int $val): array { return [(($val & 0x700) >> 8) * 9362, (($val & 0x070) >> 4) * 9362, ($val & 0x007) * 9362]; } /** CalculeDegradeDistance: the palettes of the objects by distance for an hour */ private function CalculeDegradeDistance(int $heure): array { if ($heure > 512 - 50 || $heure < 50) { $pos = $heure < 50 ? $heure : 512 - $heure; $coeff = self::f32(self::f32(0.7 + self::f32($pos / 200.0)) + self::f32($this->IotaPaletteStd)); $delta = self::f32(0.5 / self::NB_DEGRADE_DISTANCE); } else { $coeff = self::f32(1.0 + self::f32($this->IotaPaletteStd)); $delta = self::f32(0.4 / self::NB_DEGRADE_DISTANCE); } $out = []; for ($i = 0; $i < self::NB_DEGRADE_DISTANCE; $i++) { $key = (int)(256.0 * $coeff); $out[$i] = $this->attenuees[$key] ??= $this->CalculePaletteAttenuee($this->mob3D + 1, $coeff, 33); $coeff = self::f32($coeff - $delta); if ($coeff < 0) { $coeff = 0.0; } } return $out; } /** CalculeToutDegrades: the sky and distance palettes of every hour */ public function CalculeToutDegrades(): void { foreach ($this->tabheur as $i => $h) { if ($h['q']) { $this->tabheur[$i]['TabCouleursCiel'] = $this->CalculeDegradeDuCiel($h['pal']); $this->tabheur[$i]['TabPaletteDistance'] = $this->CalculeDegradeDistance($h['h']); if ($this->TabPaletteDistance === []) { $this->FixeDegradesFonctionHeure($this->tabheur[$i]); } } } $this->CouleurOrageJour = Mac::color2Index(20000, 20000, 20000); $this->CouleurOrageNuit = Mac::color2Index(8000, 8000, 8000); } public function ChangeIotaPalette(float $combien): void { $this->IotaPaletteStd += $combien; $this->TabPaletteDistance = []; $this->CalculeToutDegrades(); if ($this->dervue === self::VUE_PAYSAGE) { $this->affich($this->dzeta); $this->tfrima(); } } /** FixeDegradesFonctionHeure: the colours of an entry of tabheur become the current ones */ public function FixeDegradesFonctionHeure(array $chron): void { if (isset($chron['TabCouleursCiel'])) { $this->TabCouleursCiel = $chron['TabCouleursCiel']; $this->TabPaletteDistance = $chron['TabPaletteDistance']; } } /** CalculeEtoiles: 10000 stars in a band of 2048 x 128 */ public function CalculeEtoiles(): void { $this->TabEtoiles = []; $this->bandesEtoiles = []; $this->CoulPulse = Mac::color2Index(65535, 65535, 65535); for ($i = 0; $i < self::NB_ETOILES; $i++) { $x = $this->myRandom(0, self::AIRE_X_ETOILE); $y = $this->myRandom(0, self::AIRE_Y_ETOILE); $m = $this->myRandom(0, self::MAX_MAGNITUDE); $c = 55000 - $y * intdiv(32768, self::AIRE_Y_ETOILE); $this->TabEtoiles[$i] = ['x' => $x, 'y' => $y, 'magnitude' => $m, 'couleur' => Mac::color2Index($c, $c, 0)]; } } /** * The stars of magnitude <= $magnitude drawn in a band (mirrored: screen x shows the band at * (x + 768 - 2 dzeta) & 2047), twinkling variant $variant (half the stars white, at random). * @return array{0:string[],1:string[]} rows (2048 + 512 wide) and masks */ private function bandeEtoiles(int $magnitude, int $variant): array { $key = $magnitude . '-' . $variant; if (isset($this->bandesEtoiles[$key])) { return $this->bandesEtoiles[$key]; } if (count($this->bandesEtoiles) >= 8) { $this->bandesEtoiles = []; } $rows = array_fill(0, self::AIRE_Y_ETOILE, str_repeat("\0", 2048)); $masks = array_fill(0, self::AIRE_Y_ETOILE, str_repeat("\xFF", 2048)); mt_srand(1234 + $variant); foreach ($this->TabEtoiles as $p) { if ($p['magnitude'] <= $magnitude) { $k = (2048 - $p['x']) & 2047; $rows[$p['y']][$k] = chr(mt_rand(0, 255) > 127 ? $this->CoulPulse : $p['couleur']); $masks[$p['y']][$k] = "\0"; } } mt_srand(); foreach ($rows as $y => $r) { $rows[$y] = $r . substr($r, 0, 512); $masks[$y] = $masks[$y] . substr($masks[$y], 0, 512); } return $this->bandesEtoiles[$key] = [$rows, $masks]; } /** AfficheEtoiles: the stars on the sky at night, a meteor sometimes, the pole star */ public function AfficheEtoiles(): void { if (!($this->heure > 480 || $this->heure < 20)) { return; } if ($this->heure < self::MAX_MAGNITUDE) { $magnitude = self::MAX_MAGNITUDE - $this->heure; } elseif ($this->heure < 480 + self::MAX_MAGNITUDE) { $magnitude = $this->heure - (480 + self::MAX_MAGNITUDE); } else { $magnitude = self::MAX_MAGNITUDE; } if ($magnitude >= 0 && $this->TabEtoiles !== []) { [$rows, $masks] = $this->bandeEtoiles($magnitude, $this->Rnd() & 3); $port = $this->curPort; $s = (768 - $this->dzeta * 2) & 2047; for ($y = 0; $y < self::AIRE_Y_ETOILE; $y++) { $port->rows[$y] = (substr($port->rows[$y], 0, 512) & substr($masks[$y], $s, 512)) | substr($rows[$y], $s, 512); } $port->dirty = true; } if ($this->meteore) { $x = (($this->dzeta * 2 - $this->xMeteore - 512) & 2047) - 512 + 256; $this->Affmobbrt($this->mob3D + 21, intdiv($x, 2), intdiv($this->yMeteore, 2), 0); $this->meteore--; $this->xMeteore += 5; $this->yMeteore += 5; } elseif ($this->myRandom(0, 1000) > 990) { $this->xMeteore = $this->myRandom(0, self::AIRE_X_ETOILE); $this->yMeteore = $this->myRandom(0, intdiv(self::AIRE_Y_ETOILE, 2)); $this->meteore = $this->myRandom(3, 6); } // the pole star $x = (($this->dzeta * 2 + 512) % 2048) - 512 + 256; $this->Affmobbrt($this->mob3D + $this->myRandom(22, 24), intdiv($x, 2), 20, 0); } // ---- the ground -------------------------------------------------------------------------------- private function CalculeDimensionsLignes(): void { $t = &$this->TabLigneSol; for ($i = 1; $i < self::NB_INTERV; $i++) { $t[$i]['hauteur'] = self::ss($t[$i]['y'] - $t[$i - 1]['y']); $t[$i]['largeur'] = self::ss(intdiv(self::LARGEUR_MOB * $t[$i]['hauteur'], self::HAUTEUR_MOB)); } $t[0]['hauteur'] = $t[0]['y']; $t[0]['largeur'] = self::ss(intdiv(self::LARGEUR_MOB * $t[0]['hauteur'], self::HAUTEUR_MOB)); } /** TourneSol: the player turns his head, the bands of the ground move */ private function TourneSol(): void { if ($this->dzeta !== $this->lastDzeta) { $delta = self::ss($this->dzeta - $this->lastDzeta); $t = &$this->TabLigneSol; for ($i = 1; $i < self::NB_INTERV; $i++) { $h = $t[$i]['hauteur']; if ($h) { $o = self::ss($t[$i]['offsetEnMob'] + intdiv(($delta * 2) * self::HAUTEUR_MOB, $h)); $o %= self::LARGEUR_MOB; $t[$i]['offsetEnMob'] = $o; $t[$i]['offsetEcran'] = self::ss(intdiv(-$o * $h, self::HAUTEUR_MOB)); } } $this->lastDzeta = $this->dzeta; } } private function CalcY(int $idx): int { $dist = $this->TabLigneSol[$idx]['dist']; if ($dist === 0) { return 0; } return self::ss(intdiv(256 * (38 + intdiv($this->zj, 20)), $dist)); } /** AvanceSol: the player walks, the bands of the ground come nearer */ public function AvanceSol(int $dist): void { if ($dist) { $this->SolRandSeed = self::l32($this->TickCount() + $this->xj + $this->yj); } if ($this->TabLigneSol === []) { for ($i = 0; $i < self::NB_INTERV; $i++) { $this->TabLigneSol[$i] = ['y' => 0, 'hauteur' => 0, 'largeur' => 0, 'offsetEnMob' => 0, 'offsetEcran' => 0, 'dist' => 0]; } } $t = &$this->TabLigneSol; if ($t[0]['dist'] === 0) { $d = 70; for ($i = 0; $i < self::NB_INTERV; $i++) { $t[self::NB_INTERV - 1 - $i]['dist'] = $d; $t[self::NB_INTERV - 1 - $i]['y'] = $this->CalcY(self::NB_INTERV - 1 - $i); $d += self::LARGEUR_BANDE_SOL; } } else { for ($i = 0; $i < self::NB_INTERV; $i++) { $t[$i]['dist'] -= $dist; $t[$i]['y'] = $this->CalcY($i); } // the nearest band went away? while ($t[self::NB_INTERV - 1]['dist'] < 70) { for ($i = self::NB_INTERV - 1; $i > 0; $i--) { $t[$i] = $t[$i - 1]; } $t[0]['dist'] = $t[1]['dist'] + self::LARGEUR_BANDE_SOL; $t[0]['y'] = $this->CalcY(0); } } } private static function numPaletteSol(int $bottom): int { if ($bottom > self::NB_LIGNES_CIEL) { $n = (self::NB_LIGNES_CIEL + self::NB_LIGNES_TERRE) - $bottom; } else { $n = self::NB_LIGNES_CIEL - self::NB_LIGNES_TERRE - $bottom; } return max(0, min(self::NB_DEGRADE_DISTANCE - 1, $n)); } /** AfficheSol: the bands of the ground (far: thin bands of mob 25, near: mob 20) */ private function AfficheSol(): void { $this->AvanceSol(0); $this->CalculeDimensionsLignes(); $this->TourneSol(); $t = $this->TabLigneSol; for ($i = 1; $i < self::NB_INTERV && $t[$i]['hauteur'] < 4; $i++) { } if ($i < self::NB_INTERV) { $ymax = $t[$i]['y'] + self::NB_LIGNES_CIEL; } else { $ymax = self::NB_LIGNES_CIEL + self::NB_LIGNES_TERRE; } $largeur = intdiv(self::HAUTEUR_MIN_BANDE * self::LARGEUR_MOB * 4, self::HAUTEUR_MOB); $randSeedBak = $this->randSeed; $this->randSeed = $this->SolRandSeed; $map = $this->TabPaletteDistance; for ($j = $ymax; $j > self::NB_LIGNES_CIEL; $j -= self::HAUTEUR_MIN_BANDE) { $offset = (2048 - $this->dzeta * 2 + $this->Rnd()) % $largeur; if ($j < self::NB_LIGNES_CIEL + self::HAUTEUR_MIN_BANDE) { $r = [256 - $offset, self::NB_LIGNES_CIEL, 256 + $largeur - $offset, $j]; } else { $r = [256 - $offset, $j - self::HAUTEUR_MIN_BANDE, 256 + $largeur - $offset, $j]; } $pal = $map[self::numPaletteSol($r[3])] ?? null; $this->bandeSol($this->mob3D + 25, $r, $largeur, 256 - $offset, $pal); } for (; $i < self::NB_INTERV; $i++) { $hauteur = $t[$i]['hauteur']; $largeur = $t[$i]['largeur']; if ($hauteur > 0 && $largeur > 0) { $offset = $t[$i]['offsetEcran']; if ($i) { $r = [256 - $offset, $t[$i - 1]['y'] + self::NB_LIGNES_CIEL, 256 + $largeur - $offset, $t[$i]['y'] + self::NB_LIGNES_CIEL]; } else { $r = [256 - $offset, self::NB_LIGNES_CIEL, 256 + $largeur - $offset, $t[$i]['y'] + self::NB_LIGNES_CIEL]; } $pal = $map[self::numPaletteSol($r[3])] ?? null; $this->bandeSol($this->mob3D + 20, $r, $largeur, 256 - $offset, $pal); } } $this->randSeed = $randSeedBak; } /** One band of the ground: the tile repeated right then left of $start */ private function bandeSol(int $num, array $r, int $largeur, int $start, ?string $pal): void { $rdep = $r; for ($k = $start; $k < 512; $k += $largeur) { $this->blit3d($num, $r, $pal); $r[0] += $largeur; $r[2] += $largeur; } $r = $rdep; for ($k = $start; $k > 0; $k -= $largeur) { $r[0] -= $largeur; $r[2] -= $largeur; $this->blit3d($num, $r, $pal); } } /** * Affmob3d with a cache: the rows of the mob scaled to the size of $aire (same sampling as * Affmob3d), clipped to the current port. */ private function blit3d(int $num, array $aire, ?string $tabIndex): void { $mob = $this->TabMob[$num] ?? null; if ($mob === null || $mob->nbcol <= 0 || $mob->nbligne <= 0) { return; } [$l, $t, $r, $b] = $aire; $i = $r - $l; $j = $b - $t; if ($i <= 0 || $j <= 0) { return; } $port = $this->curPort; if ($l >= $port->w || $t >= $port->h || $r <= 0 || $b <= 0) { return; } $key = $num . '|' . $i . '|' . $j . '|' . ($tabIndex ?? ''); if (!isset($this->cache3d[$key])) { if (count($this->cache3d) > 300) { $this->cache3d = []; } $tx = $mob->nbcol; $ty = $mob->nbligne; $pasX = intdiv($tx << 16, $i); $pasY = intdiv($ty << 16, $j); // columns: runs of the same source column $runs = []; $sx = 0; $rest = 0; for ($k = 0; $k < $i; $k++) { $c = min($sx, $tx - 1); $n = count($runs); if ($n && $runs[$n - 1][0] === $c) { $runs[$n - 1][1]++; } else { $runs[] = [$c, 1]; } $rest += $pasX; $sx += $rest >> 16; $rest &= 0xFFFF; } $identity = $i === $tx; $srcRows = []; $rows = []; $masks = []; $sy = 0; $restY = 0; for ($y = 0; $y < $j && $sy < $ty; $y++) { if (!isset($srcRows[$sy])) { $src = $tabIndex === null ? $mob->rows[$sy] : $mob->rowWith($sy, $tabIndex); $mask = $mob->masks[$sy]; if (!$identity) { $s = ''; $m = ''; foreach ($runs as [$c, $n]) { $s .= str_repeat($src[$c], $n); $m .= str_repeat($mask[$c], $n); } $src = $s; $mask = $m; } $srcRows[$sy] = [$src, $mask]; } $rows[$y] = $srcRows[$sy][0]; $masks[$y] = $srcRows[$sy][1]; $restY += $pasY; $sy += $restY >> 16; $restY &= 0xFFFF; } $this->cache3d[$key] = [$rows, $masks]; } [$rows, $masks] = $this->cache3d[$key]; $x0 = $l < 0 ? -$l : 0; $dl = max(0, $l); $w = min($port->w, $r) - $dl; if ($w <= 0) { return; } $whole = $x0 === 0 && $w === $i; $y1 = min($b, $port->h); for ($y = max(0, $t); $y < $y1; $y++) { $k = $y - $t; if (!isset($rows[$k])) { break; } $s = $whole ? $rows[$k] : substr($rows[$k], $x0, $w); $m = $whole ? $masks[$k] : substr($masks[$k], $x0, $w); $row = $port->rows[$y]; $port->rows[$y] = substr_replace($row, (substr($row, $dl, $w) & $m) | $s, $dl, $w); } $port->dirty = true; } // ---- storm, rain ------------------------------------------------------------------------------ /** TrameCielOrage: a grey checkerboard over the sky */ private function TrameCielOrage(int $index): void { $port = $this->curPort; $w = $port->w; $c = chr($index); $even = str_repeat($c . "\0", intdiv($w, 2)); $evenMask = str_repeat("\0\xFF", intdiv($w, 2)); $odd = str_repeat("\0" . $c, intdiv($w, 2)); $oddMask = str_repeat("\xFF\0", intdiv($w, 2)); for ($y = 0; $y < self::NB_LIGNES_CIEL; $y += 2) { $port->rows[$y] = ($port->rows[$y] & $evenMask) | $even; $port->rows[$y + 1] = ($port->rows[$y + 1] & $oddMask) | $odd; } $port->dirty = true; } /** Flou: every third pixel takes the colour of the next one */ private function Flou(): void { $port = $this->curPort; $w = $port->w; for ($y = 0; $y < 200 && $y < $port->h; $y++) { $row = $port->rows[$y]; for ($x = 0; $x + 1 < $w; $x += 3) { $row[$x] = $row[$x + 1]; } $port->rows[$y] = $row; } $port->dirty = true; } /** TracePluie: $n drops of rain (diagonal lines of 50 pixels) */ private function TracePluie(int $index, int $n): void { if ($this->myRandom(0, 1000) > 500) { $this->Flou(); } $port = $this->curPort; $w = $port->w; $size = $w * $port->h; $c = chr($index); while ($n-- > 0) { $x = $this->myRandom(0, $w); $y = $this->myRandom(0, 150); $off = $y * $w + $x; for ($j = 0; $j < 50; $j++) { if ($off >= 0 && $off < $size) { $port->rows[intdiv($off, $w)][$off % $w] = $c; } $off += $w - 1; } } $port->dirty = true; } /** eff3d: the background of the view: sky, stars, sun or moon, storm, ground */ public function eff3d(int $ciel, int $terre, int $ymeteore, int $xmeteore, int $isLune): void { $gbak = $this->curPort; $this->curPort = $this->Ecran1; if ($this->TabCouleursCiel !== []) { $this->RemplitZoneLignes(0, self::NB_LIGNES_CIEL, $this->TabCouleursCiel); } $this->AfficheEtoiles(); $this->Affmobbrt($this->MOB_SOLEIL() + $isLune, self::ss($xmeteore), self::ss($ymeteore), 0); if ($this->FreqEclair < 1000) { $this->TrameCielOrage($isLune ? $this->CouleurOrageNuit : $this->CouleurOrageJour); } $this->AfficheSol(); $this->curPort = $gbak; } /** Pluie: the rain during a storm, and water on the ground */ public function Pluie(): void { $gbak = $this->curPort; $this->curPort = $this->Ecran1; if ($this->FreqEclair < 1000 && $this->FreqEclair > 500) { $n = $this->FreqEclair > 750 ? 1000 - $this->FreqEclair : $this->FreqEclair - 500; $this->TracePluie(($this->heure & 0x200) ? $this->CouleurOrageNuit : $this->CouleurOrageJour, $n); $this->PosePluie(); } $this->curPort = $gbak; } /** PosePluie: when it rains there is water on the ground */ public function PosePluie(): void { if ($this->FreqEclair < 1000 && $this->FreqEclair > 500) { $l = $this->liste4; if (($l->taille === 0 || $l->elements[$l->taille - 1] !== 2) && $l->taille < $l->maxtaille) { $l->taille++; $this->tlpos[4] = $l->taille; $l->elements[$l->taille - 1] = 2; } } } /** CalculeAltitude: the altitude of the player (the mountains around him) */ private function CalculeAltitude(int $x, int $y): int { $z = 40; foreach ($this->montagnes as $i) { $p = $this->aigoual[$i]; $f = $this->formes[$p['forme']]; if (!$f['hauteur']) { $d = $this->distance($this->xj - $p['posx'], $this->yj - $p['posy']); $r = $f['rayon']; if ($d < $r) { $z = self::ss($z + intdiv(($r - $d) * $f['nbrdisk'] * $p['ign'], $r)); } } } return $z; } /** * Visu: draws the objects (indexes, far to near) on Ecran1. * @param int[] $liste */ public function visu(array $liste): void { $gbak = $this->curPort; $this->curPort = $this->Ecran1; $this->zj = $this->CalculeAltitude($this->xj, $this->yj); $angle = $this->dzeta * 3.1415927 / 512.0; $ca = cos(-$angle); $sa = sin(-$angle); $base = $this->mob3D; $map = $this->TabPaletteDistance; foreach ($liste as $i) { $mont = $this->aigoual[$i]; $forme = $this->formes[$mont['forme']]; $p = $this->CalcPoint3D($this->xj, $this->yj, $this->zj, $ca, $sa, $mont['posx'], $mont['posy'], $forme['hauteur']); if ($p === null) { continue; } [$x, $y, $dist] = $p; $rd = self::ss(intdiv($forme['rayon'] * 2 * 256, $dist)); $hauteurDisque = self::ss(intdiv($mont['ign'] * $forme['nbrdisk'] * 256, $dist)); $r = [self::ss($x - intdiv($rd, 2)), self::ss($y - $hauteurDisque), self::ss($x + intdiv($rd, 2)), $y]; if (!self::EmptyRect($r) && $r[0] < self::LARGEUR_ECRAN && $r[2] > 0 && $r[3] > 0 && $r[1] < self::HAUTEUR_ECRAN) { if ($r[3] > self::NB_LIGNES_CIEL) { $n = (self::NB_LIGNES_CIEL + self::NB_LIGNES_TERRE) - $r[3]; } else { $n = $r[3] - (self::NB_LIGNES_CIEL - self::NB_LIGNES_TERRE); } $n = max(0, min(self::NB_DEGRADE_DISTANCE - 1, $n)); $this->blit3d($base + $forme['numMob3d'], $r, $map[$n] ?? null); } } $this->curPort = $gbak; } /** * ChaineObjet for all the objects: the visible ones (near enough, not behind), sorted far to near * like the C chain (equal distances: the later object first). Object 0 is never drawn (C quirk). * @return int[] */ public function ChaineObjets(): array { if ($this->nbreobj === 0) { return []; } $xj = $this->xj; $yj = $this->yj; $quart = ($this->dzeta >> 8) & 3; $order = []; $cand = $this->objetsProches($xj, $yj, self::GRID); for ($k = count($cand) - 1; $k >= 0; $k--) { $i = $cand[$k]; if ($i === 0) { continue; } $mont = $this->aigoual[$i]; if ($mont['ign'] === 0) { continue; } $forme = $this->formes[$mont['forme']]; $this->aigoual[$i]['distance'] = $dist =$this->distance($mont['posx'] - $xj, $mont['posy'] - $yj); if ($dist > intdiv(256 * $forme['nbrdisk'] * $mont['ign'], 4) || $dist > intdiv(256 * $forme['rayon'], 3)) { continue; } $px = $mont['posx']; $py = $mont['posy']; if (($quart === 0 && $px < $xj && $py < $yj) || ($quart === 1 && $px > $xj && $py < $yj) || ($quart === 2 && $px > $xj && $py > $yj) || ($quart === 3 && $px < $xj && $py > $yj)) { continue; } $order[$i] = $dist; } arsort($order); return array_keys($order); } }