diff --git a/src/dehacked.c b/src/dehacked.c index e3a90683..207b442f 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -6389,6 +6389,9 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_POKEY8", "S_POKEYIDLE", + "S_SHADOW", + "S_WHITESHADOW", + #ifdef SEENAMES "S_NAMECHECK", #endif @@ -6950,6 +6953,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_POKEY", // Huh, thought this was a default asset for some reason, guess not. "MT_ENEMYFLIP", "MT_WAYPOINT", + "MT_SHADOW", #ifdef SEENAMES "MT_NAMECHECK", diff --git a/src/info.c b/src/info.c index acaf5d3c..71711b68 100644 --- a/src/info.c +++ b/src/info.c @@ -58,7 +58,7 @@ char sprnames[NUMSPRITES + 1][5] = "SPRG","BSPR","RNDM","RPOP","KFRE","DRIF","DSMO","FITM","DFAK","BANA", "DBAN","GSHE","GSTR","DGSH","RSHE","RSTR","DRSH","BOMB","BLIG","LIGH", "SINK","SITR","LAKI","POKE","AUDI","DECO","DOOD","SNES","GBAS","SPRS", - "BUZB","CHOM","SACO","CRAB" + "BUZB","CHOM","SACO","CRAB", "SHAD" }; // Doesn't work with g++, needs actionf_p1 (don't modify this comment) @@ -2847,6 +2847,10 @@ state_t states[NUMSTATES] = {SPR_CRAB, 10, -1, {NULL}, 0, 0, S_NULL}, // S_LAMPPOST {SPR_CRAB, 11, -1, {NULL}, 0, 0, S_NULL}, // S_MOSSYTREE + // Fake Shadow + {SPR_SHAD, FF_TRANS50, -1, {NULL}, 0, 0, S_NULL}, // S_SHADOW + {SPR_SHAD, FF_FULLBRIGHT|FF_TRANS50|1, -1, {NULL}, 0, 0, S_NULL}, // S_WHITESHADOW + #ifdef SEENAMES {SPR_NULL, 0, 1, {NULL}, 0, 0, S_NULL}, // S_NAMECHECK #endif @@ -16479,6 +16483,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_SHADOW + -1, // doomednum + S_SHADOW, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 60*FRACUNIT, // speed + 50*FRACUNIT, // radius + 1*FRACUNIT, // height + -1, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOGRAVITY|MF_SCENERY, // flags + S_NULL // raisestate + }, + // ============================================================================================================================// #ifdef SEENAMES diff --git a/src/info.h b/src/info.h index 8378a16a..c6c448f0 100644 --- a/src/info.h +++ b/src/info.h @@ -615,6 +615,7 @@ typedef enum sprite SPR_CHOM, // Sapphire Coast Chomper SPR_SACO, // Sapphire Coast Fauna SPR_CRAB, // Crystal Abyss mobs + SPR_SHAD, // TD shadows SPR_FIRSTFREESLOT, SPR_LASTFREESLOT = SPR_FIRSTFREESLOT + NUMSPRITEFREESLOTS - 1, @@ -3360,6 +3361,8 @@ typedef enum state S_FLYINGGARG8, S_LAMPPOST, S_MOSSYTREE, + S_SHADOW, + S_WHITESHADOW, #ifdef SEENAMES S_NAMECHECK, @@ -3996,6 +3999,7 @@ typedef enum mobj_type MT_FLYINGGARG, MT_LAMPPOST, MT_MOSSYTREE, + MT_SHADOW, #ifdef SEENAMES MT_NAMECHECK, diff --git a/src/p_local.h b/src/p_local.h index f81053ee..ca6d39d0 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -204,6 +204,8 @@ void P_RespawnSpecials(void); mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type); +mobj_t *P_SpawnShadowMobj(mobj_t * caster); + void P_RecalcPrecipInSector(sector_t *sector); void P_PrecipitationEffects(void); diff --git a/src/p_mobj.c b/src/p_mobj.c index 497633a5..baa38042 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -8274,6 +8274,9 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) break; } + if (mobj->type == MT_PLAYER) + P_SpawnShadowMobj(mobj); + if (!(mobj->flags & MF_NOTHINK)) P_AddThinker(&mobj->thinker); @@ -8309,6 +8312,115 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) return mobj; } +// +// P_SpawnShadowMobj +// warning: Do not send a shadow mobj as a caster into here, or try to spawn spawn shadows for shadows in P_SpawnMobj, we do not want recursive shadows +// +mobj_t *P_SpawnShadowMobj(mobj_t * caster) +{ + const mobjinfo_t *info = &mobjinfo[MT_SHADOW]; + state_t *st; + mobj_t *mobj = Z_Calloc(sizeof (*mobj), PU_LEVEL, NULL); + + // this is officially a mobj, declared as soon as possible. + mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker; + mobj->type = MT_SHADOW; + mobj->info = info; + + mobj->x = caster->x; + mobj->y = caster->y; + + mobj->radius = info->radius; + mobj->height = info->height; + mobj->flags = info->flags; + + mobj->health = info->spawnhealth; + + mobj->reactiontime = info->reactiontime; + + mobj->lastlook = -1; // stuff moved in P_enemy.P_LookForPlayer + + // do not set the state with P_SetMobjState, + // because action routines can not be called yet + if (caster->frame & FF_FULLBRIGHT) + st = &states[S_WHITESHADOW]; + else + st = &states[info->spawnstate]; + + mobj->state = st; + mobj->tics = st->tics; + mobj->sprite = st->sprite; + mobj->frame = st->frame; // FF_FRAMEMASK for frame, and other bits.. + + mobj->friction = ORIG_FRICTION; + + mobj->movefactor = ORIG_FRICTION_FACTOR; + + // All mobjs are created at 100% scale. + mobj->scale = FRACUNIT; + mobj->destscale = mobj->scale; + mobj->scalespeed = FRACUNIT/12; + + // TODO: Make this a special map header + if ((maptol & TOL_ERZ3) && !(mobj->type == MT_BLACKEGGMAN)) + mobj->destscale = FRACUNIT/2; + + // set subsector and/or block links + P_SetThingPosition(mobj); + I_Assert(mobj->subsector != NULL); + + // Make sure scale matches destscale immediately when spawned + P_SetScale(mobj, mobj->destscale); + + mobj->floorz = mobj->subsector->sector->floorheight; + mobj->ceilingz = mobj->subsector->sector->ceilingheight; + + // Tells MobjCheckWater that the water height was not set. + mobj->watertop = INT32_MAX; + + mobj->z = mobj->floorz; + + // defaults onground + if (mobj->z == mobj->floorz) + mobj->eflags |= MFE_ONGROUND; + + if (!(mobj->flags & MF_NOTHINK)) + P_AddThinker(&mobj->thinker); + + // Call action functions when the state is set + if (st->action.acp1 && (mobj->flags & MF_RUNSPAWNFUNC)) + { + if (levelloading) + { + // Cache actions in a linked list + // with function pointer, and + // var1 & var2, which will be executed + // when the level finishes loading. + P_AddCachedAction(mobj, mobj->info->spawnstate); + } + else + { + var1 = st->var1; + var2 = st->var2; +#ifdef HAVE_BLUA + astate = st; +#endif + st->action.acp1(mobj); + // DANGER! This is the ONLY way for P_SpawnMobj to return NULL! + // Avoid using MF_RUNSPAWNFUNC on mobjs whose spawn state expects target or tracer to already be set! + if (P_MobjWasRemoved(mobj)) + return NULL; + } + } + + if (CheckForReverseGravity && !(mobj->flags & MF_NOBLOCKMAP)) + P_CheckGravity(mobj, false); + + P_SetTarget(&mobj->target, caster); // set the shadow's caster as the target + + return mobj; +} + static precipmobj_t *P_SpawnPrecipMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) { state_t *st;