affcarmess(ord($debut[$i])); } $this->TfrBadAss(); } /** * TailleChaine (static of SAPREL.C): width in pixels (font 10) and number of line breaks. * @return array{0:int,1:int} [tx, ty] */ private function relTailleChaine(string $debut): array { $x = 0; $taillex = 0; $y = 0; $ty = 0; $n = strlen($debut); $i = 0; while ($i < $n) { $car = ord($debut[$i]); if ($car === 13) { $ty++; $x = 0; $i++; } else { // TailleCarMess $i++; if ($car > 128) { [$tx2, $ty2] = $this->relTailleChaine($this->GetStrBkMot($car)); $x += $tx2; $y += $ty2; } else { $x += $this->TailleCar($car, 10); } if ($x > $taillex) { $taillex = $x; } $ty += $y; } } return [$taillex, $ty]; } /** Sappause: a pause, ended by the mouse (moved or clicked) */ private function Sappause(int $duree): \Generator { while ($this->bouton) { yield; $this->vq_mouse($this->bouton, $this->sposx, $this->sposy); } $this->dsposx = $this->sposx; $this->dsposy = $this->sposy; while ($duree && !$this->bouton && $this->sposx === $this->dsposx && $this->sposy === $this->dsposy) { yield from $this->delai(1); $duree--; $this->vq_mouse($this->bouton, $this->sposx, $this->sposy); } } public function efbadi(): void { $this->Gotoxy(3, 15); $this->efbadass(); } public function scrollbadi(): void { $this->Gotoxy(3, 15); $this->scrollbadass(); } /** CLRBADI: clears the message line if it is used */ public function clrbadi(): void { if ($this->flagbadi) { $this->efbadi(); $this->flagbadi = 0; } } /** SCEVEU: the message of a phase of the scenario, in the 3D area (6: won -> Final) */ public function sceveu(int $quoi): \Generator { $scenme = $this->GetScenme(); $this->dervue = self::VUE_AUCUNE; $this->effvirt(0x007); $this->Gotoxy(12, 1); $p = (string)($scenme[$quoi - 1] ?? ''); $n = strlen($p); $k = 0; $car = $k < $n ? ord($p[$k++]) : 0; while ($car) { $this->car2($car); do { $car = $k < $n ? ord($p[$k++]) : 0; if ($car === 13) { $this->Gotoxy(12, $this->linaff + 1); } } while ($car === 13); } if ($quoi === 6) { for ($i = 0; $i < 3; $i++) { $this->bruit(6); // bruit(6) waited 30 ticks in SAPWMUS.C yield from $this->delai(30); } } $this->TfrEcr(null); if ($quoi === 6) { // you won $this->CopyOffscreen($this->debecr, $this->Ecran1, [32 * 2, 1 * 2, (32 + 255) * 2, (1 + 100) * 2], [0, 0, 255 * 2, 100 * 2]); yield from $this->delai(15 * 60); yield from $this->Final(); } } /** SceveuBulle: a message of the scenario in a bubble (index 0-based) */ public function SceveuBulle(int $quoi): void { $scenme = $this->GetScenme(); $this->effaceBulles(); $this->bulle((string)($scenme[$quoi] ?? ''), 1); } /** GAINSBOUR: physical decay */ public function gainsbour(): void { $this->bless = ($this->bless - (6 - intdiv(6 * $this->resistance, 256))) & 0xFF; if ($this->bless > 251) { if ($this->mort) { $this->mort--; $this->bless = 20; } else { $this->presence = 0; $this->fdialo = 0; } } } /** PASTMP: time passes (the meat rots) */ public function pastmp(): void { $i = $this->liste1->taille; while ($i) { $i--; if ($this->liste1->elements[$i] >= 37 && $this->liste1->elements[$i] < 41) { $this->liste1->elements[$i]++; } } } /** TFRENE: keeps the anger of the tribe against you */ public function tfrene(): void { if ($this->presence < 5 && $this->presence) { if ($this->fpress) { $this->enetoi[$this->tribu - 1] = $this->enerv; if ($this->chef || $this->tribu !== 1) { $this->tagress[$this->tribu - 1] = $this->agress; } } } } /** COMMENT: a comment (mescon, 1-based) on the message line */ public function comment(int $rang): void { $mescon = $this->GetMesCon(); if (!$this->flefba) { $this->flefba++; $this->scrollbadi(); } $this->flagbadi = 1; $p = (string)($mescon[$rang - 1] ?? ''); $n = strlen($p); for ($i = 0; $i < $n; $i++) { $this->affcarmess(ord($p[$i])); } $this->TfrBadAss(); } public function effaceBulles(): void { $this->tfrvirt(); if (!self::EmptyRect($this->relBulleJoueur)) { $this->CopyOffscreen($this->debecr, $this->mainWindow, $this->relBulleJoueur, $this->relBulleJoueur); } $this->relBulleJoueur = [0, 0, 0, 0]; if (!self::EmptyRect($this->relBulleAdversaire)) { $this->CopyOffscreen($this->debecr, $this->mainWindow, $this->relBulleAdversaire, $this->relBulleAdversaire); } $this->relBulleAdversaire = [0, 0, 0, 0]; } /** BULLE: a speech bubble with a text; $qui 0 = the player, 1 = the opponent */ public function bulle(string $debut, int $qui): void { $xDroite = 640 - 64 - 16; $xGauche = 80; $yBulle = 58; $queue = 20; $gbak = $this->curPort; $this->curPort = $this->debecr; [$tx, $ty] = $this->relTailleChaine($debut); if ($tx < 64) { $tx = 64; } $gauche = ($qui ^ $this->sap1->sens) !== 0; if ($gauche) { $r = [$xGauche, $yBulle - ($ty + 2) * 12, $xGauche + $tx + 16, $yBulle]; } else { $r = [$xDroite - $tx - 16, $yBulle - ($ty + 2) * 12, $xDroite, $yBulle]; } $r[3] += $queue; $rbase = $r; if ($qui === 0) { $this->effaceBulles(); $this->relBulleJoueur = $r; } else { if (!self::EmptyRect($this->relBulleAdversaire)) { $b = $this->relBulleAdversaire; $r = self::OffsetRect($r, 0, $b[3] - $b[1] - $queue - 4); $this->relBulleAdversaire = [min($b[0], $r[0]), min($b[1], $r[1]), max($b[2], $r[2]), max($b[3], $r[3])]; $rbase = $this->relBulleAdversaire; } else { $this->relBulleAdversaire = $r; } } $r[3] -= $queue; // the region: a round rectangle (16x16 corners) and its tail $x = $gauche ? $r[0] + 48 : $r[2] - 48; $y = $r[3]; $tail = $gauche ? [[$x, $y], [$x - $queue, $y + $queue], [$x - 8, $y]] : [[$x, $y], [$x + $queue, $y + $queue], [$x + 8, $y]]; $this->relPaintBubble($this->debecr, $r, $tail); $this->qdClip = $r; $x = $r[0] + 8; $y = $r[1] + 6; $this->GotoxyPixel($x, $y); $n = strlen($debut); $k = 0; while ($k < $n && $debut[$k] === ' ') { $k++; } while ($k < $n) { if ($debut[$k] === "\r") { $y += 14; $this->GotoxyPixel($x, $y); do { $k++; } while ($k < $n && $debut[$k] === ' '); } else { $this->affcar10(ord($debut[$k++])); } } $this->qdClip = null; $this->curPort = $gbak; $this->TfrEcr($rbase); $this->DoPenSize(1, 1); } /** * EraseRgn + FrameRgn of the bubble: white inside, a black outline of one pixel. * @param array $tail triangle */ private function relPaintBubble(Port $port, array $r, array $tail): void { $l = min($r[0], $tail[0][0], $tail[1][0], $tail[2][0]); $rr = max($r[2], $tail[0][0], $tail[1][0], $tail[2][0]) + 1; $t = $r[1]; $b = max($r[3], $tail[1][1]) + 1; $inside = function (int $px, int $py) use ($r, $tail): bool { if ($px >= $r[0] && $px < $r[2] && $py >= $r[1] && $py < $r[3]) { // corners: ovals of 16x16 $cx = $px < $r[0] + 8 ? $r[0] + 8 : ($px >= $r[2] - 8 ? $r[2] - 8 : $px); $cy = $py < $r[1] + 8 ? $r[1] + 8 : ($py >= $r[3] - 8 ? $r[3] - 8 : $py); $dx = $px + 0.5 - $cx; $dy = $py + 0.5 - $cy; if (($px < $r[0] + 8 || $px >= $r[2] - 8) && ($py < $r[1] + 8 || $py >= $r[3] - 8)) { return $dx * $dx + $dy * $dy <= 64; } return true; } // the tail (triangle) [$a, $bb, $c] = $tail; $s1 = ($bb[0] - $a[0]) * ($py - $a[1]) - ($bb[1] - $a[1]) * ($px - $a[0]); $s2 = ($c[0] - $bb[0]) * ($py - $bb[1]) - ($c[1] - $bb[1]) * ($px - $bb[0]); $s3 = ($a[0] - $c[0]) * ($py - $c[1]) - ($a[1] - $c[1]) * ($px - $c[0]); return ($s1 >= 0 && $s2 >= 0 && $s3 >= 0) || ($s1 <= 0 && $s2 <= 0 && $s3 <= 0); }; $in = []; for ($py = $t - 1; $py <= $b; $py++) { for ($px = $l - 1; $px <= $rr; $px++) { $in[$py][$px] = $inside($px, $py); } } $white = chr(Mac::WHITE); $black = chr(Mac::BLACK); for ($py = max(0, $t); $py < min($port->h, $b); $py++) { for ($px = max(0, $l); $px < min($port->w, $rr); $px++) { if (!$in[$py][$px]) { continue; } $edge = !$in[$py - 1][$px] || !$in[$py + 1][$px] || !$in[$py][$px - 1] || !$in[$py][$px + 1]; $port->rows[$py][$px] = $edge ? $black : $white; } } $port->dirty = true; } /** * CHRONO: the clock. heure 0..1023 (morning 0-90, afternoon 426-512, night 512-602, end of * night 938-1023). quoi 0: normal, 1: no decrease nor message, 2: decrease but no message. */ public function chrono(int $quoi): void { if ($this->minute) { $this->minute--; return; } $this->minute = 5; $this->heure = ($this->heure + 1) & 0xFFFF; if (($this->heure & 255) === 90) { $this->heure += 336; } $this->heure &= 0x03FF; if (!($this->heure & 0xFF)) { $this->pastmp(); } while ($this->heure >= $this->tabheur[$this->pos_evt]['h']) { if ($this->tabheur[$this->pos_evt]['q']) { // the gradient of the sky with its 8 ST colours $this->ciel_degr = $this->tabheur[$this->pos_evt]['pal']; $this->FixeDegradesFonctionHeure($this->tabheur[$this->pos_evt]); } else { $this->palhaute = $this->tabheur[$this->pos_evt]['pal']; } if (++$this->pos_evt >= self::NBREVTHEURE) { $this->pos_evt = 0; $this->heure = 0; } } if ($this->heure === 0) { $this->flefba = 0; $this->comment(114); $this->jour_act = ($this->jour_act + 1) & 0xFFFF; } // position of the sun / moon if ($this->heure & 0x100) { $this->metpos = (($this->heure & 0xFF) - 180) & 0xFFFF; $this->angsol = 256; } else { $this->metpos = (75 - ($this->heure & 0xFF)) & 0xFFFF; $this->angsol = 768; } if ($quoi === 1) { return; } $this->finjou = 1; $this->faim = ($this->faim - 2) & 0xFF; $avemess = 0; if ($this->faim > 253) { $this->faim = 5; $this->gainsbour(); $avemess = 93; } $this->soif = ($this->soif - 4) & 0xFF; if ($this->soif > 251) { $this->soif = 5; $this->gainsbour(); $avemess = 94; } $this->fatig = ($this->fatig - 2) & 0xFF; if ($this->fatig > 253) { $this->fatig = 5; $this->gainsbour(); $avemess = 95; } if ($avemess && !$quoi) { $this->efbadi(); $this->comment($avemess); } } /** CAUSE: one of the 4 sentences of a context (1..), in a bubble, if one is talking */ private function cause(int $context, ?string $messAdd): void { $mescon = $this->GetMesCon(); if ($this->fdialo) { $rang = ($context - 1) * 4 + ($this->Rnd() & 3); $chaine = (string)($mescon[$rang] ?? ''); if ($messAdd !== null) { $chaine .= $messAdd; } $this->bulle($chaine, 1); } } /** FUITE: he runs away */ public function fuite(): \Generator { $this->flefba = 0; if ($this->agress < 245) { $this->agress += 10; } $this->comment(80); yield from $this->quitte(); } /** INTRSQ: value of an object (doubled if it is the favourite one) */ public function intrsq(int $objet): int { $refer1 = $this->refer[self::REFER1]; $valeur = ($refer1[$objet - 1][1] ?? 0) & 0xF; if ($objet === $this->predil) { $valeur = ($valeur * 2) & 0xFF; } return $valeur; } /** RAPIAS: the most interesting object you carry */ public function rapias(): int { $max = 0; $objet = 0; for ($i = 0; $i < $this->liste1->taille; $i++) { $valeur = $this->intrsq($this->liste1->elements[$i]); if ($valeur > $max) { $max = $valeur; $objet = $this->liste1->elements[$i]; } } return $objet; } /** EXCITE: the anger increases */ public function excite(int $valeur): void { $valeur &= 0xFFFF; $tfr = ($this->enerv + $valeur) & 0xFFFF; if ($this->enerv <= $tfr) { $this->enerv = $tfr; } else { $this->enerv = 65535; } if ($this->enerv < 51 * 256) { if ($this->fpress) { $this->cause(7, null); } else { $this->cause(2, null); } return; } if ($this->enerv < 102 * 256) { $this->cause(8, null); return; } if ($this->Rnd() < intdiv($this->enerv, 512)) { $this->inimen(0); $this->flcomb = 1; return; } if ($this->enerv < 153 * 256) { $this->cause(9, null); return; } if ($this->enerv < 204 * 256) { $this->cause(10, null); return; } $this->cause(11, null); } /** MODREL: another action than SAY / GIVE while someone is there: he gets angry */ public function modrel(): void { if ($this->tlpos[0] === 5 || $this->tlpos[0] === 6 || !$this->fdialo) { return; } $tfr = ($this->enerv + 100) & 0xFFFF; if ($this->enerv < $tfr) { $this->enerv = $tfr; } if ($this->enerv < 51 * 256) { if ($this->fpress) { $this->cause(7, null); } else { $this->cause(2, null); } return; } if ($this->enerv < 102 * 256) { $this->cause(8, null); } if ($this->Rnd() > intdiv($this->enerv, 512)) { if ($this->enerv < 153 * 256) { $this->cause(9, null); } elseif ($this->enerv < 204 * 256) { $this->cause(10, null); } else { $this->cause(11, null); } } } /** PRENDS: he takes the object rega of your list */ public function prends(): void { $erreur = $this->enlevl($this->liste1, $this->list10, $this->rega); if ($erreur === 1 && $this->lpos > 1) { $this->lpos--; } $this->inimen($this->s_menu); } /** TRDONN: give the object $rangb */ public function trdonn(int $rangb): \Generator { $mescon = $this->GetMesCon(); if (!$this->fdialo) { return; } $objet = $this->liste1->elements[$rangb]; $this->flefba = 0; if ($this->chef && $this->tribu === 1) { // to your chief if ($this->scenar !== 1) { if ($objet !== $this->scenar + 40) { $this->SceveuBulle($this->scenar - 1); return; } } else { // meat if ($objet < 35 || $objet > 39) { $this->SceveuBulle($this->scenar - 1); return; } $this->scenpr = self::sc($this->scenpr - (40 - $objet)); if ($this->scenpr > 0) { $this->prends(); $this->list10->taille = 0; $this->effaceBulles(); $this->cause(6, null); return; } } // (IsEnreg() is always true: the game is free) $this->prends(); $this->effaceBulles(); switch ($this->scenar) { case 1: $this->bulle((string)$mescon[82 - 1], 1); break; case 2: $this->bulle((string)$mescon[83 - 1], 1); break; case 3: case 4: $this->bulle((string)$mescon[84 - 1], 1); break; } $this->scenar = ($this->scenar + 1) & 0xFF; if ($this->scenar === 5) { // the last fight $this->flcomb = 1; $this->SceveuBulle($this->scenar - 1); yield from $this->delai(5 * 60); return; } if ($this->scenar === 3) { // the Bras-de-Roc are very angry: you must steal the axe $this->enetoi[2] = 60000; } return; } // a gift out of the scenario $this->effaceBulles(); $valeur = ($this->enerv - 512 * $this->intrsq($this->liste1->elements[$this->rega])) & 0xFFFF; if ($valeur > $this->enerv) { $valeur = 0; } if (!$this->fpress) { // not introduced: a present if ($this->list10->taille <= $this->list10->maxtaille) { $this->prends(); if ($this->enerv < 102 * 256) { $this->cause(1, null); $this->cause(12, null); } if ($this->agress > 0) { $this->agress--; } $this->enerv = $valeur; $this->excite(0); } return; } // exchange if ($this->enerv < 102 * 256) { $valeur = $this->intrsq($this->liste1->elements[$this->rega]); if ($this->chef && $this->scenar === $this->tribu) { $valeur = $this->liste1->elements[$this->rega] !== $this->predil ? 0 : 200; } if ($valeur > $this->vlchan) { $this->vlchan = $valeur & 0xFF; $this->cause(4, null); if ($this->echan === 1) { // protection $this->enetoi[$this->tribu - 1] = 0; $this->echan = 25; } else { $tfr = $this->liste1->elements[$rangb]; $this->liste1->elements[$rangb] = $this->echan; $this->echan = $tfr; $this->inimen($this->s_menu); } } else { $this->cause(3, null); $this->excite(50); } } else { if ($this->list10->taille < $this->list10->maxtaille) { $this->prends(); $this->enerv = $valeur; $this->cause(5, null); } } } /** IDIALO: the opponent, from the presence */ public function idialo(): void { $this->fdialo = $this->flcomb = $this->fpress = $this->flgcharism = $this->dquest = 0; if ($this->presence === 0) { return; } if ($this->presence > 4) { // an animal $this->echan = 0; $this->flcomb++; return; } $this->fdialo++; $t = $this->tribu - 1; $this->enerv = $this->tenerv[$t]; $this->agress = $this->tagress[$t]; $this->predil = $this->tabprf[$t]; $this->echan = $this->tobje[$t][$this->Rnd() & 3]; for ($i = 0; $i < 2; $i++) { $this->list10->elements[$i] = $this->tobje[$t][$this->Rnd() & 3]; } $this->list10->taille = 2; $tribuCar = chr(($this->tribu + 0x85) & 0xFF); if ($this->chef) { $this->nomtribu = chr(0x85) . $tribuCar; $this->nomadversaire = chr(($this->tribu + 0x89) & 0xFF); if ($this->tribu === 1) { $this->fpress = 1; $this->enerv = $this->enetoi[$t]; } if ($this->scenar === $this->tribu && $this->tribu !== 4) { $this->echan = $this->tribu + 40; } else { $this->echan = 1; } } else { $this->nomtribu = ' ' . $tribuCar; $this->nomadversaire = $this->relNom(); } $this->nomsecondaire = $this->relNom(); $this->vlchan = $this->intrsq($this->echan); $this->smenu[1] = 111; $this->inimen(0); } /** A name of 3 syllables of 3 letters (maced) */ private function relNom(): string { $nom = ''; for ($i = 0; $i < 6;) { $pos = intdiv(48 * $this->Rnd(), 256); $nom .= substr($this->maced, $pos, 3); $i += 3; } return $nom; } /** QUITTE: he is frightened: he runs away */ public function quitte(): \Generator { $this->tfrene(); $this->flcomb = 0; $this->presence = 0; $this->idialo(); $this->present[1] = 0; $this->present[3] = 0; $this->prm($this->sap2, 0); $this->prm($this->sap4, 0); $this->dsposx = $this->sposx; $this->dsposy = $this->sposy; $this->reset_mouse(); if ($this->dervue === self::VUE_LATERALE) { yield from $this->laterb(false); } else { yield from $this->latera(); } $this->generique(true); $this->inimen(0); } /** CREECOP: makes a friend (sap2 becomes sap3) */ public function creecop(int $qui): \Generator { $this->sap3->cross = $this->sap2->cross; if ($this->presence < 5) { $this->nomcopain = $this->nomadversaire; } else { // your friend $this->nomcopain = chr(128 + 45); } $this->prm($this->sap3, $qui); $this->sap3->colonne = $this->sap2->colonne; $this->sap3->sens = $this->sap2->sens; $this->sap3->coeur = $this->sap2->coeur; $this->sap3->cptrsouffle = 1; $this->prm($this->sap2, 0); if ($this->dervue !== self::VUE_PAYSAGE) { $this->presence = 0; yield from $this->trobsv(1); } $this->echan = 0; $this->peuple[2]->possession->taille = 0; $this->generique(true); } /** BONNECHASS: "good hunting" */ public function bonnechass(): \Generator { $mescon = $this->GetMesCon(); if ($this->enerv > 102 * 256) { $this->excite(300); } else { if (!$this->fpress) { $this->cause(2, null); } else { $this->bulle((string)$mescon[73 - 1], 1); yield from $this->quitte(); $this->inimen(0); $this->effaceBulles(); } } } /** VIENSAVEC: "come with me" */ public function viensavec(): \Generator { $mescon = $this->GetMesCon(); if ($this->fpress && $this->presence && $this->presence < 5 && $this->present[2] === 0 && $this->present[3] === 0 && !$this->chef && !$this->flgcharism) { if (($this->charisme * $this->Rnd() > $this->enerv) && $this->Rnd() < 70) { yield from $this->creecop(1); $this->viecopain = 3; $this->sap3->vie = 200; $this->bulle((string)$mescon[100 - 1], 1); $this->flagbadi = 0; } else { $this->excite(1500); } } else { $this->bulle((string)$mescon[99 - 1], 1); $this->excite(2000); } $this->flgcharism = 1; } /** TUVEUQUOI: "what do you want?" */ public function tuveuquoi(): void { $refer1 = $this->refer[self::REFER1]; $mescon = $this->GetMesCon(); $scenme = $this->GetScenme(); if ($this->enerv > 102 * 256) { $this->cause(13, null); return; } if ($this->chef && $this->tribu === 1) { $this->dquest = 0; $this->bulle((string)($scenme[$this->scenar - 1] ?? ''), 1); return; } if ($this->tribu === $this->scenar && $this->chef) { $this->cause(14, $refer1[$this->predil - 1][0] ?? ''); return; } $valeur = $this->intrsq($this->rapias()); if ($valeur > $this->vlchan) { $this->cause(14, $refer1[$this->rapias() - 1][0] ?? ''); } elseif ($this->chef) { $this->cause(14, $refer1[$this->predil - 1][0] ?? ''); } else { $this->bulle((string)$mescon[66 - 1], 1); $this->excite(0); } } /** TRDIRE: say the sentence $diquoi of the list of sentences */ public function trdire(int $diquoi): \Generator { $refer1 = $this->refer[self::REFER1]; $mescon = $this->GetMesCon(); if (!$this->fdialo) { return; } $this->flefba = 1; if ($diquoi === $this->dquest) { $this->excite(1000); return; } $this->dquest = $diquoi; if ($diquoi === 5) { // I am ... if (!$this->fpress) { $this->fpress++; $tfr = self::ss($this->enerv - $this->tenerv[$this->tribu - 1]); $this->enerv = $this->enetoi[$this->tribu - 1]; $enerv2 = ($this->enerv + $tfr) & 0xFFFF; if ($tfr > 0 && $enerv2 < $this->enerv) { $enerv2 = 65535; } if ($tfr < 0 && $enerv2 > $this->enerv) { $enerv2 = 0; } $this->enerv = $enerv2; if ($this->enerv > 0x3400) { // so it is you $this->bulle((string)$mescon[72 - 1], 1); $this->excite(0); } else { $this->bulle((string)$mescon[71 - 1], 1); } } else { $this->excite(600); } return; } if ($diquoi === 6) { yield from $this->bonnechass(); return; } if ($diquoi === 7 || $diquoi === 8) { yield from $this->viensavec(); return; } if ($this->stock1 === 0) { if ($diquoi === 1) { // who are you? if ($this->enerv > 102 * 256) { $this->excite(100); } else { $this->cause(12, $this->GetStrBkMot(129 + 3)); if (!$this->fpress && !$this->chef) { $this->bulle((string)$mescon[65 - 1], 1); $this->excite(200); } } return; } if ($diquoi === 2) { $this->tuveuquoi(); return; } if ($diquoi === 3) { // what do you offer? if ($this->enerv > 256 * 102) { $this->bulle((string)$mescon[66 - 1], 1); $tfr = self::ss($this->enerv + 200); if ($this->enerv < $tfr) { $this->enerv = $tfr & 0xFFFF; } } else { if (!$this->fpress) { $this->cause(2, null); } else { $this->cause(15, $refer1[$this->echan - 1][0] ?? ''); } } return; } // where is your chief? if ($this->chef) { $this->excite(32700); } else { if ($this->enerv > 51 * 256) { $this->cause(16, null); $tfr = self::ss($this->enerv + 256); if ($this->enerv < $tfr) { $this->enerv = $tfr & 0xFFFF; } } else { if (!$this->fpress) { $this->cause(2, null); } else { $p = $this->poschef; $p[0] = ' '; $p[2] = ' '; $d = $this->destine[$this->tribu - 1]; if (intdiv($this->xj, 256) < $d[1]) { $p[0] = chr(128 + 31); } if (intdiv($this->xj, 256) > $d[1]) { $p[0] = chr(128 + 32); } if (intdiv($this->yj, 256) > $d[2]) { $p[2] = chr(128 + 33); } if (intdiv($this->yj, 256) < $d[2]) { $p[2] = chr(128 + 34); } $this->poschef = $p; $this->bulle($p, 1); } } } } else { // flattery or insult switch ($this->stock1) { case 1: // insult $this->excite(200 * $this->stock2); break; case 2: // flattery if ($this->agress < 200) { $this->agress++; $tfr = self::ss($this->enerv - $this->stock2 * 100); if ($tfr < $this->enerv) { $this->enerv = $tfr & 0xFFFF; } $this->excite(0); break; } // no break (as in the C code) default: // boasting if ($this->agress < 200 && !$this->chef) { $this->agress++; if ($this->Rnd() > intdiv($this->enerv, 200)) { yield from $this->fuite(); } else { $tfr = self::ss($this->enerv - $this->stock2 * 100); if ($tfr < $this->enerv) { $this->enerv = $tfr & 0xFFFF; } $this->excite(0); } } else { // he takes it as an insult $this->excite(200 * $this->stock2); } break; } } } /** VOLE: you lost: he takes everything */ public function vole(): void { $this->liste4->taille = 0; $i = $this->liste1->taille; while ($i) { $i--; if ($this->intrsq($this->liste1->elements[$i]) > 5 && $this->liste1->elements[$i] < 40 && $this->Rnd() > 128) { $this->enlevl($this->liste1, $this->listbidon, $i); } } $this->tlpos[1] = 1; $this->tlpos[4] = 1; } /** OUTARM: the weapons after a fight */ public function outarm(): void { $this->tlpos[1] = 1; $this->tlpos[4] = 1; $i = $this->liste1->taille; // the spears thrown are lost while ($i) { $i--; if ($this->liste1->elements[$i] === 23) { if ($this->sap1->nbresag) { $this->sap1->nbresag--; } else { $this->enlevl($this->liste1, $this->listbidon, $i); } } } if ($this->sap1->forcehache === 0) { // no more axe $i = $this->liste1->taille; while ($i) { $i--; if ($this->liste1->elements[$i] === 21 || $this->liste1->elements[$i] === 22) { $this->enlevl($this->liste1, $this->listbidon, $i); $i = 0; } } } if ($this->sap1->vie !== $this->entvie) { $tfr = ($this->fatig - intdiv($this->entvie - $this->sap1->vie, 2)) & 0xFF; if ($tfr < $this->fatig) { $this->fatig = $tfr; } else { $this->fatig = 0; } } } /** TRDORS: sleep */ public function trdors(): \Generator { if ($this->fatig > 192) { $this->flefba = 0; $this->comment(108); return; } $this->liste4->taille = 0; $this->tlpos[4] = 1; $tfr = ($this->fatig + $this->stock2) & 0xFF; if ($tfr < $this->fatig) { $tfr = 255; } $this->fatig = 255; $this->dervue = self::VUE_PAYSAGE; $this->stock2 = intdiv($this->stock2, 7); while ($this->stock2) { $this->GereInterface(); for ($i = 0; $i < 9; $i++) { $this->minute = 0; $this->chrono(1); } $t = $this->TickCount(); while ($this->TickCount() < $t + 10) { yield; } $this->affich($this->dzeta); $this->tfrima(); $this->minute = 0; $this->chrono(2); $this->stock2--; } $this->fatig = $tfr; $this->chrono(0); $this->afstat(); $this->inimen(0); } private function InitVueLaterale(): \Generator { if ($this->dervue !== self::VUE_LATERALE) { $this->ldzeta = $this->dzeta; } $this->prm($this->sap2, $this->present[1]); $this->prm($this->sap4, $this->present[3]); $this->flcomb = 1; if ($this->presence) { if ($this->presence < 5) { $this->flcomb = 0; } // bit 7: friendly animal $this->presence &= 127; $this->prm($this->sap1, $this->present[0]); $this->prm($this->sap3, $this->present[2]); if ($this->dervue !== self::VUE_LATERALE) { yield from $this->latera(); } $this->dervue = self::VUE_LATERALE; } $this->pos0_mouse(); } /** TROBSV: observe: 0 landscape (3D), 1 side view, 2 panorama, 3 map */ public function trobsv(int $regb): \Generator { if ($this->presence) { return; } if ($regb === 0) { $this->LastObsv = 0; $this->dismin = 0; $this->lastmen = 255; $this->clrbadi(); $this->dervue = self::VUE_PAYSAGE; $this->MenuFenetres(0); $this->AffichePictAide(1); yield from $this->clav3d(); $this->MenuFenetres(1); $this->pos0_mouse(); if ($this->presence) { $regb = 1; } } if ($regb === 1) { $this->LastObsv = 1; yield from $this->InitVueLaterale(); return; } if ($regb === 2) { // panorama while ($this->getkey(0)) { yield; } $this->dervue = self::VUE_PAYSAGE; $dzetdep = $this->dzeta; do { $posTimer = $this->sapiensTimer; $this->affich($this->dzeta); $this->tfrima(); $this->dzeta = ($this->dzeta + 16) & 0x3FF; do { yield; } while ($posTimer > $this->sapiensTimer - 8); } while ($dzetdep !== $this->dzeta && !$this->getkey(0)); } if ($regb === 3) { $this->dervue = self::VUE_PAYSAGE; yield from $this->zoocar(); $this->lastmen = 255; } $this->inimen(0); $this->pos0_mouse(); $this->efbadass(); $this->TfrBadAss(); } /** ZONEVT: an event from the position (0: none, else the presence 1..8) */ public function zonevt(int $okmess): int { for ($i = 0; $i < self::NBREVTDESTINE; $i++) { $dieu = $this->destine[$i]; $dist = $this->distance($dieu[1] * 256 - $this->xj, $dieu[2] * 256 - $this->yj); $dist = intdiv($dist, 256); if ($dist > $dieu[3]) { // out of the zone: it is valid again $this->destine[$i][0] &= 127; } else { if (!($dieu[0] & 128) && ($dist * 12 + 10) < $this->Rnd()) { if ($this->Rnd() >= $dieu[4]) { if ($this->Rnd() > 240) { if ($okmess) { $this->efbadi(); $this->comment($dieu[6]); return 0; } } } else { if ($dieu[5] === 0) { // water $this->source = ($this->source + 1) & 0xFF; $this->liste4->taille = $this->tlpos[4] = 1; $this->liste4->elements[0] = 2; $this->efbadi(); $this->comment(76); return 0; } $this->chef = $dieu[5] > 127 ? 1 : 0; $qui = $dieu[5] & 127; // one shot if ($dieu[0]) { $this->destine[$i][0] |= 128; } $nuit = $this->heure > 512; if ($this->chef || ($nuit && $qui !== 5 && $this->Rnd() > 222) || (!$nuit && $qui === 5 && $this->Rnd() > 222) || ($nuit && $qui === 5) || (!$nuit && $qui !== 5)) { return $qui; } } } } } return 0; } /** ZONINF: the events of the position (sets the presences) */ public function zoninf(): \Generator { if ($this->presence) { return; } if ($this->derxj === ($this->xj & 0xFFE0) && $this->deryj === ($this->yj & 0xFFE0)) { return; } $this->derxj = $this->xj & 0xFFE0; $this->deryj = $this->yj & 0xFFE0; $this->chef = 0; $this->flcomb = 0; $this->source = 0; $this->present[1] = $this->zonevt(1); if ($this->present[1]) { // a chief comes with another one if (!$this->chef) { $this->present[3] = $this->zonevt(0); } else { $this->present[3] = $this->present[1]; } // a man first if ($this->present[1] > 4 && $this->present[3] < 5 && $this->present[3] !== 0) { $tfr = $this->present[1]; $this->present[1] = $this->present[3]; $this->present[3] = $tfr; } $this->presence = $this->present[1]; $this->tribu = $this->presence; if ($this->dervue !== self::VUE_LATERALE) { $this->sap1->colonne = 3; } $this->prm($this->sap2, $this->present[1]); $this->prm($this->sap4, $this->present[3]); $this->idialo(); // the charisma on an animal if ($this->presence > 4 && $this->charisme > $this->Rnd() && $this->Rnd() < 40 && !$this->present[2]) { $this->flefba = 0; $this->comment(97 + $this->presence); yield from $this->creecop($this->presence); $this->viecopain = 1; $this->flefba = 0; if ($this->dervue === self::VUE_LATERALE) { $this->flagbadi = 0; } $this->presence = 128; return; } } if (!$this->source) { if ($this->scenar === 4 && !$this->scenpr) { // the sacred stick of the Hyenes-Folles, under the three totems if (abs($this->derxj - 0xABE0) < 500 && abs($this->deryj - 0xB2E0) < 500) { $this->liste4->taille = $this->tlpos[4] = 1; $this->liste4->elements[0] = 44; return; } } $this->liste4->taille = $this->Rnd() & 7; $i = $this->liste4->taille; $this->tlpos[4] = 1; while ($i) { $this->liste4->elements[--$i] = 3 + intdiv(17 * $this->Rnd(), 256); } } } }