Esempio n. 1
0
coord_t Mobj_Friction(mobj_t const *mo)
{
    if(Mobj_IsAirborne(mo))
    {
        // Airborne "friction".
        return FRICTION_FLY;
    }

#if __JHERETIC__
    if(P_ToXSector(Mobj_Sector(mo))->special == 15) // Low friction.
    {
        return FRICTION_LOW;
    }
#endif

#if __JHEXEN__
    terraintype_t const *tt = P_MobjFloorTerrain(mo);
    if(tt->flags & TTF_FRICTION_LOW)
    {
        return FRICTION_LOW;
    }
#endif

#if LIBCOMMON_HAVE_XG
    // Use the current sector's friction.
    return XS_Friction(Mobj_Sector(mo));
#else
    return FRICTION_NORMAL;
#endif
}
Esempio n. 2
0
coord_t Mobj_ThrustMul(mobj_t const *mo)
{
    coord_t mul = 1.0;

#if __JHEXEN__
    if(P_MobjFloorTerrain(mo)->flags & TTF_FRICTION_LOW)
    {
        mul /= 2;
    }
#else // !__JHEXEN__
    Sector *sec = Mobj_Sector(mo);

#if __JHERETIC__
    if(P_ToXSector(sec)->special == 15) // Friction_Low
    {
        mul /= 4;
        return mul; // XG friction ignored.
    }
#endif

    // Use a thrust multiplier based on the sector's friction.
    mul = Mobj_ThrustMulForFriction(XS_Friction(sec));

#endif

    return mul;
}
Esempio n. 3
0
/**
 * Called every tic frame that the player origin is in a special sector
 */
void P_PlayerInSpecialSector(player_t *player)
{
    Sector *sector = Mobj_Sector(player->plr->mo);

    if(IS_CLIENT) return;

    // Falling, not all the way down yet?
    if(!FEQUAL(player->plr->mo->origin[VZ], P_GetDoublep(sector, DMU_FLOOR_HEIGHT))) return;

    // Has hitten ground.
    switch(P_ToXSector(sector)->special)
    {
    default:
        break;

    case 5: ///< HELLSLIME DAMAGE
        if(!player->powers[PT_IRONFEET])
            if(!(mapTime & 0x1f))
                P_DamageMobj(player->plr->mo, NULL, NULL, 10, false);
        break;

    case 7: ///< NUKAGE DAMAGE
        if(!player->powers[PT_IRONFEET])
            if(!(mapTime & 0x1f))
                P_DamageMobj(player->plr->mo, NULL, NULL, 5, false);
        break;

    case 16: ///< SUPER HELLSLIME DAMAGE
    case 4:  ///< STROBE HURT
        if(!player->powers[PT_IRONFEET] || (P_Random() < 5))
        {
            if(!(mapTime & 0x1f))
                P_DamageMobj(player->plr->mo, NULL, NULL, 20, false);
        }
        break;

    case 9: ///< SECRET SECTOR
        player->secretCount++;
        P_ToXSector(sector)->special = 0;
        if(cfg.secretMsg)
        {
            P_SetMessage(player, "You've found a secret area!");
            // S_ConsoleSound(SFX_SECRET, 0, player - players); // jd64
        }
        break;
    }
}
Esempio n. 4
0
static int findMobj(thinker_t *th, void *context)
{
    findmobjparams_t *params = (findmobjparams_t *) context;
    mobj_t *mo = (mobj_t *) th;

    // Must be of the correct type?
    if(params->type >= 0 && params->type != mo->type)
        return false; // Continue iteration.

    // Must be in the specified sector?
    if(params->sec && params->sec != Mobj_Sector(mo))
        return false; // Continue iteration.

    // Found it!
    params->foundMobj = mo;
    return true; // Stop iteration.
}
Esempio n. 5
0
int EV_Teleport(Line* line, int side, mobj_t* mo, dd_bool spawnFog)
{
    mobj_t* dest;

    // Clients cannot teleport on their own.
    if(IS_CLIENT) return 0;

    if(mo->flags2 & MF2_NOTELEPORT) return 0;

    // Don't teleport if hit back of line, so you can get out of teleporter.
    if(side == 1) return 0;

    if((dest = getTeleportDestination(P_ToXLine(line)->tag)) != NULL)
    {
        // A suitable destination has been found.
        coord_t oldPos[3], aboveFloor;
        angle_t oldAngle;
        mobj_t* fog;
        uint an;

        memcpy(oldPos, mo->origin, sizeof(mo->origin));
        oldAngle = mo->angle;
        aboveFloor = mo->origin[VZ] - mo->floorZ;

        if(!P_TeleportMove(mo, dest->origin[VX], dest->origin[VY], false))
            return 0;

        mo->origin[VZ] = mo->floorZ;

        if(spawnFog)
        {
            // Spawn teleport fog at source and destination.
            if((fog = P_SpawnMobj(MT_TFOG, oldPos, oldAngle + ANG180, 0)))
                S_StartSound(SFX_TELEPT, fog);

            an = dest->angle >> ANGLETOFINESHIFT;
            if((fog = P_SpawnMobjXYZ(MT_TFOG, dest->origin[VX] + 20 * FIX2FLT(finecosine[an]),
                                              dest->origin[VY] + 20 * FIX2FLT(finesine[an]),
                                              mo->origin[VZ], dest->angle + ANG180, 0)))
            {
                // Emit sound, where?
                S_StartSound(SFX_TELEPT, fog);
            }
        }

        mo->angle = dest->angle;
        if(mo->flags2 & MF2_FLOORCLIP)
        {
            mo->floorClip = 0;

            if(FEQUAL(mo->origin[VZ], P_GetDoublep(Mobj_Sector(mo), DMU_FLOOR_HEIGHT)))
            {
                terraintype_t const *tt = P_MobjFloorTerrain(mo);
                if(tt->flags & TTF_FLOORCLIP)
                {
                    mo->floorClip = 10;
                }
            }
        }

        mo->mom[MX] = mo->mom[MY] = mo->mom[MZ] = 0;

        // $voodoodolls Must be the real player.
        if(mo->player && mo->player->plr->mo == mo)
        {
            mo->reactionTime = 18; // Don't move for a bit.
            if(mo->player->powers[PT_FLIGHT] && aboveFloor > 0)
            {
                mo->origin[VZ] = mo->floorZ + aboveFloor;
                if(mo->origin[VZ] + mo->height > mo->ceilingZ)
                {
                    mo->origin[VZ] = mo->ceilingZ - mo->height;
                }
            }
            else
            {
                //mo->dPlayer->clLookDir = 0; /* $unifiedangles */
                mo->dPlayer->lookDir = 0;
            }
            mo->player->viewHeight = (coord_t) cfg.common.plrViewHeight;
            mo->player->viewHeightDelta = 0;
            mo->player->viewZ = mo->origin[VZ] + mo->player->viewHeight;
            mo->player->viewOffset[VX] = mo->player->viewOffset[VY] = mo->player->viewOffset[VZ] = 0;
            mo->player->bob = 0;

            //mo->dPlayer->clAngle = mo->angle; /* $unifiedangles */
            mo->dPlayer->flags |= DDPF_FIXANGLES | DDPF_FIXORIGIN | DDPF_FIXMOM;
        }

        return 1;
    }
Esempio n. 6
0
int mobj_s::read(MapStateReader *msr)
{
#define FF_FULLBRIGHT 0x8000 ///< Used to be a flag in thing->frame.
#define FF_FRAMEMASK  0x7fff

    Reader1 *reader = msr->reader();

    int ver = Reader_ReadByte(reader);

#if !__JHEXEN__
    if(ver >= 2) // Version 2 has mobj archive numbers.
    {
        msr->addMobjToThingArchive(this, Reader_ReadInt16(reader));
    }
#endif

#if !__JHEXEN__
    target = 0;
    if(ver >= 2)
    {
        target   = INT2PTR(mobj_t, Reader_ReadInt16(reader));
    }
#endif

#if __JDOOM__ || __JDOOM64__
    tracer = 0;
    if(ver >= 5)
    {
        tracer   = INT2PTR(mobj_t, Reader_ReadInt16(reader));
    }
#endif

    onMobj = 0;
#if __JHEXEN__
    if(ver >= 8)
#else
    if(ver >= 5)
#endif
    {
        onMobj   = INT2PTR(mobj_t, Reader_ReadInt16(reader));
    }

    origin[VX]   = FIX2FLT(Reader_ReadInt32(reader));
    origin[VY]   = FIX2FLT(Reader_ReadInt32(reader));
    origin[VZ]   = FIX2FLT(Reader_ReadInt32(reader));
    angle        = Reader_ReadInt32(reader);
    sprite       = Reader_ReadInt32(reader);

    frame        = Reader_ReadInt32(reader); // might be ORed with FF_FULLBRIGHT
    if(frame & FF_FULLBRIGHT)
        frame &= FF_FRAMEMASK; // not used anymore.

#if __JHEXEN__
    if(ver < 6)
    {
        /*floorflat =*/ Reader_ReadInt32(reader);
    }
#else
    floorZ       = FIX2FLT(Reader_ReadInt32(reader));
    ceilingZ     = FIX2FLT(Reader_ReadInt32(reader));
#endif

    radius       = FIX2FLT(Reader_ReadInt32(reader));
    height       = FIX2FLT(Reader_ReadInt32(reader));
    mom[MX]      = FIX2FLT(Reader_ReadInt32(reader));
    mom[MY]      = FIX2FLT(Reader_ReadInt32(reader));
    mom[MZ]      = FIX2FLT(Reader_ReadInt32(reader));
    valid        = Reader_ReadInt32(reader);
    type         = Reader_ReadInt32(reader);

#if __JHEXEN__
    if(ver < 7)
    {
        /*info   = (mobjinfo_t *)*/ Reader_ReadInt32(reader);
    }
#endif
    info = &MOBJINFO[type];

    if(info->flags2 & MF2_FLOATBOB)
        mom[MZ] = 0;

    if(info->flags & MF_SOLID)
        ddFlags |= DDMF_SOLID;
    if(info->flags2 & MF2_DONTDRAW)
        ddFlags |= DDMF_DONTDRAW;

    tics         = Reader_ReadInt32(reader);
    state        = INT2PTR(state_t, Reader_ReadInt32(reader));
#if __JHEXEN__
    damage       = Reader_ReadInt32(reader);
#endif
    flags        = Reader_ReadInt32(reader);
#if __JHEXEN__
    flags2       = Reader_ReadInt32(reader);
    if(ver >= 5)
    {
        flags3   = Reader_ReadInt32(reader);
    }
    special1     = Reader_ReadInt32(reader);
    special2     = Reader_ReadInt32(reader);
#endif
    health       = Reader_ReadInt32(reader);

#if __JHERETIC__
    if(ver < 8)
    {
        // Fix a bunch of kludges in the original Heretic.
        switch(type)
        {
        case MT_MACEFX1:
        case MT_MACEFX2:
        case MT_MACEFX3:
        case MT_HORNRODFX2:
        case MT_HEADFX3:
        case MT_WHIRLWIND:
        case MT_TELEGLITTER:
        case MT_TELEGLITTER2:
            special3 = health;
            if(type == MT_HORNRODFX2 && special3 > 16)
                special3 = 16;
            health = MOBJINFO[type].spawnHealth;
            break;

        default:
            break;
        }
    }
#endif

    moveDir      = Reader_ReadInt32(reader);
    moveCount    = Reader_ReadInt32(reader);
#if __JHEXEN__
    target       = INT2PTR(mobj_t, Reader_ReadInt32(reader));
#endif
    reactionTime = Reader_ReadInt32(reader);
    threshold    = Reader_ReadInt32(reader);
    player       = INT2PTR(player_t, Reader_ReadInt32(reader));
    lastLook     = Reader_ReadInt32(reader);

#if __JHEXEN__
    floorClip    = FIX2FLT(Reader_ReadInt32(reader));
    msr->addMobjToThingArchive(this, Reader_ReadInt32(reader));
    tid          = Reader_ReadInt32(reader);
#else
    // For nightmare respawn.
    if(ver >= 6)
    {
        spawnSpot.origin[VX] = FIX2FLT(Reader_ReadInt32(reader));
        spawnSpot.origin[VY] = FIX2FLT(Reader_ReadInt32(reader));
        spawnSpot.origin[VZ] = FIX2FLT(Reader_ReadInt32(reader));
        spawnSpot.angle      = Reader_ReadInt32(reader);
        if(ver < 10)
        {
            /*spawnSpot.type =*/ Reader_ReadInt32(reader);
        }
        spawnSpot.flags      = Reader_ReadInt32(reader);
    }
    else
    {
        spawnSpot.origin[VX] = (float) Reader_ReadInt16(reader);
        spawnSpot.origin[VY] = (float) Reader_ReadInt16(reader);
        spawnSpot.origin[VZ] = 0; // Initialize with "something".
        spawnSpot.angle      = (angle_t) (ANG45 * (Reader_ReadInt16(reader) / 45));
        /*spawnSpot.type       = (int)*/ Reader_ReadInt16(reader);
        spawnSpot.flags      = (int) Reader_ReadInt16(reader);
    }

# if __JDOOM__ || __JDOOM64__
    if(ver >= 3)
# elif __JHERETIC__
    if(ver >= 5)
# endif
    {
        intFlags = Reader_ReadInt32(reader);
        dropOffZ = FIX2FLT(Reader_ReadInt32(reader));
        gear     = Reader_ReadInt32(reader);
    }

# if __JDOOM__ || __JDOOM64__
    if(ver >= 6)
    {
        damage = Reader_ReadInt32(reader);
        flags2 = Reader_ReadInt32(reader);
    }
    else // flags2 will be applied from the defs.
    {
        damage = DDMAXINT; // Use the value set in mo->info->damage
    }

# elif __JHERETIC__
    damage = Reader_ReadInt32(reader);
    flags2 = Reader_ReadInt32(reader);
# endif

    if(ver >= 7)
    {
        flags3 = Reader_ReadInt32(reader);
    } // Else flags3 will be applied from the defs.
#endif

#if __JHEXEN__
    special = Reader_ReadInt32(reader);
    Reader_Read(reader, args, 1 * 5);
#elif __JHERETIC__
    special1 = Reader_ReadInt32(reader);
    special2 = Reader_ReadInt32(reader);
    if(ver >= 8)
    {
        special3 = Reader_ReadInt32(reader);
    }
#endif

#if __JHEXEN__
    if(ver >= 2)
#else
    if(ver >= 4)
#endif
    {
        translucency = Reader_ReadByte(reader);
    }

#if __JHEXEN__
    if(ver >= 3)
#else
    if(ver >= 5)
#endif
    {
        visTarget = (short) (Reader_ReadByte(reader)) -1;
    }

#if __JHEXEN__
    if(ver >= 4)
    {
        tracer    = INT2PTR(mobj_t, Reader_ReadInt32(reader));
    }

    if(ver >= 4)
    {
        lastEnemy = INT2PTR(mobj_t, Reader_ReadInt32(reader));
    }
#else
    if(ver >= 5)
    {
        floorClip = FIX2FLT(Reader_ReadInt32(reader));
    }
#endif

#if __JHERETIC__
    if(ver >= 7)
    {
        generator = INT2PTR(mobj_t, Reader_ReadInt16(reader));
    }
    else
    {
        generator = 0;
    }
#endif

    /*
     * Restore! (unmangle)
     */
    info = &MOBJINFO[type];

    Mobj_SetState(this, PTR2INT(state));
#if __JHEXEN__
    if(flags2 & MF2_DORMANT)
    {
        tics = -1;
    }
#endif

    if(player)
    {
        // The player number translation table is used to find out the
        // *current* (actual) player number of the referenced player.
        player = msr->player(PTR2INT(player));
#if __JHEXEN__
        if(!player)
        {
            // This saved player does not exist in the current game!
            // Destroy this mobj.
            Mobj_Destroy(this);
            return false;
        }
#endif
        dPlayer = player->plr;

        dPlayer->mo      = this;
        //dPlayer->clAngle = angle; /* $unifiedangles */
        dPlayer->lookDir = 0; /* $unifiedangles */
    }

    visAngle = angle >> 16;

#if !__JHEXEN__
    if(dPlayer && !dPlayer->inGame)
    {
        dPlayer->mo = 0;
        Mobj_Destroy(this);
        return false;
    }
#endif

#if !__JDOOM64__
    // Do we need to update this mobj's flag values?
    if(ver < MOBJ_SAVEVERSION)
    {
        SV_TranslateLegacyMobjFlags(this, ver);
    }
#endif

    P_MobjLink(this);
    floorZ   = P_GetDoublep(Mobj_Sector(this), DMU_FLOOR_HEIGHT);
    ceilingZ = P_GetDoublep(Mobj_Sector(this), DMU_CEILING_HEIGHT);

    return false;

#undef FF_FRAMEMASK
#undef FF_FULLBRIGHT
}
Esempio n. 7
0
void Mobj_XYMoveStopping(mobj_t *mo)
{
    DENG_ASSERT(mo != 0);

    player_t *player = mo->player;

    if(player && (P_GetPlayerCheats(player) & CF_NOMOMENTUM))
    {
        // Debug option for no sliding at all.
        mo->mom[MX] = mo->mom[MY] = 0;
        return;
    }

    if(mo->flags & (MF_MISSILE | MF_SKULLFLY))
    {
        // No friction for missiles.
        return;
    }

    if(mo->origin[VZ] > mo->floorZ && !mo->onMobj && !(mo->flags2 & MF2_FLY))
    {
        // No friction when falling.
        return;
    }

#ifndef __JHEXEN__
    if(cfg.slidingCorpses)
    {
        // $dropoff_fix: Add objects falling off ledges. Does not apply to players!
        if(((mo->flags & MF_CORPSE) || (mo->intFlags & MIF_FALLING)) && !mo->player)
        {
            // Do not stop sliding if halfway off a step with some momentum.
            if(!INRANGE_OF(mo->mom[MX], 0, DROPOFFMOMENTUM_THRESHOLD) ||
               !INRANGE_OF(mo->mom[MY], 0, DROPOFFMOMENTUM_THRESHOLD))
            {
                if(!FEQUAL(mo->floorZ, P_GetDoublep(Mobj_Sector(mo), DMU_FLOOR_HEIGHT)))
                    return;
            }
        }
    }
#endif

    bool isVoodooDoll = Mobj_IsVoodooDoll(mo);
    bool belowWalkStop = (INRANGE_OF(mo->mom[MX], 0, WALKSTOP_THRESHOLD) &&
                          INRANGE_OF(mo->mom[MY], 0, WALKSTOP_THRESHOLD));

    bool belowStandSpeed = false;
    bool isMovingPlayer  = false;
    if(player)
    {
        belowStandSpeed = (INRANGE_OF(mo->mom[MX], 0, STANDSPEED) &&
                           INRANGE_OF(mo->mom[MY], 0, STANDSPEED));
        isMovingPlayer = (!FEQUAL(player->plr->forwardMove, 0) ||
                        !FEQUAL(player->plr->sideMove, 0));
    }

    // Stop player walking animation (only real players).
    if(!isVoodooDoll && player && belowStandSpeed && !isMovingPlayer &&
        !IS_NETWORK_SERVER) // Netgame servers use logic elsewhere for player animation.
    {
        // If in a walking frame, stop moving.
        if(P_PlayerInWalkState(player))
        {
            P_MobjChangeState(player->plr->mo, statenum_t(PCLASS_INFO(player->class_)->normalState));
        }
    }

    // Apply friction.
    if(belowWalkStop && !isMovingPlayer)
    {
        // $voodoodolls: Do not zero mom for voodoo dolls!
        if(!isVoodooDoll)
        {
            // Momentum is below the walkstop threshold; stop it completely.
            mo->mom[MX] = mo->mom[MY] = 0;

            // $voodoodolls: Stop view bobbing if this isn't a voodoo doll.
            if(player) player->bob = 0;
        }
    }
    else
    {
        coord_t friction = Mobj_Friction(mo);
        mo->mom[MX] *= friction;
        mo->mom[MY] *= friction;
    }
}
Esempio n. 8
0
static void SV_v13_ReadMobj(void)
{
    coord_t pos[3], mom[3], floorz, ceilingz, radius, height;
    int frame, valid, type, ddflags = 0;
    spritenum_t sprite;
    mobjinfo_t* info;
    angle_t angle;
    mobj_t* mo;

    // The thinker was 3 ints long.
    Reader_ReadInt32(svReader);
    Reader_ReadInt32(svReader);
    Reader_ReadInt32(svReader);

    pos[VX] = FIX2FLT(Reader_ReadInt32(svReader));
    pos[VY] = FIX2FLT(Reader_ReadInt32(svReader));
    pos[VZ] = FIX2FLT(Reader_ReadInt32(svReader));

    // Sector links.
    Reader_ReadInt32(svReader);
    Reader_ReadInt32(svReader);

    angle = (angle_t) (ANG45 * (Reader_ReadInt32(svReader) / 45));
    sprite = Reader_ReadInt32(svReader);
    frame = Reader_ReadInt32(svReader);
    frame &= ~FF_FRAMEMASK; // not used anymore.

    // Block links.
    Reader_ReadInt32(svReader);
    Reader_ReadInt32(svReader);

    // Subsector.
    Reader_ReadInt32(svReader);

    floorz   = FIX2FLT(Reader_ReadInt32(svReader));
    ceilingz = FIX2FLT(Reader_ReadInt32(svReader));
    radius   = FIX2FLT(Reader_ReadInt32(svReader));
    height   = FIX2FLT(Reader_ReadInt32(svReader));
    mom[MX]  = FIX2FLT(Reader_ReadInt32(svReader));
    mom[MY]  = FIX2FLT(Reader_ReadInt32(svReader));
    mom[MZ]  = FIX2FLT(Reader_ReadInt32(svReader));
    valid    = Reader_ReadInt32(svReader);
    type     = Reader_ReadInt32(svReader);

    info = &MOBJINFO[type];

    if(info->flags & MF_SOLID)
        ddflags |= DDMF_SOLID;
    if(info->flags2 & MF2_DONTDRAW)
        ddflags |= DDMF_DONTDRAW;

    /**
     * We now have all the information we need to create the mobj.
     */
    mo = Mobj_CreateXYZ(P_MobjThinker, pos[VX], pos[VY], pos[VZ], angle,
                         radius, height, ddflags);

    mo->sprite  = sprite;
    mo->frame   = frame;
    mo->floorZ  = floorz;
    mo->ceilingZ = ceilingz;
    mo->mom[MX] = mom[MX];
    mo->mom[MY] = mom[MY];
    mo->mom[MZ] = mom[MZ];
    mo->valid   = valid;
    mo->type    = type;
    mo->moveDir = DI_NODIR;

    /**
     * Continue reading the mobj data.
     */

    Reader_ReadInt32(svReader); // info

    mo->tics     = Reader_ReadInt32(svReader);
    mo->state    = INT2PTR(state_t, Reader_ReadInt32(svReader));
    mo->damage   = Reader_ReadInt32(svReader);
    mo->flags    = Reader_ReadInt32(svReader);
    mo->flags2   = Reader_ReadInt32(svReader);
    mo->special1 = Reader_ReadInt32(svReader);
    mo->special2 = Reader_ReadInt32(svReader);
    mo->health   = Reader_ReadInt32(svReader);

    // Fix a bunch of kludges in the original Heretic.
    switch(mo->type)
    {
    case MT_MACEFX1:
    case MT_MACEFX2:
    case MT_MACEFX3:
    case MT_HORNRODFX2:
    case MT_HEADFX3:
    case MT_WHIRLWIND:
    case MT_TELEGLITTER:
    case MT_TELEGLITTER2:
        mo->special3 = mo->health;
        mo->health = info->spawnHealth;
        break;

    default: break;
    }

    mo->moveDir      = Reader_ReadInt32(svReader);
    mo->moveCount    = Reader_ReadInt32(svReader);
    Reader_ReadInt32(svReader); // target
    mo->reactionTime = Reader_ReadInt32(svReader);
    mo->threshold    = Reader_ReadInt32(svReader);
    mo->player       = INT2PTR(player_t, Reader_ReadInt32(svReader));
    mo->lastLook     = Reader_ReadInt32(svReader);

    mo->spawnSpot.origin[VX] = (coord_t) Reader_ReadInt32(svReader);
    mo->spawnSpot.origin[VY] = (coord_t) Reader_ReadInt32(svReader);
    mo->spawnSpot.origin[VZ] = 0; // Initialize with "something".
    mo->spawnSpot.angle = (angle_t) (ANG45 * (Reader_ReadInt32(svReader) / 45));
    /*mo->spawnSpot.type = (int)*/ Reader_ReadInt32(svReader);

    {
    int spawnFlags = ((int) Reader_ReadInt32(svReader)) & ~MASK_UNKNOWN_MSF_FLAGS;
    // Spawn on the floor by default unless the mobjtype flags override.
    spawnFlags |= MSF_Z_FLOOR;
    mo->spawnSpot.flags = spawnFlags;
    }

    mo->info = info;
    SV_TranslateLegacyMobjFlags(mo, 0);

    mo->state = &STATES[PTR2INT(mo->state)];
    mo->target = NULL;
    if(mo->player)
    {
        mo->player = &players[PTR2INT(mo->player) - 1];
        mo->player->plr->mo = mo;
        mo->player->plr->mo->dPlayer = mo->player->plr;
    }
    P_MobjLink(mo);
    mo->floorZ   = P_GetDoublep(Mobj_Sector(mo), DMU_FLOOR_HEIGHT);
    mo->ceilingZ = P_GetDoublep(Mobj_Sector(mo), DMU_CEILING_HEIGHT);
}