diff --git a/doomsday/plugins/common/include/p_saveg.h b/doomsday/plugins/common/include/p_saveg.h index 754f18caa2..cdbd198480 100644 --- a/doomsday/plugins/common/include/p_saveg.h +++ b/doomsday/plugins/common/include/p_saveg.h @@ -28,6 +28,7 @@ DENG_EXTERN_C int thingArchiveVersion; DENG_EXTERN_C uint thingArchiveSize; +DENG_EXTERN_C int saveToRealPlayerNum[MAXPLAYERS]; DENG_EXTERN_C SaveSlots saveSlots; #ifdef __cplusplus @@ -95,6 +96,8 @@ typedef ushort ThingSerialId; */ ThingSerialId SV_ThingArchiveId(mobj_t const *mobj); +void SV_InsertThingInArchive(mobj_t const *mobj, ThingSerialId thingId); + /** * To be called after reading a game state has been read to lookup a pointer * to the mobj which is associated with the specified @a thingId. @@ -148,9 +151,6 @@ void SV_InitTargetPlayers(void); class MapStateReader; class MapStateWriter; -void SV_WriteMobj(thinker_t *th, MapStateWriter *msw); -int SV_ReadMobj(thinker_t *th, MapStateReader *msr); - #if __JHEXEN__ void SV_WriteMovePoly(struct polyevent_s const *movepoly, MapStateWriter *msw); int SV_ReadMovePoly(struct polyevent_s *movepoly, MapStateReader *msr); diff --git a/doomsday/plugins/common/src/mobj.cpp b/doomsday/plugins/common/src/mobj.cpp index aef4832150..241b0b16df 100644 --- a/doomsday/plugins/common/src/mobj.cpp +++ b/doomsday/plugins/common/src/mobj.cpp @@ -28,10 +28,11 @@ #include "common.h" #include "mobj.h" +#include "dmu_lib.h" #include "p_actor.h" #include "p_player.h" #include "p_map.h" -#include "dmu_lib.h" +#include "p_saveg.h" #include #include @@ -327,3 +328,556 @@ dd_bool P_MobjChangeStateNoAction(mobj_t *mobj, statenum_t stateNum) { return changeMobjState(mobj, stateNum, false /*don't call action functions*/); } + +#if __JHEXEN__ +# define MOBJ_SAVEVERSION 8 +#elif __JHERETIC__ +# define MOBJ_SAVEVERSION 10 +#else +# define MOBJ_SAVEVERSION 10 +#endif + +void mobj_s::write(MapStateWriter *msw) const +{ + Writer *writer = msw->writer(); + + mobj_t const *original = (mobj_t *) this; + mobj_t temp, *mo = &temp; + + std::memcpy(mo, original, sizeof(*mo)); + // Mangle it! + mo->state = (state_t *) (mo->state - STATES); + if(mo->player) + mo->player = (player_t *) ((mo->player - players) + 1); + + // Version. + // JHEXEN + // 2: Added the 'translucency' byte. + // 3: Added byte 'vistarget' + // 4: Added long 'tracer' + // 4: Added long 'lastenemy' + // 5: Added flags3 + // 6: Floor material removed. + // + // JDOOM || JHERETIC || JDOOM64 + // 4: Added byte 'translucency' + // 5: Added byte 'vistarget' + // 5: Added tracer in jDoom + // 5: Added dropoff fix in jHeretic + // 5: Added long 'floorclip' + // 6: Added proper respawn data + // 6: Added flags 2 in jDoom + // 6: Added damage + // 7: Added generator in jHeretic + // 7: Added flags3 + // + // JDOOM + // 9: Revised mapspot flag interpretation + // + // JHERETIC + // 8: Added special3 + // 9: Revised mapspot flag interpretation + // + // JHEXEN + // 7: Removed superfluous info ptr + // 8: Added 'onMobj' + Writer_WriteByte(writer, MOBJ_SAVEVERSION); + +#if !__JHEXEN__ + // A version 2 features: archive number and target. + Writer_WriteInt16(writer, SV_ThingArchiveId((mobj_t*) original)); + Writer_WriteInt16(writer, SV_ThingArchiveId(mo->target)); + +# if __JDOOM__ || __JDOOM64__ + // Ver 5 features: Save tracer (fixes Archvile, Revenant bug) + Writer_WriteInt16(writer, SV_ThingArchiveId(mo->tracer)); +# endif +#endif + + Writer_WriteInt16(writer, SV_ThingArchiveId(mo->onMobj)); + + // Info for drawing: position. + Writer_WriteInt32(writer, FLT2FIX(mo->origin[VX])); + Writer_WriteInt32(writer, FLT2FIX(mo->origin[VY])); + Writer_WriteInt32(writer, FLT2FIX(mo->origin[VZ])); + + //More drawing info: to determine current sprite. + Writer_WriteInt32(writer, mo->angle); // Orientation. + Writer_WriteInt32(writer, mo->sprite); // Used to find patch_t and flip value. + Writer_WriteInt32(writer, mo->frame); + +#if !__JHEXEN__ + // The closest interval over all contacted Sectors. + Writer_WriteInt32(writer, FLT2FIX(mo->floorZ)); + Writer_WriteInt32(writer, FLT2FIX(mo->ceilingZ)); +#endif + + // For movement checking. + Writer_WriteInt32(writer, FLT2FIX(mo->radius)); + Writer_WriteInt32(writer, FLT2FIX(mo->height)); + + // Momentums, used to update position. + Writer_WriteInt32(writer, FLT2FIX(mo->mom[MX])); + Writer_WriteInt32(writer, FLT2FIX(mo->mom[MY])); + Writer_WriteInt32(writer, FLT2FIX(mo->mom[MZ])); + + // If == VALIDCOUNT, already checked. + Writer_WriteInt32(writer, mo->valid); + + Writer_WriteInt32(writer, mo->type); + Writer_WriteInt32(writer, mo->tics); // State tic counter. + Writer_WriteInt32(writer, PTR2INT(mo->state)); + +#if __JHEXEN__ + Writer_WriteInt32(writer, mo->damage); +#endif + + Writer_WriteInt32(writer, mo->flags); +#if __JHEXEN__ + Writer_WriteInt32(writer, mo->flags2); + Writer_WriteInt32(writer, mo->flags3); + + if(mo->type == MT_KORAX) + Writer_WriteInt32(writer, 0); // Searching index. + else + Writer_WriteInt32(writer, mo->special1); + + switch(mo->type) + { + case MT_LIGHTNING_FLOOR: + case MT_LIGHTNING_ZAP: + case MT_HOLY_TAIL: + case MT_LIGHTNING_CEILING: + if(mo->flags & MF_CORPSE) + Writer_WriteInt32(writer, 0); + else + Writer_WriteInt32(writer, SV_ThingArchiveId(INT2PTR(mobj_t, mo->special2))); + break; + + default: + Writer_WriteInt32(writer, mo->special2); + break; + } +#endif + Writer_WriteInt32(writer, mo->health); + + // Movement direction, movement generation (zig-zagging). + Writer_WriteInt32(writer, mo->moveDir); // 0-7 + Writer_WriteInt32(writer, mo->moveCount); // When 0, select a new dir. + +#if __JHEXEN__ + if(mo->flags & MF_CORPSE) + Writer_WriteInt32(writer, 0); + else + Writer_WriteInt32(writer, (int) SV_ThingArchiveId(mo->target)); +#endif + + // Reaction time: if non 0, don't attack yet. + // Used by player to freeze a bit after teleporting. + Writer_WriteInt32(writer, mo->reactionTime); + + // If >0, the target will be chased no matter what (even if shot). + Writer_WriteInt32(writer, mo->threshold); + + // Additional info record for player avatars only (only valid if type is MT_PLAYER). + Writer_WriteInt32(writer, PTR2INT(mo->player)); + + // Player number last looked for. + Writer_WriteInt32(writer, mo->lastLook); + +#if !__JHEXEN__ + // For nightmare/multiplayer respawn. + Writer_WriteInt32(writer, FLT2FIX(mo->spawnSpot.origin[VX])); + Writer_WriteInt32(writer, FLT2FIX(mo->spawnSpot.origin[VY])); + Writer_WriteInt32(writer, FLT2FIX(mo->spawnSpot.origin[VZ])); + Writer_WriteInt32(writer, mo->spawnSpot.angle); + Writer_WriteInt32(writer, mo->spawnSpot.flags); + + Writer_WriteInt32(writer, mo->intFlags); // $dropoff_fix: internal flags. + Writer_WriteInt32(writer, FLT2FIX(mo->dropOffZ)); // $dropoff_fix + Writer_WriteInt32(writer, mo->gear); // Used in torque simulation. + + Writer_WriteInt32(writer, mo->damage); + Writer_WriteInt32(writer, mo->flags2); + Writer_WriteInt32(writer, mo->flags3); +# ifdef __JHERETIC__ + Writer_WriteInt32(writer, mo->special1); + Writer_WriteInt32(writer, mo->special2); + Writer_WriteInt32(writer, mo->special3); +# endif + + Writer_WriteByte(writer, mo->translucency); + Writer_WriteByte(writer, (byte)(mo->visTarget +1)); +#endif + + Writer_WriteInt32(writer, FLT2FIX(mo->floorClip)); +#if __JHEXEN__ + Writer_WriteInt32(writer, SV_ThingArchiveId((mobj_t *) original)); + Writer_WriteInt32(writer, mo->tid); + Writer_WriteInt32(writer, mo->special); + Writer_Write(writer, mo->args, sizeof(mo->args)); + Writer_WriteByte(writer, mo->translucency); + Writer_WriteByte(writer, (byte)(mo->visTarget +1)); + + switch(mo->type) + { + case MT_BISH_FX: + case MT_HOLY_FX: + case MT_DRAGON: + case MT_THRUSTFLOOR_UP: + case MT_THRUSTFLOOR_DOWN: + case MT_MINOTAUR: + case MT_SORCFX1: + case MT_MSTAFF_FX2: + case MT_HOLY_TAIL: + case MT_LIGHTNING_CEILING: + if(mo->flags & MF_CORPSE) + Writer_WriteInt32(writer, 0); + else + Writer_WriteInt32(writer, SV_ThingArchiveId(mo->tracer)); + break; + + default: + DENG_ASSERT(mo->tracer == NULL); /// @todo Tracer won't be saved correctly? + Writer_WriteInt32(writer, PTR2INT(mo->tracer)); + break; + } + + Writer_WriteInt32(writer, PTR2INT(mo->lastEnemy)); +#elif __JHERETIC__ + Writer_WriteInt16(writer, SV_ThingArchiveId(mo->generator)); +#endif +} + +static void RestoreMobj(mobj_t *mo, int ver) +{ +#if __JDOOM64__ + DENG_UNUSED(ver); +#endif + + mo->info = &MOBJINFO[mo->type]; + + Mobj_SetState(mo, PTR2INT(mo->state)); +#if __JHEXEN__ + if(mo->flags2 & MF2_DORMANT) + mo->tics = -1; +#endif + + if(mo->player) + { + // The player number translation table is used to find out the + // *current* (actual) player number of the referenced player. + int pNum = saveToRealPlayerNum[PTR2INT(mo->player) - 1]; + +#if __JHEXEN__ + if(pNum < 0) + { + // This saved player does not exist in the current game! + // Destroy this mobj. + Z_Free(mo); + + return; // Don't add this thinker. + } +#endif + + mo->player = &players[pNum]; + mo->dPlayer = mo->player->plr; + + mo->dPlayer->mo = mo; + //mo->dPlayer->clAngle = mo->angle; /* $unifiedangles */ + mo->dPlayer->lookDir = 0; /* $unifiedangles */ + } + + mo->visAngle = mo->angle >> 16; + +#if !__JHEXEN__ + if(mo->dPlayer && !mo->dPlayer->inGame) + { + mo->dPlayer->mo = 0; + Mobj_Destroy(mo); + + return; + } +#endif + +#if !__JDOOM64__ + // Do we need to update this mobj's flag values? + if(ver < MOBJ_SAVEVERSION) + { + SV_TranslateLegacyMobjFlags(mo, ver); + } +#endif + + P_MobjLink(mo); + mo->floorZ = P_GetDoublep(Mobj_Sector(mo), DMU_FLOOR_HEIGHT); + mo->ceilingZ = P_GetDoublep(Mobj_Sector(mo), DMU_CEILING_HEIGHT); + + return; +} + +int mobj_s::read(MapStateReader *msr) +{ +#define FF_FULLBRIGHT 0x8000 ///< Used to be a flag in thing->frame. +#define FF_FRAMEMASK 0x7fff + + Reader *reader = msr->reader(); + + int ver = Reader_ReadByte(reader); + +#if !__JHEXEN__ + if(ver >= 2) // Version 2 has mobj archive numbers. + { + SV_InsertThingInArchive(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)); + SV_InsertThingInArchive(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) + RestoreMobj(this, ver); + + return false; + +#undef FF_FRAMEMASK +#undef FF_FULLBRIGHT +} diff --git a/doomsday/plugins/common/src/p_saveg.cpp b/doomsday/plugins/common/src/p_saveg.cpp index cfc20313cc..08aaa6e3ce 100644 --- a/doomsday/plugins/common/src/p_saveg.cpp +++ b/doomsday/plugins/common/src/p_saveg.cpp @@ -81,7 +81,7 @@ int thingArchiveVersion; uint thingArchiveSize; static bool thingArchiveExcludePlayers; -static int saveToRealPlayerNum[MAXPLAYERS]; +int saveToRealPlayerNum[MAXPLAYERS]; #if __JHEXEN__ static targetplraddress_t *targetPlayerAddrs; #endif @@ -260,7 +260,7 @@ static void initThingArchiveForSave(bool excludePlayers = false) thingArchiveExcludePlayers = excludePlayers; } -static void insertThingInArchive(mobj_t const *mo, ThingSerialId thingId) +void SV_InsertThingInArchive(mobj_t const *mo, ThingSerialId thingId) { DENG_ASSERT(mo != 0); @@ -848,226 +848,6 @@ static void SV_ReadPlayer(player_t *p, Reader *reader) p->update |= PSF_REBORN; } -#if __JHEXEN__ -# define MOBJ_SAVEVERSION 8 -#elif __JHERETIC__ -# define MOBJ_SAVEVERSION 10 -#else -# define MOBJ_SAVEVERSION 10 -#endif - -void SV_WriteMobj(thinker_t *th, MapStateWriter *msw) -{ - Writer *writer = msw->writer(); - - mobj_t const *original = (mobj_t *) th; - mobj_t temp, *mo = &temp; - - std::memcpy(mo, original, sizeof(*mo)); - // Mangle it! - mo->state = (state_t *) (mo->state - STATES); - if(mo->player) - mo->player = (player_t *) ((mo->player - players) + 1); - - // Version. - // JHEXEN - // 2: Added the 'translucency' byte. - // 3: Added byte 'vistarget' - // 4: Added long 'tracer' - // 4: Added long 'lastenemy' - // 5: Added flags3 - // 6: Floor material removed. - // - // JDOOM || JHERETIC || JDOOM64 - // 4: Added byte 'translucency' - // 5: Added byte 'vistarget' - // 5: Added tracer in jDoom - // 5: Added dropoff fix in jHeretic - // 5: Added long 'floorclip' - // 6: Added proper respawn data - // 6: Added flags 2 in jDoom - // 6: Added damage - // 7: Added generator in jHeretic - // 7: Added flags3 - // - // JDOOM - // 9: Revised mapspot flag interpretation - // - // JHERETIC - // 8: Added special3 - // 9: Revised mapspot flag interpretation - // - // JHEXEN - // 7: Removed superfluous info ptr - // 8: Added 'onMobj' - Writer_WriteByte(writer, MOBJ_SAVEVERSION); - -#if !__JHEXEN__ - // A version 2 features: archive number and target. - Writer_WriteInt16(writer, SV_ThingArchiveId((mobj_t*) original)); - Writer_WriteInt16(writer, SV_ThingArchiveId(mo->target)); - -# if __JDOOM__ || __JDOOM64__ - // Ver 5 features: Save tracer (fixes Archvile, Revenant bug) - Writer_WriteInt16(writer, SV_ThingArchiveId(mo->tracer)); -# endif -#endif - - Writer_WriteInt16(writer, SV_ThingArchiveId(mo->onMobj)); - - // Info for drawing: position. - Writer_WriteInt32(writer, FLT2FIX(mo->origin[VX])); - Writer_WriteInt32(writer, FLT2FIX(mo->origin[VY])); - Writer_WriteInt32(writer, FLT2FIX(mo->origin[VZ])); - - //More drawing info: to determine current sprite. - Writer_WriteInt32(writer, mo->angle); // Orientation. - Writer_WriteInt32(writer, mo->sprite); // Used to find patch_t and flip value. - Writer_WriteInt32(writer, mo->frame); - -#if !__JHEXEN__ - // The closest interval over all contacted Sectors. - Writer_WriteInt32(writer, FLT2FIX(mo->floorZ)); - Writer_WriteInt32(writer, FLT2FIX(mo->ceilingZ)); -#endif - - // For movement checking. - Writer_WriteInt32(writer, FLT2FIX(mo->radius)); - Writer_WriteInt32(writer, FLT2FIX(mo->height)); - - // Momentums, used to update position. - Writer_WriteInt32(writer, FLT2FIX(mo->mom[MX])); - Writer_WriteInt32(writer, FLT2FIX(mo->mom[MY])); - Writer_WriteInt32(writer, FLT2FIX(mo->mom[MZ])); - - // If == VALIDCOUNT, already checked. - Writer_WriteInt32(writer, mo->valid); - - Writer_WriteInt32(writer, mo->type); - Writer_WriteInt32(writer, mo->tics); // State tic counter. - Writer_WriteInt32(writer, PTR2INT(mo->state)); - -#if __JHEXEN__ - Writer_WriteInt32(writer, mo->damage); -#endif - - Writer_WriteInt32(writer, mo->flags); -#if __JHEXEN__ - Writer_WriteInt32(writer, mo->flags2); - Writer_WriteInt32(writer, mo->flags3); - - if(mo->type == MT_KORAX) - Writer_WriteInt32(writer, 0); // Searching index. - else - Writer_WriteInt32(writer, mo->special1); - - switch(mo->type) - { - case MT_LIGHTNING_FLOOR: - case MT_LIGHTNING_ZAP: - case MT_HOLY_TAIL: - case MT_LIGHTNING_CEILING: - if(mo->flags & MF_CORPSE) - Writer_WriteInt32(writer, 0); - else - Writer_WriteInt32(writer, SV_ThingArchiveId(INT2PTR(mobj_t, mo->special2))); - break; - - default: - Writer_WriteInt32(writer, mo->special2); - break; - } -#endif - Writer_WriteInt32(writer, mo->health); - - // Movement direction, movement generation (zig-zagging). - Writer_WriteInt32(writer, mo->moveDir); // 0-7 - Writer_WriteInt32(writer, mo->moveCount); // When 0, select a new dir. - -#if __JHEXEN__ - if(mo->flags & MF_CORPSE) - Writer_WriteInt32(writer, 0); - else - Writer_WriteInt32(writer, (int) SV_ThingArchiveId(mo->target)); -#endif - - // Reaction time: if non 0, don't attack yet. - // Used by player to freeze a bit after teleporting. - Writer_WriteInt32(writer, mo->reactionTime); - - // If >0, the target will be chased no matter what (even if shot). - Writer_WriteInt32(writer, mo->threshold); - - // Additional info record for player avatars only (only valid if type is MT_PLAYER). - Writer_WriteInt32(writer, PTR2INT(mo->player)); - - // Player number last looked for. - Writer_WriteInt32(writer, mo->lastLook); - -#if !__JHEXEN__ - // For nightmare/multiplayer respawn. - Writer_WriteInt32(writer, FLT2FIX(mo->spawnSpot.origin[VX])); - Writer_WriteInt32(writer, FLT2FIX(mo->spawnSpot.origin[VY])); - Writer_WriteInt32(writer, FLT2FIX(mo->spawnSpot.origin[VZ])); - Writer_WriteInt32(writer, mo->spawnSpot.angle); - Writer_WriteInt32(writer, mo->spawnSpot.flags); - - Writer_WriteInt32(writer, mo->intFlags); // $dropoff_fix: internal flags. - Writer_WriteInt32(writer, FLT2FIX(mo->dropOffZ)); // $dropoff_fix - Writer_WriteInt32(writer, mo->gear); // Used in torque simulation. - - Writer_WriteInt32(writer, mo->damage); - Writer_WriteInt32(writer, mo->flags2); - Writer_WriteInt32(writer, mo->flags3); -# ifdef __JHERETIC__ - Writer_WriteInt32(writer, mo->special1); - Writer_WriteInt32(writer, mo->special2); - Writer_WriteInt32(writer, mo->special3); -# endif - - Writer_WriteByte(writer, mo->translucency); - Writer_WriteByte(writer, (byte)(mo->visTarget +1)); -#endif - - Writer_WriteInt32(writer, FLT2FIX(mo->floorClip)); -#if __JHEXEN__ - Writer_WriteInt32(writer, SV_ThingArchiveId((mobj_t *) original)); - Writer_WriteInt32(writer, mo->tid); - Writer_WriteInt32(writer, mo->special); - Writer_Write(writer, mo->args, sizeof(mo->args)); - Writer_WriteByte(writer, mo->translucency); - Writer_WriteByte(writer, (byte)(mo->visTarget +1)); - - switch(mo->type) - { - case MT_BISH_FX: - case MT_HOLY_FX: - case MT_DRAGON: - case MT_THRUSTFLOOR_UP: - case MT_THRUSTFLOOR_DOWN: - case MT_MINOTAUR: - case MT_SORCFX1: - case MT_MSTAFF_FX2: - case MT_HOLY_TAIL: - case MT_LIGHTNING_CEILING: - if(mo->flags & MF_CORPSE) - Writer_WriteInt32(writer, 0); - else - Writer_WriteInt32(writer, SV_ThingArchiveId(mo->tracer)); - break; - - default: - DENG_ASSERT(mo->tracer == NULL); /// @todo Tracer won't be saved correctly? - Writer_WriteInt32(writer, PTR2INT(mo->tracer)); - break; - } - - Writer_WriteInt32(writer, PTR2INT(mo->lastEnemy)); -#elif __JHERETIC__ - Writer_WriteInt16(writer, SV_ThingArchiveId(mo->generator)); -#endif -} - #if !__JDOOM64__ void SV_TranslateLegacyMobjFlags(mobj_t *mo, int ver) { @@ -1127,344 +907,6 @@ void SV_TranslateLegacyMobjFlags(mobj_t *mo, int ver) } #endif -static void RestoreMobj(mobj_t *mo, int ver) -{ -#if __JDOOM64__ - DENG_UNUSED(ver); -#endif - - mo->info = &MOBJINFO[mo->type]; - - Mobj_SetState(mo, PTR2INT(mo->state)); -#if __JHEXEN__ - if(mo->flags2 & MF2_DORMANT) - mo->tics = -1; -#endif - - if(mo->player) - { - // The player number translation table is used to find out the - // *current* (actual) player number of the referenced player. - int pNum = saveToRealPlayerNum[PTR2INT(mo->player) - 1]; - -#if __JHEXEN__ - if(pNum < 0) - { - // This saved player does not exist in the current game! - // Destroy this mobj. - Z_Free(mo); - - return; // Don't add this thinker. - } -#endif - - mo->player = &players[pNum]; - mo->dPlayer = mo->player->plr; - - mo->dPlayer->mo = mo; - //mo->dPlayer->clAngle = mo->angle; /* $unifiedangles */ - mo->dPlayer->lookDir = 0; /* $unifiedangles */ - } - - mo->visAngle = mo->angle >> 16; - -#if !__JHEXEN__ - if(mo->dPlayer && !mo->dPlayer->inGame) - { - mo->dPlayer->mo = 0; - Mobj_Destroy(mo); - - return; - } -#endif - -#if !__JDOOM64__ - // Do we need to update this mobj's flag values? - if(ver < MOBJ_SAVEVERSION) - { - SV_TranslateLegacyMobjFlags(mo, ver); - } -#endif - - P_MobjLink(mo); - mo->floorZ = P_GetDoublep(Mobj_Sector(mo), DMU_FLOOR_HEIGHT); - mo->ceilingZ = P_GetDoublep(Mobj_Sector(mo), DMU_CEILING_HEIGHT); - - return; -} - -/** - * Always returns @c false as a thinker will have already been allocated in - * the mobj creation process. - */ -int SV_ReadMobj(thinker_t *th, MapStateReader *msr) -{ -#define FF_FULLBRIGHT 0x8000 ///< Used to be a flag in thing->frame. -#define FF_FRAMEMASK 0x7fff - - mobj_t *mo = (mobj_t *) th; - Reader *reader = msr->reader(); - - int ver = Reader_ReadByte(reader); - -#if !__JHEXEN__ - if(ver >= 2) // Version 2 has mobj archive numbers. - { - insertThingInArchive(mo, Reader_ReadInt16(reader)); - } -#endif - -#if !__JHEXEN__ - mo->target = 0; - if(ver >= 2) - { - mo->target = INT2PTR(mobj_t, Reader_ReadInt16(reader)); - } -#endif - -#if __JDOOM__ || __JDOOM64__ - mo->tracer = 0; - if(ver >= 5) - { - mo->tracer = INT2PTR(mobj_t, Reader_ReadInt16(reader)); - } -#endif - - mo->onMobj = 0; -#if __JHEXEN__ - if(ver >= 8) -#else - if(ver >= 5) -#endif - { - mo->onMobj = INT2PTR(mobj_t, Reader_ReadInt16(reader)); - } - - mo->origin[VX] = FIX2FLT(Reader_ReadInt32(reader)); - mo->origin[VY] = FIX2FLT(Reader_ReadInt32(reader)); - mo->origin[VZ] = FIX2FLT(Reader_ReadInt32(reader)); - mo->angle = Reader_ReadInt32(reader); - mo->sprite = Reader_ReadInt32(reader); - - mo->frame = Reader_ReadInt32(reader); // might be ORed with FF_FULLBRIGHT - if(mo->frame & FF_FULLBRIGHT) - mo->frame &= FF_FRAMEMASK; // not used anymore. - -#if __JHEXEN__ - if(ver < 6) - { - /*mo->floorflat =*/ Reader_ReadInt32(reader); - } -#else - mo->floorZ = FIX2FLT(Reader_ReadInt32(reader)); - mo->ceilingZ = FIX2FLT(Reader_ReadInt32(reader)); -#endif - - mo->radius = FIX2FLT(Reader_ReadInt32(reader)); - mo->height = FIX2FLT(Reader_ReadInt32(reader)); - mo->mom[MX] = FIX2FLT(Reader_ReadInt32(reader)); - mo->mom[MY] = FIX2FLT(Reader_ReadInt32(reader)); - mo->mom[MZ] = FIX2FLT(Reader_ReadInt32(reader)); - mo->valid = Reader_ReadInt32(reader); - mo->type = Reader_ReadInt32(reader); - -#if __JHEXEN__ - if(ver < 7) - { - /*mo->info = (mobjinfo_t *)*/ Reader_ReadInt32(reader); - } -#endif - mo->info = &MOBJINFO[mo->type]; - - if(mo->info->flags2 & MF2_FLOATBOB) - mo->mom[MZ] = 0; - - if(mo->info->flags & MF_SOLID) - mo->ddFlags |= DDMF_SOLID; - if(mo->info->flags2 & MF2_DONTDRAW) - mo->ddFlags |= DDMF_DONTDRAW; - - mo->tics = Reader_ReadInt32(reader); - mo->state = INT2PTR(state_t, Reader_ReadInt32(reader)); -#if __JHEXEN__ - mo->damage = Reader_ReadInt32(reader); -#endif - mo->flags = Reader_ReadInt32(reader); -#if __JHEXEN__ - mo->flags2 = Reader_ReadInt32(reader); - if(ver >= 5) - { - mo->flags3 = Reader_ReadInt32(reader); - } - mo->special1 = Reader_ReadInt32(reader); - mo->special2 = Reader_ReadInt32(reader); -#endif - mo->health = Reader_ReadInt32(reader); - -#if __JHERETIC__ - if(ver < 8) - { - // 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; - if(mo->type == MT_HORNRODFX2 && mo->special3 > 16) - mo->special3 = 16; - mo->health = MOBJINFO[mo->type].spawnHealth; - break; - - default: - break; - } - } -#endif - - mo->moveDir = Reader_ReadInt32(reader); - mo->moveCount = Reader_ReadInt32(reader); -#if __JHEXEN__ - mo->target = INT2PTR(mobj_t, Reader_ReadInt32(reader)); -#endif - mo->reactionTime = Reader_ReadInt32(reader); - mo->threshold = Reader_ReadInt32(reader); - mo->player = INT2PTR(player_t, Reader_ReadInt32(reader)); - mo->lastLook = Reader_ReadInt32(reader); - -#if __JHEXEN__ - mo->floorClip = FIX2FLT(Reader_ReadInt32(reader)); - insertThingInArchive(mo, Reader_ReadInt32(reader)); - mo->tid = Reader_ReadInt32(reader); -#else - // For nightmare respawn. - if(ver >= 6) - { - mo->spawnSpot.origin[VX] = FIX2FLT(Reader_ReadInt32(reader)); - mo->spawnSpot.origin[VY] = FIX2FLT(Reader_ReadInt32(reader)); - mo->spawnSpot.origin[VZ] = FIX2FLT(Reader_ReadInt32(reader)); - mo->spawnSpot.angle = Reader_ReadInt32(reader); - if(ver < 10) - { - /*mo->spawnSpot.type =*/ Reader_ReadInt32(reader); - } - mo->spawnSpot.flags = Reader_ReadInt32(reader); - } - else - { - mo->spawnSpot.origin[VX] = (float) Reader_ReadInt16(reader); - mo->spawnSpot.origin[VY] = (float) Reader_ReadInt16(reader); - mo->spawnSpot.origin[VZ] = 0; // Initialize with "something". - mo->spawnSpot.angle = (angle_t) (ANG45 * (Reader_ReadInt16(reader) / 45)); - /*mo->spawnSpot.type = (int)*/ Reader_ReadInt16(reader); - mo->spawnSpot.flags = (int) Reader_ReadInt16(reader); - } - -# if __JDOOM__ || __JDOOM64__ - if(ver >= 3) -# elif __JHERETIC__ - if(ver >= 5) -# endif - { - mo->intFlags = Reader_ReadInt32(reader); - mo->dropOffZ = FIX2FLT(Reader_ReadInt32(reader)); - mo->gear = Reader_ReadInt32(reader); - } - -# if __JDOOM__ || __JDOOM64__ - if(ver >= 6) - { - mo->damage = Reader_ReadInt32(reader); - mo->flags2 = Reader_ReadInt32(reader); - } - else // flags2 will be applied from the defs. - { - mo->damage = DDMAXINT; // Use the value set in mo->info->damage - } - -# elif __JHERETIC__ - mo->damage = Reader_ReadInt32(reader); - mo->flags2 = Reader_ReadInt32(reader); -# endif - - if(ver >= 7) - { - mo->flags3 = Reader_ReadInt32(reader); - } // Else flags3 will be applied from the defs. -#endif - -#if __JHEXEN__ - mo->special = Reader_ReadInt32(reader); - Reader_Read(reader, mo->args, 1 * 5); -#elif __JHERETIC__ - mo->special1 = Reader_ReadInt32(reader); - mo->special2 = Reader_ReadInt32(reader); - if(ver >= 8) - { - mo->special3 = Reader_ReadInt32(reader); - } -#endif - -#if __JHEXEN__ - if(ver >= 2) -#else - if(ver >= 4) -#endif - { - mo->translucency = Reader_ReadByte(reader); - } - -#if __JHEXEN__ - if(ver >= 3) -#else - if(ver >= 5) -#endif - { - mo->visTarget = (short) (Reader_ReadByte(reader)) -1; - } - -#if __JHEXEN__ - if(ver >= 4) - { - mo->tracer = INT2PTR(mobj_t, Reader_ReadInt32(reader)); - } - - if(ver >= 4) - { - mo->lastEnemy = INT2PTR(mobj_t, Reader_ReadInt32(reader)); - } -#else - if(ver >= 5) - { - mo->floorClip = FIX2FLT(Reader_ReadInt32(reader)); - } -#endif - -#if __JHERETIC__ - if(ver >= 7) - { - mo->generator = INT2PTR(mobj_t, Reader_ReadInt16(reader)); - } - else - { - mo->generator = 0; - } -#endif - - // Restore! (unmangle) - RestoreMobj(mo, ver); - - return false; - -#undef FF_FRAMEMASK -#undef FF_FULLBRIGHT -} - /** * Prepare and write the player header info. */ @@ -2936,7 +2378,7 @@ void SV_HxLoadHubMap() void SV_HxBackupPlayersInHub(playerbackup_t playerBackup[MAXPLAYERS]) { - DENG_ASSERT(playerBackup); + DENG_ASSERT(playerBackup != 0); for(int i = 0; i < MAXPLAYERS; ++i) { diff --git a/doomsday/plugins/common/src/thinkerinfo.cpp b/doomsday/plugins/common/src/thinkerinfo.cpp index e29025adce..2263586108 100644 --- a/doomsday/plugins/common/src/thinkerinfo.cpp +++ b/doomsday/plugins/common/src/thinkerinfo.cpp @@ -21,6 +21,7 @@ #include "common.h" #include "thinkerinfo.h" +#include "mobj.h" #include "p_saveg.h" #include "p_ceiling.h" #include "p_door.h" @@ -54,8 +55,8 @@ static ThinkerClassInfo thinkerInfo[] = { TC_MOBJ, (thinkfunc_t) P_MobjThinker, TSF_SERVERONLY, - (WriteThinkerFunc) SV_WriteMobj, - (ReadThinkerFunc) SV_ReadMobj, + de::function_cast(writeThinkerAs), + de::function_cast(readThinkerAs), sizeof(mobj_t) }, #if !__JHEXEN__ diff --git a/doomsday/plugins/doom/include/p_mobj.h b/doomsday/plugins/doom/include/p_mobj.h index 3559d2be44..e8829afe80 100644 --- a/doomsday/plugins/doom/include/p_mobj.h +++ b/doomsday/plugins/doom/include/p_mobj.h @@ -41,6 +41,10 @@ #include "doomdata.h" #include "info.h" #include "tables.h" +#ifdef __cplusplus +# include "mapstatereader.h" +# include "mapstatewriter.h" +#endif #define NOMOM_THRESHOLD (0.0001) // (integer) 0 #define DROPOFFMOM_THRESHOLD (0.25) // FRACUNIT/4 @@ -221,8 +225,13 @@ typedef struct mobj_s { int corpseTics; // $vanish: how long has this been dead? #ifdef __cplusplus - void write(Writer *writer) const; - int read(Reader *reader, int mapVersion); + void write(MapStateWriter *msw) const; + + /** + * Always returns @c false as a thinker will have already been allocated in + * the mobj creation process. + */ + int read(MapStateReader *msr); #endif } mobj_t; diff --git a/doomsday/plugins/doom64/include/p_mobj.h b/doomsday/plugins/doom64/include/p_mobj.h index a2682d96d6..d69bde94c3 100644 --- a/doomsday/plugins/doom64/include/p_mobj.h +++ b/doomsday/plugins/doom64/include/p_mobj.h @@ -42,6 +42,10 @@ #include "p_terraintype.h" #include "d_think.h" #include "info.h" +#ifdef __cplusplus +# include "mapstatereader.h" +# include "mapstatewriter.h" +#endif #define NOMOM_THRESHOLD (0.0001) // (integer) 0 #define DROPOFFMOM_THRESHOLD (0.25) // FRACUNIT/4 @@ -243,8 +247,13 @@ typedef struct mobj_s { int spawnFadeTics; #ifdef __cplusplus - void write(Writer *writer) const; - int read(Reader *reader, int mapVersion); + void write(MapStateWriter *msw) const; + + /** + * Always returns @c false as a thinker will have already been allocated in + * the mobj creation process. + */ + int read(MapStateReader *msr); #endif } mobj_t; diff --git a/doomsday/plugins/heretic/include/p_mobj.h b/doomsday/plugins/heretic/include/p_mobj.h index a089b4d072..b41335fbdf 100644 --- a/doomsday/plugins/heretic/include/p_mobj.h +++ b/doomsday/plugins/heretic/include/p_mobj.h @@ -38,6 +38,10 @@ #include "p_terraintype.h" #include "h_think.h" #include "info.h" +#ifdef __cplusplus +# include "mapstatereader.h" +# include "mapstatewriter.h" +#endif #define NOMOM_THRESHOLD (0.0001) // (integer) 0 #define DROPOFFMOM_THRESHOLD (0.25) // FRACUNIT/4 @@ -220,8 +224,13 @@ typedef struct mobj_s { int corpseTics; // $vanish: how long has this been dead? #ifdef __cplusplus - void write(Writer *writer) const; - int read(Reader *reader, int mapVersion); + void write(MapStateWriter *msw) const; + + /** + * Always returns @c false as a thinker will have already been allocated in + * the mobj creation process. + */ + int read(MapStateReader *msr); #endif } mobj_t; diff --git a/doomsday/plugins/hexen/include/p_mobj.h b/doomsday/plugins/hexen/include/p_mobj.h index a50a37192b..09942823f7 100644 --- a/doomsday/plugins/hexen/include/p_mobj.h +++ b/doomsday/plugins/hexen/include/p_mobj.h @@ -35,6 +35,10 @@ #include "doomsday.h" #include "p_terraintype.h" +#ifdef __cplusplus +# include "mapstatereader.h" +# include "mapstatewriter.h" +#endif #define NOMOM_THRESHOLD (0.0001) // (integer) 0 #define WALKSTOP_THRESHOLD (0.062484741) // FIX2FLT(0x1000-1) @@ -209,8 +213,13 @@ typedef struct mobj_s { struct mobj_s* lastEnemy; #ifdef __cplusplus - void write(Writer *writer) const; - int read(Reader *reader, int mapVersion); + void write(MapStateWriter *msw) const; + + /** + * Always returns @c false as a thinker will have already been allocated in + * the mobj creation process. + */ + int read(MapStateReader *msr); #endif } mobj_t;