MOKTAR

par davidnurdin · Remakes
pas encore noté
▶ Jouer Sources Éditer GitHub
. / src / Titus / ObjectsTrait.php · commit 2fb9809
<?phpnamespace App\Titus;/** * objects.c (OpenTitus): handle objects. * * loadobjects() / freeobjects() are not ported: Game::initoriginal() builds the ObjectData table and * Level->objectdata holds it. */trait ObjectsTrait{    /** original.h */    public const ORIG_OBJECT_COUNT = 71;    public function move_objects(Level $level): void    {        if ($this->GRAVITY_FLAG == 0) return; //Skip execution if there are no active objects        /** @var TitusObject|null $off_object */        $off_object = null;        // uint8 i, hflag, fflag, max_speed; int16 tileX, tileY, speed, j; bool obj_vs_sprite; int8 reduction, tile_count;        for ($i = 0; $i < $level->objectcount; $i++) {            $obj_vs_sprite = false;            if (!($level->object[$i]->sprite->enabled)) continue; //Skip unused objects            if ($level->object[$i]->sprite->x <= 8) { //Left edge of level                $level->object[$i]->sprite->speedX = 2 * 16;                $level->object[$i]->sprite->speedY = 0;            }            if ($level->object[$i]->sprite->x >= $level->width * 16 - 8) { //Right edge of level                $level->object[$i]->sprite->speedX = -2 * 16;                $level->object[$i]->sprite->speedY = 0;            }            //Handle carpet            if (($level->object[$i]->sprite->number == self::FIRST_OBJET + 21) || ($level->object[$i]->sprite->number == self::FIRST_OBJET + 22)) { //Flying carpet, flying                $this->GRAVITY_FLAG = 4; //Keep doing gravity                //(Adjust height after player)                if ($this->TAPISWAIT_FLAG != 0) { //Flying ready                    $level->object[$i]->mass = 0;                    if ($level->object[$i]->sprite->y == ($level->player->sprite->y - 8))                        $level->object[$i]->sprite->speedY = 0;                    else if ($level->object[$i]->sprite->y < ($level->player->sprite->y - 8))                        $level->object[$i]->sprite->speedY = 16;                    else                        $level->object[$i]->sprite->speedY = -16;                }                if ($this->TAPISFLY_FLAG == 0) { //Time's up! Stop flying                    $this->updateobjectsprite($level, $level->object[$i], self::FIRST_OBJET + 19, true); //position falling and carry  (jump and carry sprite?)                    $level->object[$i]->sprite->speedX = 0;                    $this->TAPISWAIT_FLAG = 2; //Timed out                }            } else if ((($level->object[$i]->sprite->number == self::FIRST_OBJET + 19) || ($level->object[$i]->sprite->number == self::FIRST_OBJET + 20)) && //Flying carpet, not flying              (($this->IMAGE_COUNTER & 0x03) == 0) && ($level->object[$i]->sprite->speedY > 0) && ($this->TAPISWAIT_FLAG != 2)) {                //The carpet is being throwed                if ($level->object[$i]->sprite->number == self::FIRST_OBJET + 19) { //Carpet is closed                    $level->object[$i]->sprite->speedX = $level->object[$i]->sprite->speedX >> 1;                    $this->updateobjectsprite($level, $level->object[$i], self::FIRST_OBJET + 20, false); //Open, not flying                } else { //Open, not flying (sprite FIRST_OBJET + 20)                    $level->object[$i]->sprite->speedX = 0;                    $this->updateobjectsprite($level, $level->object[$i], self::FIRST_OBJET + 21, false); //Flying ready                }                $this->TAPISWAIT_FLAG = 1;                $this->TAPISFLY_FLAG = 200;            }            //Does it move in X?            if ($level->object[$i]->sprite->speedX != 0) {                //Test for horizontal collision                $tileX = $level->object[$i]->sprite->x >> 4;                $tileY = $level->object[$i]->sprite->y >> 4;                if (($level->object[$i]->sprite->y & 0x000F) == 0) {                    $tileY--;                }                $hflag = $this->get_horizflag($level, $tileY, $tileX);                if (($hflag == self::HFLAG_WALL) || ($hflag == self::HFLAG_DEADLY) || ($hflag == self::HFLAG_PADLOCK)) {                    //Collision horizontal, change direction                    $level->object[$i]->sprite->speedX = 0 - $level->object[$i]->sprite->speedX;                    $level->object[$i]->sprite->x += $level->object[$i]->sprite->speedX >> 4;                } else if (((($level->object[$i]->sprite->speedX >> 4) + $level->object[$i]->sprite->x) >> 4) != $tileX) { //If speedX is big enough, check the neighbour tile                    if ($level->object[$i]->sprite->speedX < 0)                        $tileX--;                    else                        $tileX++;                    if (($tileX < $level->width) && ($tileX >= 0)) {                        $hflag = $this->get_horizflag($level, $tileY, $tileX);                        if (($hflag == self::HFLAG_WALL) || ($hflag == self::HFLAG_DEADLY) || ($hflag == self::HFLAG_PADLOCK)) {                            //Collision horizontal, change direction                            $level->object[$i]->sprite->speedX = 0 - $level->object[$i]->sprite->speedX;                            $level->object[$i]->sprite->x += $level->object[$i]->sprite->speedX >> 4;                        }                    }                }                //Move the object in X and reduce speedX                $this->GRAVITY_FLAG = 4;                $level->object[$i]->sprite->x += ($level->object[$i]->sprite->speedX >> 4); //Move the object                if (abs($level->object[$i]->sprite->speedY) >= 16) {                    $reduction = 1;                } else {                    $reduction = 3;                }                if ($level->object[$i]->sprite->speedX < 0) {                    $reduction = 0 - $reduction;                }                $level->object[$i]->sprite->speedX -= $reduction;                if (abs($level->object[$i]->sprite->speedX) < 16) {                    $level->object[$i]->sprite->speedX = 0;                }            }            if ($level->object[$i]->sprite->speedY < 0) {                //Object's going up!                $tileX = $level->object[$i]->sprite->x >> 4;                $tileY = ($level->object[$i]->sprite->y >> 4) - ($level->object[$i]->sprite->spritedata->collheight >> 4) - 1; //tile above the object                if ($this->get_ceilflag($level, $tileY, $tileX) != self::CFLAG_NOCEILING) {                    //Hit, stop elevating                    $level->object[$i]->sprite->speedY = 0;                    if (!($level->object[$i]->objectdata->bounce)) {                        $level->object[$i]->sprite->y = self::s16($level->object[$i]->sprite->y & 0xFFF0);                    }                    continue;                } else {                    if ((($level->object[$i]->sprite->speedY >> 4) + $level->object[$i]->sprite->y) != ($level->object[$i]->sprite->y >> 4)) {                        //The purpose is to test if the speed makes it necessary to check above sprite, the above line is a bug                        $tileY--;                        if ($this->get_ceilflag($level, $tileY, $tileX) != self::CFLAG_NOCEILING) { //Hit, stop elevating                            //Hit, stop elevating                            $level->object[$i]->sprite->speedY = 0;                            if (!($level->object[$i]->objectdata->bounce)) {                                $level->object[$i]->sprite->y = self::s16($level->object[$i]->sprite->y & 0xFFF0); //if not bouncing, place it on top of the floor tile when falling down                            }                            continue;                        }                    }                }            } else if (($level->object[$i]->sprite->droptobottom) || (($level->object[$i]->objectdata->droptobottom) && ($level->object[$i]->sprite->speedY >= 10 * 16))) {                //Object's falling down, and it's dropping to bottom (skip collision detection)                if (!$level->object[$i]->sprite->visible) {                    //if it's dropping to bottom and is not visible, delete                    $level->object[$i]->sprite->enabled = false;                    continue;                }            } else {                //Object's falling down, but not dropping to bottom, do collision detection!                $tileX = $level->object[$i]->sprite->x >> 4;                $tileY = $level->object[$i]->sprite->y >> 4;                $hflag = $this->get_horizflag($level, $tileY, $tileX);                $fflag = $this->get_floorflag($level, $tileY, $tileX);                if (($level->object[$i]->sprite->y <= 6) || ($level->object[$i]->sprite->y >= ($level->height << 4))) {                    $fflag = self::FFLAG_NOFLOOR;                    if ($level->object[$i]->sprite->y >= ($level->height << 4) + 64) {                        //delete if object is below level + 64                        $level->object[$i]->sprite->enabled = false;                        continue;                    }                }                if ($fflag == self::FFLAG_FIRE) {                    //delete if floor eats all objects (fire)                    $level->object[$i]->sprite->enabled = false;                    continue;                }                if ($fflag == self::FFLAG_WATER) {                    if ($level->object[$i]->sprite->number == self::FIRST_OBJET + 9) { //Ball                        //Collision, stop fall                        $level->object[$i]->sprite->speedY = 0;                        continue;                    } else {                        //if it ain't a ball, delete!                        $level->object[$i]->sprite->enabled = false;                        continue;                    }                }                if (($fflag != self::FFLAG_LADDER) && (($fflag != self::FFLAG_NOFLOOR) || ($hflag == self::HFLAG_WALL) || ($hflag == self::HFLAG_DEADLY) || ($hflag == self::HFLAG_PADLOCK))) {                    //Collision, stop fall                    $level->object[$i]->sprite->speedY = 0;                    if (!($level->object[$i]->objectdata->bounce)) {                        $level->object[$i]->sprite->y = self::s16($level->object[$i]->sprite->y & 0xFFF0); //if not bouncing, place it on top of the floor tile when falling down                    }                    continue;                }                //Test for sprite collision                $tile_count = self::s8((($level->object[$i]->sprite->y + ($level->object[$i]->sprite->speedY >> 4)) >> 4) - ($level->object[$i]->sprite->y >> 4));                if ($tile_count != 0) { //Bug: this should be inside the tile collision test                    $obj_vs_sprite = $this->SPRITES_VS_SPRITES($level, $level->object[$i]->sprite, $level->object[$i]->sprite->spritedata, $off_object);                }                //Test all tiles the object will travel through                for ($j = 0; $j < $tile_count; $j++) {                    if ($obj_vs_sprite) {                        break;                    }                    $tileY += 1;                    $hflag = $this->get_horizflag($level, $tileY, $tileX);                    $fflag = $this->get_floorflag($level, $tileY, $tileX);                    if ($fflag == self::FFLAG_FIRE) {                        //delete if floor eats all objects (fire)                        $level->object[$i]->sprite->enabled = false;                        break;                    }                    if (($fflag != self::FFLAG_LADDER) && (($fflag != self::FFLAG_NOFLOOR) || ($hflag == self::HFLAG_WALL) || ($hflag == self::HFLAG_DEADLY) || ($hflag == self::HFLAG_PADLOCK))) {                        //Collision at high speed, stop fall                        if (!($level->object[$i]->objectdata->bounce)) {                            $level->object[$i]->sprite->speedY = 0;                            $level->object[$i]->sprite->y = self::s16(($level->object[$i]->sprite->y & 0xFFF0) + 16);                            if (($level->object[$i]->sprite->number >= self::FIRST_OBJET + 19) && ($level->object[$i]->sprite->number <= self::FIRST_OBJET + 22)) { //Flying carpet                                $this->updateobjectsprite($level, $level->object[$i], self::FIRST_OBJET + 19, false); //Carpet is closed                                $this->TAPISWAIT_FLAG = 0;                            }                            if (($level->object[$i]->sprite->visible) && !($level->player->sprite2->enabled)) {                                $this->FUME_FLAG = 32; //Smoke                                $level->player->sprite2->y = $level->object[$i]->sprite->y;                                $level->player->sprite2->x = $level->object[$i]->sprite->x;                                $this->updatesprite($level, $level->player->sprite2, self::FIRST_OBJET + 16, true); //Smoke                            }                        } else {                            //object is bouncing                            $level->object[$i]->sprite->y = self::s16(($level->object[$i]->sprite->y & 0xFFF0) + 16);                            $this->GRAVITY_FLAG = 4;                            $level->object[$i]->mass = 0;                            //Bounce, decrease speed                            $level->object[$i]->sprite->speedY = 0 - $level->object[$i]->sprite->speedY + 16 * 3;                            if ($level->object[$i]->sprite->speedY > 0) {                                $level->object[$i]->sprite->speedY = 0;                            }                        }                        break;                    }                }                if (!($obj_vs_sprite) && (($j < $tile_count) || !($level->object[$i]->sprite->enabled))) { //collision/kill in previous for loop                    continue;                }                if (!($obj_vs_sprite)) {                    $obj_vs_sprite = $this->SPRITES_VS_SPRITES($level, $level->object[$i]->sprite, $level->object[$i]->sprite->spritedata, $off_object);                }                if ($obj_vs_sprite) {                    $level->object[$i]->mass = 0;                    if ($off_object->objectdata->bounce) {                        //offending object bounces                        $off_object->sprite->UNDER |= 0x01; //(?)                        $off_object->sprite->ONTOP = $level->object[$i]->sprite; //(check this)                        if ($level->object[$i]->sprite->speedY > 64) {                            $off_object->sprite->speedY = ((0 - $level->object[$i]->sprite->speedY) >> 1) + 32;                            $level->object[$i]->sprite->speedY = ((0 - $level->object[$i]->sprite->speedY) >> 1) + 16;                            $off_object->sprite->speedX = $level->object[$i]->sprite->speedX >> 1;                        } else {                            $level->object[$i]->sprite->speedY = 0;                            $level->object[$i]->sprite->y = $off_object->sprite->y - $off_object->sprite->spritedata->collheight;                            continue;                        }                    } else if ($level->object[$i]->objectdata->bounce) {                        //object bounces                        $level->object[$i]->sprite->y = $off_object->sprite->y - $off_object->sprite->spritedata->collheight;                        if (($level->object[$i]->sprite->speedY >= 16) || ($level->object[$i]->sprite->speedY < 0)) {                            $this->GRAVITY_FLAG = 4;                            //level->object[i].mass = 0;                            //Bounce, decrease speed                            $level->object[$i]->sprite->speedY = 0 - $level->object[$i]->sprite->speedY + 16 * 3;                            if ($level->object[$i]->sprite->speedY > 0) {                                $level->object[$i]->sprite->speedY = 0;                            }                        } else {                            $level->object[$i]->sprite->speedY = 0;                        }                        continue;                    } else {                        //neither of the objects bounces                        $level->object[$i]->sprite->y = $off_object->sprite->y - $off_object->sprite->spritedata->collheight;                        $level->object[$i]->sprite->speedY = 0;                    }                }            }            $max_speed = 15; //max speed            if ($level->object[$i]->sprite->number < self::FIRST_NMI) {                $max_speed = $level->object[$i]->objectdata->maxspeedY;            }            $speed = ($level->object[$i]->sprite->speedY >> 4);            if ($speed != 0) {                $this->GRAVITY_FLAG = 4;            }            $level->object[$i]->sprite->y += $speed; //Finally move the object at it's speed            if ($speed < $max_speed) {                $level->object[$i]->sprite->speedY += 16;                if ($level->object[$i]->sprite->speedY > 0) {                    $level->object[$i]->mass++;                }            }            $this->shock($level, $level->object[$i]); //Falling object versus player        }    }    public function shock(Level $level, TitusObject $object): void    { //Falling object versus player        $player = $level->player;        //Quick test        if ($object->mass < 10) return;        if ($player->sprite->speedY >= self::MAX_Y * 16) return;        if (abs($player->sprite->y - $object->sprite->y) >= 32) {            return;        }        if (abs($player->sprite->x - $object->sprite->x) >= 32) {            return;        }        //Test X        if ($object->sprite->x > $player->sprite->x) { //Object center is right for player            if ($object->sprite->x > $player->sprite->x + 24) return; //Object is too far right        } else { //Object center is left for center            if ($object->sprite->x + $object->sprite->spritedata->collwidth < $player->sprite->x) return; //Return if object is too far left        }        //Test Y        if ($object->sprite->y < $player->sprite->y) { //Object bottom is above player bottom            if ($object->sprite->y <= $player->sprite->y - 32) return; //Return if object is completely above the player        } else { //Object bottom is below player bottom            if ($object->sprite->y - $object->sprite->spritedata->collheight + 1 >= $player->sprite->y) return; //Return if object is completely below the player        }        //Hit!        $this->FX_START(5); //Sound effect        $this->CHOC_FLAG = 24;        if ($object->sprite->killing) {            if (!$this->GODMODE) {                $this->DEC_ENERGY($level);            }            $object->sprite->killing = false;        }    }    /**     * Check if there is an object below that can support the input object.     *     * In C, sprite1ref is a TITUS_sprite* but only sprite1ref->spritedata is used, and player.c passes     * &(level->spritedata[0]) (a spritedata, type-punned). Here sprite1ref is directly that spritedata:     * pass $sprite->spritedata for an object, $level->spritedata[0] for the player.     */    public function SPRITES_VS_SPRITES(Level $level, Sprite $sprite1, SpriteData $sprite1ref, ?TitusObject &$object2): bool    { //check if there is an object below that can support the input object        //sprite1ref is equal to sprite1, except when sprite1 is the player, then sprite1ref is level->spritedata[0] (first player sprite)        $obj1left = self::s16($sprite1->x - ($sprite1ref->w >> 1));        for ($i = 0; $i < $level->objectcount; $i++) { //loop all objects            if (($level->object[$i]->sprite === $sprite1) || !($level->object[$i]->sprite->enabled) || !($level->object[$i]->objectdata->support)) continue; //skip disabled objects and itself, and it must support            if (abs($level->object[$i]->sprite->x - $obj1left) > 64) continue; // Bug, center vs left edge            if (abs($level->object[$i]->sprite->y - $sprite1->y) > 70) continue;            //complex test, first test X            //level->object[i].sprite.collisionwidth = true size of the X level->object[i]            $obj2left = self::s16($level->object[$i]->sprite->x - ($level->object[$i]->sprite->spritedata->collwidth >> 1)); //Object 2's left X for collision            if ($obj2left > $obj1left) {                //Object 2's left collision edge is right for object 1's left sprite edge                if ($obj1left + $sprite1ref->collwidth <= $obj2left) continue; //Sprite 1 is too far left (SZOBJ2 = object 1 collision width)            } else {                //Object 2's left collision edge is left for or equal to object 1's left sprite edge                if ($obj2left + $level->object[$i]->sprite->spritedata->collwidth <= $obj1left) continue; //Sprite 1 is too far right            }            //test Y            if ($level->object[$i]->sprite->y - ($level->object[$i]->sprite->spritedata->collheight >> 3) >= $sprite1->y) { //The object must necessarily be below!                if ($level->object[$i]->sprite->y - $level->object[$i]->sprite->spritedata->collheight <= $sprite1->y) {                    //Collision!                    $object2 = $level->object[$i];                    return true;                }            }        } //loop to next object        return false; //No collision    }    public function updateobjectsprite(Level $level, TitusObject $obj, int $number, bool $clearflags): int    {        $index = $number - self::FIRST_OBJET;        $this->updatesprite($level, $obj->sprite, $number, $clearflags);        if (($index < 0) || ($index >= self::ORIG_OBJECT_COUNT)) {            $index = 0;        }        $obj->objectdata = $level->objectdata[$index];        return (0);    }}