diff --git a/src/p_polyobj.c b/src/p_polyobj.c index fd3237c9d..c2b3ba399 100644 --- a/src/p_polyobj.c +++ b/src/p_polyobj.c @@ -2853,6 +2853,165 @@ INT32 EV_DoPolyObjFlag(line_t *pfdata) return 1; } +void T_PolyObjFade(polyfade_t *th) +{ + boolean stillfading = false; + polyobj_t *po = Polyobj_GetForNum(th->polyObjNum); + + if (!po) +#ifdef RANGECHECK + I_Error("T_PolyObjFade: thinker has invalid id %d\n", th->polyObjNum); +#else + { + CONS_Debug(DBG_POLYOBJ, "T_PolyObjFade: thinker with invalid id %d removed.\n", th->polyObjNum); + P_RemoveThinkerDelayed(&th->thinker); + return; + } +#endif + + // check for displacement due to override and reattach when possible + if (po->thinker == NULL) + po->thinker = &th->thinker; + + stillfading = th->ticbased ? !(--(th->timer) <= 0) + : !((th->timer -= th->duration) <= 0); + + if (th->timer <= 0) + { + po->translucency = max(min(th->destvalue, NUMTRANSMAPS), 0); + + // remove thinker + if (po->thinker == &th->thinker) + po->thinker = NULL; + P_RemoveThinker(&th->thinker); + } + else + { + INT16 delta = abs(th->destvalue - th->sourcevalue); + INT32 duration = th->ticbased ? th->duration + : abs(FixedMul(FixedDiv(256, NUMTRANSMAPS), NUMTRANSMAPS - th->destvalue) + - FixedMul(FixedDiv(256, NUMTRANSMAPS), NUMTRANSMAPS - th->sourcevalue)); // speed-based internal counter duration: delta in 256 scale + fixed_t factor = min(FixedDiv(duration - th->timer, duration), 1*FRACUNIT); + if (th->destvalue < th->sourcevalue) + po->translucency = max(min(po->translucency, th->sourcevalue - (INT16)FixedMul(delta, factor)), th->destvalue); + else if (th->destvalue > th->sourcevalue) + po->translucency = min(max(po->translucency, th->sourcevalue + (INT16)FixedMul(delta, factor)), th->destvalue); + } + + if (!stillfading) + { + // set render flags + if (po->translucency >= NUMTRANSMAPS) // invisible + po->flags &= ~POF_RENDERALL; + else + po->flags |= (po->spawnflags & POF_RENDERALL); + + // set collision + if (th->docollision) + { + if (th->destvalue > th->sourcevalue) // faded out + { + po->flags &= ~POF_SOLID; + po->flags |= POF_NOSPECIALS; + } + else + { + po->flags |= (po->spawnflags & POF_SOLID); + if (!(po->spawnflags & POF_NOSPECIALS)) + po->flags &= ~POF_NOSPECIALS; + } + } + } + else + { + if (po->translucency >= NUMTRANSMAPS) + // HACK: OpenGL renders fully opaque when >= NUMTRANSMAPS + po->translucency = NUMTRANSMAPS-1; + + po->flags |= (po->spawnflags & POF_RENDERALL); + + // set collision + if (th->docollision) + { + if (th->doghostfade) + { + po->flags &= ~POF_SOLID; + po->flags |= POF_NOSPECIALS; + } + else + { + po->flags |= (po->spawnflags & POF_SOLID); + if (!(po->spawnflags & POF_NOSPECIALS)) + po->flags &= ~POF_NOSPECIALS; + } + } + } +} + +INT32 EV_DoPolyObjFade(polyfadedata_t *pfdata) +{ + polyobj_t *po; + polyobj_t *oldpo; + polyfade_t *th; + INT32 start; + + if (!(po = Polyobj_GetForNum(pfdata->polyObjNum))) + { + CONS_Debug(DBG_POLYOBJ, "EV_DoPolyObjFade: bad polyobj %d\n", pfdata->polyObjNum); + return 0; + } + + // don't allow line actions to affect bad polyobjects + if (po->isBad) + return 0; + + // already equal, nothing to do + if (po->translucency == pfdata->destvalue) + return 1; + + if (po->thinker && po->thinker->function.acp1 == (actionf_p1)T_PolyObjFade) + P_RemoveThinker(po->thinker); + + // create a new thinker + th = Z_Malloc(sizeof(polyfade_t), PU_LEVSPEC, NULL); + th->thinker.function.acp1 = (actionf_p1)T_PolyObjFade; + PolyObj_AddThinker(&th->thinker); + po->thinker = &th->thinker; + + // set fields + th->polyObjNum = pfdata->polyObjNum; + th->sourcevalue = po->translucency; + th->destvalue = pfdata->destvalue; + th->docollision = pfdata->docollision; + th->doghostfade = pfdata->doghostfade; + + if (pfdata->ticbased) + { + th->ticbased = true; + th->timer = th->duration = abs(pfdata->speed); // pfdata->speed is duration + } + else + { + th->ticbased = false; + th->timer = abs(FixedMul(FixedDiv(256, NUMTRANSMAPS), NUMTRANSMAPS - th->destvalue) + - FixedMul(FixedDiv(256, NUMTRANSMAPS), NUMTRANSMAPS - th->sourcevalue)); // delta converted to 256 scale, use as internal counter + th->duration = abs(pfdata->speed); // use th->duration as speed decrement + } + + oldpo = po; + + // apply action to mirroring polyobjects as well + start = 0; + while ((po = Polyobj_GetChild(oldpo, &start))) + { + pfdata->polyObjNum = po->id; + EV_DoPolyObjFade(pfdata); + } + + // action was successful + return 1; +} + #endif // ifdef POLYOBJECTS // EOF diff --git a/src/p_polyobj.h b/src/p_polyobj.h index c9838a922..524518f2a 100644 --- a/src/p_polyobj.h +++ b/src/p_polyobj.h @@ -207,6 +207,20 @@ typedef struct polydisplace_s fixed_t oldHeights; } polydisplace_t; +typedef struct polyfade_s +{ + thinker_t thinker; // must be first + + INT32 polyObjNum; + INT32 sourcevalue; + INT32 destvalue; + boolean docollision; + boolean doghostfade; + boolean ticbased; + INT32 duration; + INT32 timer; +} polyfade_t; + // // Line Activation Data Structures // @@ -266,6 +280,16 @@ typedef struct polydisplacedata_s fixed_t dy; } polydisplacedata_t; +typedef struct polyfadedata_s +{ + INT32 polyObjNum; + INT32 destvalue; + boolean docollision; + boolean doghostfade; + boolean ticbased; + INT32 speed; +} polyfadedata_t; + // // Functions // @@ -287,6 +311,7 @@ void T_PolyDoorSlide(polyslidedoor_t *); void T_PolyDoorSwing(polyswingdoor_t *); void T_PolyObjDisplace (polydisplace_t *); void T_PolyObjFlag (polymove_t *); +void T_PolyObjFade (polyfade_t *); INT32 EV_DoPolyDoor(polydoordata_t *); INT32 EV_DoPolyObjMove(polymovedata_t *); @@ -294,6 +319,7 @@ INT32 EV_DoPolyObjWaypoint(polywaypointdata_t *); INT32 EV_DoPolyObjRotate(polyrotdata_t *); INT32 EV_DoPolyObjDisplace(polydisplacedata_t *); INT32 EV_DoPolyObjFlag(struct line_s *); +INT32 EV_DoPolyObjFade(polyfadedata_t *); // diff --git a/src/p_saveg.c b/src/p_saveg.c index 020a5a272..677e995ce 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -1298,6 +1298,7 @@ typedef enum tc_polyswingdoor, tc_polyflag, tc_polydisplace, + tc_polyfade, #endif tc_end } specials_e; @@ -1984,6 +1985,20 @@ static void SavePolydisplaceThinker(const thinker_t *th, const UINT8 type) WRITEFIXED(save_p, ht->oldHeights); } +static void SavePolyfadeThinker(const thinker_t *th, const UINT8 type) +{ + const polyfade_t *ht = (const void *)th; + WRITEUINT8(save_p, type); + WRITEINT32(save_p, ht->polyObjNum); + WRITEINT32(save_p, ht->sourcevalue); + WRITEINT32(save_p, ht->destvalue); + WRITEUINT8(save_p, (UINT8)ht->docollision); + WRITEUINT8(save_p, (UINT8)ht->doghostfade); + WRITEUINT8(save_p, (UINT8)ht->ticbased); + WRITEINT32(save_p, ht->duration); + WRITEINT32(save_p, ht->timer); +} + #endif /* // @@ -2208,6 +2223,11 @@ static void P_NetArchiveThinkers(void) SavePolydisplaceThinker(th, tc_polydisplace); continue; } + else if (th->function.acp1 == (actionf_p1)T_PolyObjFade) + { + SavePolyfadeThinker(th, tc_polyfade); + continue; + } #endif #ifdef PARANOIA else if (th->function.acv != P_RemoveThinkerDelayed) // wait garbage collection @@ -2985,6 +3005,26 @@ static inline void LoadPolydisplaceThinker(actionf_p1 thinker) ht->oldHeights = READFIXED(save_p); P_AddThinker(&ht->thinker); } + +// +// LoadPolyfadeThinker +// +// Loads a polyfadet_t thinker +// +static void LoadPolyfadeThinker(actionf_p1 thinker) +{ + polyfade_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL); + ht->thinker.function.acp1 = thinker; + ht->polyObjNum = READINT32(save_p); + ht->sourcevalue = READINT32(save_p); + ht->destvalue = READINT32(save_p); + ht->docollision = (boolean)READUINT8(save_p); + ht->doghostfade = (boolean)READUINT8(save_p); + ht->ticbased = (boolean)READUINT8(save_p); + ht->duration = READINT32(save_p); + ht->timer = READINT32(save_p); + P_AddThinker(&ht->thinker); +} #endif /* @@ -3186,6 +3226,10 @@ static void P_NetUnArchiveThinkers(void) case tc_polydisplace: LoadPolydisplaceThinker((actionf_p1)T_PolyObjDisplace); break; + + case tc_polyfade: + LoadPolyfadeThinker((actionf_p1)T_PolyObjFade); + break; #endif case tc_scroll: LoadScrollThinker((actionf_p1)T_Scroll); diff --git a/src/p_spec.c b/src/p_spec.c index 3b980211f..56ef9daa8 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -1218,7 +1218,7 @@ static void PolyVisible(line_t *line) po->flags |= POF_SOLID; po->flags &= ~POF_NOSPECIALS; - po->flags |= POF_RENDERALL; + po->flags |= (po->spawnflags & POF_RENDERALL); } // @@ -1242,7 +1242,94 @@ static void PolyTranslucency(line_t *line) if (po->isBad) return; - po->translucency = (line->frontsector->floorheight >> FRACBITS) / 100; + // if DONTPEGBOTTOM, specify raw translucency value in Front X Offset + // else, take it out of 1000. If Front X Offset is specified, use that. Else, use floorheight. + if (line->flags & ML_EFFECT3) // relative calc + po->translucency = max(min(po->translucency + ((line->flags & ML_DONTPEGBOTTOM) ? + (sides[line->sidenum[0]].textureoffset ? + max(min(sides[line->sidenum[0]].textureoffset>>FRACBITS, NUMTRANSMAPS), -NUMTRANSMAPS) + : max(min(line->frontsector->floorheight>>FRACBITS, NUMTRANSMAPS), -NUMTRANSMAPS)) + : (sides[line->sidenum[0]].textureoffset ? + max(min(sides[line->sidenum[0]].textureoffset>>FRACBITS, 1000), -1000) / 100 + : max(min(line->frontsector->floorheight>>FRACBITS, 1000), -1000) / 100)), + NUMTRANSMAPS), 0); + else + po->translucency = (line->flags & ML_DONTPEGBOTTOM) ? + (sides[line->sidenum[0]].textureoffset ? + max(min(sides[line->sidenum[0]].textureoffset>>FRACBITS, NUMTRANSMAPS), 0) + : max(min(line->frontsector->floorheight>>FRACBITS, NUMTRANSMAPS), 0)) + : (sides[line->sidenum[0]].textureoffset ? + max(min(sides[line->sidenum[0]].textureoffset>>FRACBITS, 1000), 0) / 100 + : max(min(line->frontsector->floorheight>>FRACBITS, 1000), 0) / 100); +} + +// +// PolyFade +// +// Makes a polyobject translucency fade and applies tangibility +// +static boolean PolyFade(line_t *line) +{ + INT32 polyObjNum = line->tag; + polyobj_t *po; + polyfadedata_t pfd; + + if (!(po = Polyobj_GetForNum(polyObjNum))) + { + CONS_Debug(DBG_POLYOBJ, "PolyFade: bad polyobj %d\n", polyObjNum); + return 0; + } + + // don't allow line actions to affect bad polyobjects + if (po->isBad) + return 0; + + // Prevent continuous execs from interfering on an existing fade + if (!(line->flags & ML_EFFECT5) + && po->thinker + && po->thinker->function.acp1 == (actionf_p1)T_PolyObjFade) + { + CONS_Debug(DBG_POLYOBJ, "Line type 492 Executor: Fade PolyObject thinker already exists\n"); + return 0; + } + + pfd.polyObjNum = polyObjNum; + + // if DONTPEGBOTTOM, specify raw translucency value in Front X Offset + // else, take it out of 1000. If Front X Offset is specified, use that. Else, use floorheight. + if (line->flags & ML_EFFECT3) // relative calc + pfd.destvalue = max(min(po->translucency + ((line->flags & ML_DONTPEGBOTTOM) ? + (sides[line->sidenum[0]].textureoffset ? + max(min(sides[line->sidenum[0]].textureoffset>>FRACBITS, NUMTRANSMAPS), -NUMTRANSMAPS) + : max(min(line->frontsector->floorheight>>FRACBITS, NUMTRANSMAPS), -NUMTRANSMAPS)) + : (sides[line->sidenum[0]].textureoffset ? + max(min(sides[line->sidenum[0]].textureoffset>>FRACBITS, 1000), -1000) / 100 + : max(min(line->frontsector->floorheight>>FRACBITS, 1000), -1000) / 100)), + NUMTRANSMAPS), 0); + else + pfd.destvalue = (line->flags & ML_DONTPEGBOTTOM) ? + (sides[line->sidenum[0]].textureoffset ? + max(min(sides[line->sidenum[0]].textureoffset>>FRACBITS, NUMTRANSMAPS), 0) + : max(min(line->frontsector->floorheight>>FRACBITS, NUMTRANSMAPS), 0)) + : (sides[line->sidenum[0]].textureoffset ? + max(min(sides[line->sidenum[0]].textureoffset>>FRACBITS, 1000), 0) / 100 + : max(min(line->frontsector->floorheight>>FRACBITS, 1000), 0) / 100); + + // already equal, nothing to do + if (po->translucency == pfd.destvalue) + return 1; + + pfd.docollision = !(line->flags & ML_BOUNCY); // do not handle collision flags + pfd.doghostfade = (line->flags & ML_EFFECT1); // do ghost fade (no collision flags during fade) + pfd.ticbased = (line->flags & ML_EFFECT4); // Speed = Tic Duration + + // allow Back Y Offset to be consistent with other fade specials + pfd.speed = (line->sidenum[1] != 0xFFFF && !sides[line->sidenum[0]].rowoffset) ? + abs(sides[line->sidenum[1]].rowoffset>>FRACBITS) + : abs(sides[line->sidenum[0]].rowoffset>>FRACBITS); + + + return EV_DoPolyObjFade(&pfd); } // @@ -3355,6 +3442,9 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) case 491: PolyTranslucency(line); break; + case 492: + PolyFade(line); + break; #endif default: