diff options
Diffstat (limited to 'src/hexen/po_man.c')
-rw-r--r-- | src/hexen/po_man.c | 1517 |
1 files changed, 1517 insertions, 0 deletions
diff --git a/src/hexen/po_man.c b/src/hexen/po_man.c new file mode 100644 index 00000000..50b79518 --- /dev/null +++ b/src/hexen/po_man.c @@ -0,0 +1,1517 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 1993-1996 Id Software, Inc. +// Copyright(C) 1993-2008 Raven Software +// Copyright(C) 2008 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +//----------------------------------------------------------------------------- + + +// HEADER FILES ------------------------------------------------------------ + +#include "h2def.h" +#include "i_system.h" +#include "m_bbox.h" +#include "i_swap.h" +#include "p_local.h" +#include "r_local.h" + +// MACROS ------------------------------------------------------------------ + +#define PO_MAXPOLYSEGS 64 + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +boolean PO_MovePolyobj(int num, int x, int y); +boolean PO_RotatePolyobj(int num, angle_t angle); +void PO_Init(int lump); + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static polyobj_t *GetPolyobj(int polyNum); +static int GetPolyobjMirror(int poly); +static void ThrustMobj(mobj_t * mobj, seg_t * seg, polyobj_t * po); +static void UpdateSegBBox(seg_t * seg); +static void RotatePt(int an, fixed_t * x, fixed_t * y, fixed_t startSpotX, + fixed_t startSpotY); +static void UnLinkPolyobj(polyobj_t * po); +static void LinkPolyobj(polyobj_t * po); +static boolean CheckMobjBlocking(seg_t * seg, polyobj_t * po); +static void InitBlockMap(void); +static void IterFindPolySegs(int x, int y, seg_t ** segList); +static void SpawnPolyobj(int index, int tag, boolean crush); +static void TranslateToStartSpot(int tag, int originX, int originY); + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +extern seg_t *segs; + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +polyblock_t **PolyBlockMap; +polyobj_t *polyobjs; // list of all poly-objects on the level +int po_NumPolyobjs; + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static int PolySegCount; +static fixed_t PolyStartX; +static fixed_t PolyStartY; + +// CODE -------------------------------------------------------------------- + +// ===== Polyobj Event Code ===== + +//========================================================================== +// +// T_RotatePoly +// +//========================================================================== + +void T_RotatePoly(polyevent_t * pe) +{ + int absSpeed; + polyobj_t *poly; + + if (PO_RotatePolyobj(pe->polyobj, pe->speed)) + { + absSpeed = abs(pe->speed); + + if (pe->dist == -1) + { // perpetual polyobj + return; + } + pe->dist -= absSpeed; + if (pe->dist <= 0) + { + poly = GetPolyobj(pe->polyobj); + if (poly->specialdata == pe) + { + poly->specialdata = NULL; + } + SN_StopSequence((mobj_t *) & poly->startSpot); + P_PolyobjFinished(poly->tag); + P_RemoveThinker(&pe->thinker); + } + if (pe->dist < absSpeed) + { + pe->speed = pe->dist * (pe->speed < 0 ? -1 : 1); + } + } +} + +//========================================================================== +// +// EV_RotatePoly +// +//========================================================================== + +boolean EV_RotatePoly(line_t * line, byte * args, int direction, boolean + overRide) +{ + int mirror; + int polyNum; + polyevent_t *pe; + polyobj_t *poly; + + polyNum = args[0]; + poly = GetPolyobj(polyNum); + if (poly != NULL) + { + if (poly->specialdata && !overRide) + { // poly is already moving + return false; + } + } + else + { + I_Error("EV_RotatePoly: Invalid polyobj num: %d\n", polyNum); + } + pe = Z_Malloc(sizeof(polyevent_t), PU_LEVSPEC, 0); + P_AddThinker(&pe->thinker); + pe->thinker.function = T_RotatePoly; + pe->polyobj = polyNum; + if (args[2]) + { + if (args[2] == 255) + { + pe->dist = -1; + } + else + { + pe->dist = args[2] * (ANG90 / 64); // Angle + } + } + else + { + pe->dist = ANG_MAX - 1; + } + pe->speed = (args[1] * direction * (ANG90 / 64)) >> 3; + poly->specialdata = pe; + SN_StartSequence((mobj_t *) & poly->startSpot, SEQ_DOOR_STONE + + poly->seqType); + + while ((mirror = GetPolyobjMirror(polyNum)) != 0) + { + poly = GetPolyobj(mirror); + if (poly && poly->specialdata && !overRide) + { // mirroring poly is already in motion + break; + } + pe = Z_Malloc(sizeof(polyevent_t), PU_LEVSPEC, 0); + P_AddThinker(&pe->thinker); + pe->thinker.function = T_RotatePoly; + poly->specialdata = pe; + pe->polyobj = mirror; + if (args[2]) + { + if (args[2] == 255) + { + pe->dist = -1; + } + else + { + pe->dist = args[2] * (ANG90 / 64); // Angle + } + } + else + { + pe->dist = ANG_MAX - 1; + } + poly = GetPolyobj(polyNum); + if (poly != NULL) + { + poly->specialdata = pe; + } + else + { + I_Error("EV_RotatePoly: Invalid polyobj num: %d\n", polyNum); + } + direction = -direction; + pe->speed = (args[1] * direction * (ANG90 / 64)) >> 3; + polyNum = mirror; + SN_StartSequence((mobj_t *) & poly->startSpot, SEQ_DOOR_STONE + + poly->seqType); + } + return true; +} + +//========================================================================== +// +// T_MovePoly +// +//========================================================================== + +void T_MovePoly(polyevent_t * pe) +{ + int absSpeed; + polyobj_t *poly; + + if (PO_MovePolyobj(pe->polyobj, pe->xSpeed, pe->ySpeed)) + { + absSpeed = abs(pe->speed); + pe->dist -= absSpeed; + if (pe->dist <= 0) + { + poly = GetPolyobj(pe->polyobj); + if (poly->specialdata == pe) + { + poly->specialdata = NULL; + } + SN_StopSequence((mobj_t *) & poly->startSpot); + P_PolyobjFinished(poly->tag); + P_RemoveThinker(&pe->thinker); + } + if (pe->dist < absSpeed) + { + pe->speed = pe->dist * (pe->speed < 0 ? -1 : 1); + pe->xSpeed = FixedMul(pe->speed, finecosine[pe->angle]); + pe->ySpeed = FixedMul(pe->speed, finesine[pe->angle]); + } + } +} + +//========================================================================== +// +// EV_MovePoly +// +//========================================================================== + +boolean EV_MovePoly(line_t * line, byte * args, boolean timesEight, boolean + overRide) +{ + int mirror; + int polyNum; + polyevent_t *pe; + polyobj_t *poly; + angle_t an; + + polyNum = args[0]; + poly = GetPolyobj(polyNum); + if (poly != NULL) + { + if (poly->specialdata && !overRide) + { // poly is already moving + return false; + } + } + else + { + I_Error("EV_MovePoly: Invalid polyobj num: %d\n", polyNum); + } + pe = Z_Malloc(sizeof(polyevent_t), PU_LEVSPEC, 0); + P_AddThinker(&pe->thinker); + pe->thinker.function = T_MovePoly; + pe->polyobj = polyNum; + if (timesEight) + { + pe->dist = args[3] * 8 * FRACUNIT; + } + else + { + pe->dist = args[3] * FRACUNIT; // Distance + } + pe->speed = args[1] * (FRACUNIT / 8); + poly->specialdata = pe; + + an = args[2] * (ANG90 / 64); + + pe->angle = an >> ANGLETOFINESHIFT; + pe->xSpeed = FixedMul(pe->speed, finecosine[pe->angle]); + pe->ySpeed = FixedMul(pe->speed, finesine[pe->angle]); + SN_StartSequence((mobj_t *) & poly->startSpot, SEQ_DOOR_STONE + + poly->seqType); + + while ((mirror = GetPolyobjMirror(polyNum)) != 0) + { + poly = GetPolyobj(mirror); + if (poly && poly->specialdata && !overRide) + { // mirroring poly is already in motion + break; + } + pe = Z_Malloc(sizeof(polyevent_t), PU_LEVSPEC, 0); + P_AddThinker(&pe->thinker); + pe->thinker.function = T_MovePoly; + pe->polyobj = mirror; + poly->specialdata = pe; + if (timesEight) + { + pe->dist = args[3] * 8 * FRACUNIT; + } + else + { + pe->dist = args[3] * FRACUNIT; // Distance + } + pe->speed = args[1] * (FRACUNIT / 8); + an = an + ANG180; // reverse the angle + pe->angle = an >> ANGLETOFINESHIFT; + pe->xSpeed = FixedMul(pe->speed, finecosine[pe->angle]); + pe->ySpeed = FixedMul(pe->speed, finesine[pe->angle]); + polyNum = mirror; + SN_StartSequence((mobj_t *) & poly->startSpot, SEQ_DOOR_STONE + + poly->seqType); + } + return true; +} + +//========================================================================== +// +// T_PolyDoor +// +//========================================================================== + +void T_PolyDoor(polydoor_t * pd) +{ + int absSpeed; + polyobj_t *poly; + + if (pd->tics) + { + if (!--pd->tics) + { + poly = GetPolyobj(pd->polyobj); + SN_StartSequence((mobj_t *) & poly->startSpot, SEQ_DOOR_STONE + + poly->seqType); + } + return; + } + switch (pd->type) + { + case PODOOR_SLIDE: + if (PO_MovePolyobj(pd->polyobj, pd->xSpeed, pd->ySpeed)) + { + absSpeed = abs(pd->speed); + pd->dist -= absSpeed; + if (pd->dist <= 0) + { + poly = GetPolyobj(pd->polyobj); + SN_StopSequence((mobj_t *) & poly->startSpot); + if (!pd->close) + { + pd->dist = pd->totalDist; + pd->close = true; + pd->tics = pd->waitTics; + pd->direction = (ANG_MAX >> ANGLETOFINESHIFT) - + pd->direction; + pd->xSpeed = -pd->xSpeed; + pd->ySpeed = -pd->ySpeed; + } + else + { + if (poly->specialdata == pd) + { + poly->specialdata = NULL; + } + P_PolyobjFinished(poly->tag); + P_RemoveThinker(&pd->thinker); + } + } + } + else + { + poly = GetPolyobj(pd->polyobj); + if (poly->crush || !pd->close) + { // continue moving if the poly is a crusher, or is opening + return; + } + else + { // open back up + pd->dist = pd->totalDist - pd->dist; + pd->direction = (ANG_MAX >> ANGLETOFINESHIFT) - + pd->direction; + pd->xSpeed = -pd->xSpeed; + pd->ySpeed = -pd->ySpeed; + pd->close = false; + SN_StartSequence((mobj_t *) & poly->startSpot, + SEQ_DOOR_STONE + poly->seqType); + } + } + break; + case PODOOR_SWING: + if (PO_RotatePolyobj(pd->polyobj, pd->speed)) + { + absSpeed = abs(pd->speed); + if (pd->dist == -1) + { // perpetual polyobj + return; + } + pd->dist -= absSpeed; + if (pd->dist <= 0) + { + poly = GetPolyobj(pd->polyobj); + SN_StopSequence((mobj_t *) & poly->startSpot); + if (!pd->close) + { + pd->dist = pd->totalDist; + pd->close = true; + pd->tics = pd->waitTics; + pd->speed = -pd->speed; + } + else + { + if (poly->specialdata == pd) + { + poly->specialdata = NULL; + } + P_PolyobjFinished(poly->tag); + P_RemoveThinker(&pd->thinker); + } + } + } + else + { + poly = GetPolyobj(pd->polyobj); + if (poly->crush || !pd->close) + { // continue moving if the poly is a crusher, or is opening + return; + } + else + { // open back up and rewait + pd->dist = pd->totalDist - pd->dist; + pd->speed = -pd->speed; + pd->close = false; + SN_StartSequence((mobj_t *) & poly->startSpot, + SEQ_DOOR_STONE + poly->seqType); + } + } + break; + default: + break; + } +} + +//========================================================================== +// +// EV_OpenPolyDoor +// +//========================================================================== + +boolean EV_OpenPolyDoor(line_t * line, byte * args, podoortype_t type) +{ + int mirror; + int polyNum; + polydoor_t *pd; + polyobj_t *poly; + angle_t an = 0; + + polyNum = args[0]; + poly = GetPolyobj(polyNum); + if (poly != NULL) + { + if (poly->specialdata) + { // poly is already moving + return false; + } + } + else + { + I_Error("EV_OpenPolyDoor: Invalid polyobj num: %d\n", polyNum); + } + pd = Z_Malloc(sizeof(polydoor_t), PU_LEVSPEC, 0); + memset(pd, 0, sizeof(polydoor_t)); + P_AddThinker(&pd->thinker); + pd->thinker.function = T_PolyDoor; + pd->type = type; + pd->polyobj = polyNum; + if (type == PODOOR_SLIDE) + { + pd->waitTics = args[4]; + pd->speed = args[1] * (FRACUNIT / 8); + pd->totalDist = args[3] * FRACUNIT; // Distance + pd->dist = pd->totalDist; + an = args[2] * (ANG90 / 64); + pd->direction = an >> ANGLETOFINESHIFT; + pd->xSpeed = FixedMul(pd->speed, finecosine[pd->direction]); + pd->ySpeed = FixedMul(pd->speed, finesine[pd->direction]); + SN_StartSequence((mobj_t *) & poly->startSpot, SEQ_DOOR_STONE + + poly->seqType); + } + else if (type == PODOOR_SWING) + { + pd->waitTics = args[3]; + pd->direction = 1; // ADD: PODOOR_SWINGL, PODOOR_SWINGR + pd->speed = (args[1] * pd->direction * (ANG90 / 64)) >> 3; + pd->totalDist = args[2] * (ANG90 / 64); + pd->dist = pd->totalDist; + SN_StartSequence((mobj_t *) & poly->startSpot, SEQ_DOOR_STONE + + poly->seqType); + } + + poly->specialdata = pd; + + while ((mirror = GetPolyobjMirror(polyNum)) != 0) + { + poly = GetPolyobj(mirror); + if (poly && poly->specialdata) + { // mirroring poly is already in motion + break; + } + pd = Z_Malloc(sizeof(polydoor_t), PU_LEVSPEC, 0); + memset(pd, 0, sizeof(polydoor_t)); + P_AddThinker(&pd->thinker); + pd->thinker.function = T_PolyDoor; + pd->polyobj = mirror; + pd->type = type; + poly->specialdata = pd; + if (type == PODOOR_SLIDE) + { + pd->waitTics = args[4]; + pd->speed = args[1] * (FRACUNIT / 8); + pd->totalDist = args[3] * FRACUNIT; // Distance + pd->dist = pd->totalDist; + an = an + ANG180; // reverse the angle + pd->direction = an >> ANGLETOFINESHIFT; + pd->xSpeed = FixedMul(pd->speed, finecosine[pd->direction]); + pd->ySpeed = FixedMul(pd->speed, finesine[pd->direction]); + SN_StartSequence((mobj_t *) & poly->startSpot, SEQ_DOOR_STONE + + poly->seqType); + } + else if (type == PODOOR_SWING) + { + pd->waitTics = args[3]; + pd->direction = -1; // ADD: same as above + pd->speed = (args[1] * pd->direction * (ANG90 / 64)) >> 3; + pd->totalDist = args[2] * (ANG90 / 64); + pd->dist = pd->totalDist; + SN_StartSequence((mobj_t *) & poly->startSpot, SEQ_DOOR_STONE + + poly->seqType); + } + polyNum = mirror; + } + return true; +} + +// ===== Higher Level Poly Interface code ===== + +//========================================================================== +// +// GetPolyobj +// +//========================================================================== + +static polyobj_t *GetPolyobj(int polyNum) +{ + int i; + + for (i = 0; i < po_NumPolyobjs; i++) + { + if (polyobjs[i].tag == polyNum) + { + return &polyobjs[i]; + } + } + return NULL; +} + +//========================================================================== +// +// GetPolyobjMirror +// +//========================================================================== + +static int GetPolyobjMirror(int poly) +{ + int i; + + for (i = 0; i < po_NumPolyobjs; i++) + { + if (polyobjs[i].tag == poly) + { + return ((*polyobjs[i].segs)->linedef->arg2); + } + } + return 0; +} + +//========================================================================== +// +// ThrustMobj +// +//========================================================================== + +static void ThrustMobj(mobj_t * mobj, seg_t * seg, polyobj_t * po) +{ + int thrustAngle; + int thrustX; + int thrustY; + polyevent_t *pe; + + int force; + + if (!(mobj->flags & MF_SHOOTABLE) && !mobj->player) + { + return; + } + thrustAngle = (seg->angle - ANG90) >> ANGLETOFINESHIFT; + + pe = po->specialdata; + if (pe) + { + if (pe->thinker.function == T_RotatePoly) + { + force = pe->speed >> 8; + } + else + { + force = pe->speed >> 3; + } + if (force < FRACUNIT) + { + force = FRACUNIT; + } + else if (force > 4 * FRACUNIT) + { + force = 4 * FRACUNIT; + } + } + else + { + force = FRACUNIT; + } + + thrustX = FixedMul(force, finecosine[thrustAngle]); + thrustY = FixedMul(force, finesine[thrustAngle]); + mobj->momx += thrustX; + mobj->momy += thrustY; + if (po->crush) + { + if (!P_CheckPosition(mobj, mobj->x + thrustX, mobj->y + thrustY)) + { + P_DamageMobj(mobj, NULL, NULL, 3); + } + } +} + +//========================================================================== +// +// UpdateSegBBox +// +//========================================================================== + +static void UpdateSegBBox(seg_t * seg) +{ + line_t *line; + + line = seg->linedef; + + if (seg->v1->x < seg->v2->x) + { + line->bbox[BOXLEFT] = seg->v1->x; + line->bbox[BOXRIGHT] = seg->v2->x; + } + else + { + line->bbox[BOXLEFT] = seg->v2->x; + line->bbox[BOXRIGHT] = seg->v1->x; + } + if (seg->v1->y < seg->v2->y) + { + line->bbox[BOXBOTTOM] = seg->v1->y; + line->bbox[BOXTOP] = seg->v2->y; + } + else + { + line->bbox[BOXBOTTOM] = seg->v2->y; + line->bbox[BOXTOP] = seg->v1->y; + } + + // Update the line's slopetype + line->dx = line->v2->x - line->v1->x; + line->dy = line->v2->y - line->v1->y; + if (!line->dx) + { + line->slopetype = ST_VERTICAL; + } + else if (!line->dy) + { + line->slopetype = ST_HORIZONTAL; + } + else + { + if (FixedDiv(line->dy, line->dx) > 0) + { + line->slopetype = ST_POSITIVE; + } + else + { + line->slopetype = ST_NEGATIVE; + } + } +} + +//========================================================================== +// +// PO_MovePolyobj +// +//========================================================================== + +boolean PO_MovePolyobj(int num, int x, int y) +{ + int count; + seg_t **segList; + seg_t **veryTempSeg; + polyobj_t *po; + vertex_t *prevPts; + boolean blocked; + + if (!(po = GetPolyobj(num))) + { + I_Error("PO_MovePolyobj: Invalid polyobj number: %d\n", num); + } + + UnLinkPolyobj(po); + + segList = po->segs; + prevPts = po->prevPts; + blocked = false; + + validcount++; + for (count = po->numsegs; count; count--, segList++, prevPts++) + { + if ((*segList)->linedef->validcount != validcount) + { + (*segList)->linedef->bbox[BOXTOP] += y; + (*segList)->linedef->bbox[BOXBOTTOM] += y; + (*segList)->linedef->bbox[BOXLEFT] += x; + (*segList)->linedef->bbox[BOXRIGHT] += x; + (*segList)->linedef->validcount = validcount; + } + for (veryTempSeg = po->segs; veryTempSeg != segList; veryTempSeg++) + { + if ((*veryTempSeg)->v1 == (*segList)->v1) + { + break; + } + } + if (veryTempSeg == segList) + { + (*segList)->v1->x += x; + (*segList)->v1->y += y; + } + (*prevPts).x += x; // previous points are unique for each seg + (*prevPts).y += y; + } + segList = po->segs; + for (count = po->numsegs; count; count--, segList++) + { + if (CheckMobjBlocking(*segList, po)) + { + blocked = true; + } + } + if (blocked) + { + count = po->numsegs; + segList = po->segs; + prevPts = po->prevPts; + validcount++; + while (count--) + { + if ((*segList)->linedef->validcount != validcount) + { + (*segList)->linedef->bbox[BOXTOP] -= y; + (*segList)->linedef->bbox[BOXBOTTOM] -= y; + (*segList)->linedef->bbox[BOXLEFT] -= x; + (*segList)->linedef->bbox[BOXRIGHT] -= x; + (*segList)->linedef->validcount = validcount; + } + for (veryTempSeg = po->segs; veryTempSeg != segList; + veryTempSeg++) + { + if ((*veryTempSeg)->v1 == (*segList)->v1) + { + break; + } + } + if (veryTempSeg == segList) + { + (*segList)->v1->x -= x; + (*segList)->v1->y -= y; + } + (*prevPts).x -= x; + (*prevPts).y -= y; + segList++; + prevPts++; + } + LinkPolyobj(po); + return false; + } + po->startSpot.x += x; + po->startSpot.y += y; + LinkPolyobj(po); + return true; +} + +//========================================================================== +// +// RotatePt +// +//========================================================================== + +static void RotatePt(int an, fixed_t * x, fixed_t * y, fixed_t startSpotX, + fixed_t startSpotY) +{ + fixed_t trx, try; + fixed_t gxt, gyt; + + trx = *x; + try = *y; + + gxt = FixedMul(trx, finecosine[an]); + gyt = FixedMul(try, finesine[an]); + *x = (gxt - gyt) + startSpotX; + + gxt = FixedMul(trx, finesine[an]); + gyt = FixedMul(try, finecosine[an]); + *y = (gyt + gxt) + startSpotY; +} + +//========================================================================== +// +// PO_RotatePolyobj +// +//========================================================================== + +boolean PO_RotatePolyobj(int num, angle_t angle) +{ + int count; + seg_t **segList; + vertex_t *originalPts; + vertex_t *prevPts; + int an; + polyobj_t *po; + boolean blocked; + + if (!(po = GetPolyobj(num))) + { + I_Error("PO_RotatePolyobj: Invalid polyobj number: %d\n", num); + } + an = (po->angle + angle) >> ANGLETOFINESHIFT; + + UnLinkPolyobj(po); + + segList = po->segs; + originalPts = po->originalPts; + prevPts = po->prevPts; + + for (count = po->numsegs; count; count--, segList++, originalPts++, + prevPts++) + { + prevPts->x = (*segList)->v1->x; + prevPts->y = (*segList)->v1->y; + (*segList)->v1->x = originalPts->x; + (*segList)->v1->y = originalPts->y; + RotatePt(an, &(*segList)->v1->x, &(*segList)->v1->y, po->startSpot.x, + po->startSpot.y); + } + segList = po->segs; + blocked = false; + validcount++; + for (count = po->numsegs; count; count--, segList++) + { + if (CheckMobjBlocking(*segList, po)) + { + blocked = true; + } + if ((*segList)->linedef->validcount != validcount) + { + UpdateSegBBox(*segList); + (*segList)->linedef->validcount = validcount; + } + (*segList)->angle += angle; + } + if (blocked) + { + segList = po->segs; + prevPts = po->prevPts; + for (count = po->numsegs; count; count--, segList++, prevPts++) + { + (*segList)->v1->x = prevPts->x; + (*segList)->v1->y = prevPts->y; + } + segList = po->segs; + validcount++; + for (count = po->numsegs; count; count--, segList++, prevPts++) + { + if ((*segList)->linedef->validcount != validcount) + { + UpdateSegBBox(*segList); + (*segList)->linedef->validcount = validcount; + } + (*segList)->angle -= angle; + } + LinkPolyobj(po); + return false; + } + po->angle += angle; + LinkPolyobj(po); + return true; +} + +//========================================================================== +// +// UnLinkPolyobj +// +//========================================================================== + +static void UnLinkPolyobj(polyobj_t * po) +{ + polyblock_t *link; + int i, j; + int index; + + // remove the polyobj from each blockmap section + for (j = po->bbox[BOXBOTTOM]; j <= po->bbox[BOXTOP]; j++) + { + index = j * bmapwidth; + for (i = po->bbox[BOXLEFT]; i <= po->bbox[BOXRIGHT]; i++) + { + if (i >= 0 && i < bmapwidth && j >= 0 && j < bmapheight) + { + link = PolyBlockMap[index + i]; + while (link != NULL && link->polyobj != po) + { + link = link->next; + } + if (link == NULL) + { // polyobj not located in the link cell + continue; + } + link->polyobj = NULL; + } + } + } +} + +//========================================================================== +// +// LinkPolyobj +// +//========================================================================== + +static void LinkPolyobj(polyobj_t * po) +{ + int leftX, rightX; + int topY, bottomY; + seg_t **tempSeg; + polyblock_t **link; + polyblock_t *tempLink; + int i, j; + + // calculate the polyobj bbox + tempSeg = po->segs; + rightX = leftX = (*tempSeg)->v1->x; + topY = bottomY = (*tempSeg)->v1->y; + + for (i = 0; i < po->numsegs; i++, tempSeg++) + { + if ((*tempSeg)->v1->x > rightX) + { + rightX = (*tempSeg)->v1->x; + } + if ((*tempSeg)->v1->x < leftX) + { + leftX = (*tempSeg)->v1->x; + } + if ((*tempSeg)->v1->y > topY) + { + topY = (*tempSeg)->v1->y; + } + if ((*tempSeg)->v1->y < bottomY) + { + bottomY = (*tempSeg)->v1->y; + } + } + po->bbox[BOXRIGHT] = (rightX - bmaporgx) >> MAPBLOCKSHIFT; + po->bbox[BOXLEFT] = (leftX - bmaporgx) >> MAPBLOCKSHIFT; + po->bbox[BOXTOP] = (topY - bmaporgy) >> MAPBLOCKSHIFT; + po->bbox[BOXBOTTOM] = (bottomY - bmaporgy) >> MAPBLOCKSHIFT; + // add the polyobj to each blockmap section + for (j = po->bbox[BOXBOTTOM] * bmapwidth; + j <= po->bbox[BOXTOP] * bmapwidth; j += bmapwidth) + { + for (i = po->bbox[BOXLEFT]; i <= po->bbox[BOXRIGHT]; i++) + { + if (i >= 0 && i < bmapwidth && j >= 0 + && j < bmapheight * bmapwidth) + { + link = &PolyBlockMap[j + i]; + if (!(*link)) + { // Create a new link at the current block cell + *link = Z_Malloc(sizeof(polyblock_t), PU_LEVEL, 0); + (*link)->next = NULL; + (*link)->prev = NULL; + (*link)->polyobj = po; + continue; + } + else + { + tempLink = *link; + while (tempLink->next != NULL + && tempLink->polyobj != NULL) + { + tempLink = tempLink->next; + } + } + if (tempLink->polyobj == NULL) + { + tempLink->polyobj = po; + continue; + } + else + { + tempLink->next = Z_Malloc(sizeof(polyblock_t), + PU_LEVEL, 0); + tempLink->next->next = NULL; + tempLink->next->prev = tempLink; + tempLink->next->polyobj = po; + } + } + // else, don't link the polyobj, since it's off the map + } + } +} + +//========================================================================== +// +// CheckMobjBlocking +// +//========================================================================== + +static boolean CheckMobjBlocking(seg_t * seg, polyobj_t * po) +{ + mobj_t *mobj; + int i, j; + int left, right, top, bottom; + int tmbbox[4]; + line_t *ld; + boolean blocked; + + ld = seg->linedef; + + top = (ld->bbox[BOXTOP] - bmaporgy + MAXRADIUS) >> MAPBLOCKSHIFT; + bottom = (ld->bbox[BOXBOTTOM] - bmaporgy - MAXRADIUS) >> MAPBLOCKSHIFT; + left = (ld->bbox[BOXLEFT] - bmaporgx - MAXRADIUS) >> MAPBLOCKSHIFT; + right = (ld->bbox[BOXRIGHT] - bmaporgx + MAXRADIUS) >> MAPBLOCKSHIFT; + + blocked = false; + + bottom = bottom < 0 ? 0 : bottom; + bottom = bottom >= bmapheight ? bmapheight - 1 : bottom; + top = top < 0 ? 0 : top; + top = top >= bmapheight ? bmapheight - 1 : top; + left = left < 0 ? 0 : left; + left = left >= bmapwidth ? bmapwidth - 1 : left; + right = right < 0 ? 0 : right; + right = right >= bmapwidth ? bmapwidth - 1 : right; + + for (j = bottom * bmapwidth; j <= top * bmapwidth; j += bmapwidth) + { + for (i = left; i <= right; i++) + { + for (mobj = blocklinks[j + i]; mobj; mobj = mobj->bnext) + { + if (mobj->flags & MF_SOLID || mobj->player) + { + tmbbox[BOXTOP] = mobj->y + mobj->radius; + tmbbox[BOXBOTTOM] = mobj->y - mobj->radius; + tmbbox[BOXLEFT] = mobj->x - mobj->radius; + tmbbox[BOXRIGHT] = mobj->x + mobj->radius; + + if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT] + || tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT] + || tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM] + || tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP]) + { + continue; + } + if (P_BoxOnLineSide(tmbbox, ld) != -1) + { + continue; + } + ThrustMobj(mobj, seg, po); + blocked = true; + } + } + } + } + return blocked; +} + +//========================================================================== +// +// InitBlockMap +// +//========================================================================== + +static void InitBlockMap(void) +{ + int i; + + int j; + seg_t **segList; + int area; + int leftX, rightX; + int topY, bottomY; + + PolyBlockMap = Z_Malloc(bmapwidth * bmapheight * sizeof(polyblock_t *), + PU_LEVEL, 0); + memset(PolyBlockMap, 0, bmapwidth * bmapheight * sizeof(polyblock_t *)); + + for (i = 0; i < po_NumPolyobjs; i++) + { + LinkPolyobj(&polyobjs[i]); + + // calculate a rough area + // right now, working like shit...gotta fix this... + segList = polyobjs[i].segs; + leftX = rightX = (*segList)->v1->x; + topY = bottomY = (*segList)->v1->y; + for (j = 0; j < polyobjs[i].numsegs; j++, segList++) + { + if ((*segList)->v1->x < leftX) + { + leftX = (*segList)->v1->x; + } + if ((*segList)->v1->x > rightX) + { + rightX = (*segList)->v1->x; + } + if ((*segList)->v1->y < bottomY) + { + bottomY = (*segList)->v1->y; + } + if ((*segList)->v1->y > topY) + { + topY = (*segList)->v1->y; + } + } + area = ((rightX >> FRACBITS) - (leftX >> FRACBITS)) * + ((topY >> FRACBITS) - (bottomY >> FRACBITS)); + +// fprintf(stdaux, "Area of Polyobj[%d]: %d\n", polyobjs[i].tag, area); +// fprintf(stdaux, "\t[%d]\n[%d]\t\t[%d]\n\t[%d]\n", topY>>FRACBITS, +// leftX>>FRACBITS, +// rightX>>FRACBITS, bottomY>>FRACBITS); + } +} + +//========================================================================== +// +// IterFindPolySegs +// +// Passing NULL for segList will cause IterFindPolySegs to +// count the number of segs in the polyobj +//========================================================================== + +static void IterFindPolySegs(int x, int y, seg_t ** segList) +{ + int i; + + if (x == PolyStartX && y == PolyStartY) + { + return; + } + for (i = 0; i < numsegs; i++) + { + if (segs[i].v1->x == x && segs[i].v1->y == y) + { + if (!segList) + { + PolySegCount++; + } + else + { + *segList++ = &segs[i]; + } + IterFindPolySegs(segs[i].v2->x, segs[i].v2->y, segList); + return; + } + } + I_Error("IterFindPolySegs: Non-closed Polyobj located.\n"); +} + + +//========================================================================== +// +// SpawnPolyobj +// +//========================================================================== + +static void SpawnPolyobj(int index, int tag, boolean crush) +{ + int i; + int j; + int psIndex; + int psIndexOld; + seg_t *polySegList[PO_MAXPOLYSEGS]; + + for (i = 0; i < numsegs; i++) + { + if (segs[i].linedef->special == PO_LINE_START && + segs[i].linedef->arg1 == tag) + { + if (polyobjs[index].segs) + { + I_Error("SpawnPolyobj: Polyobj %d already spawned.\n", tag); + } + segs[i].linedef->special = 0; + segs[i].linedef->arg1 = 0; + PolySegCount = 1; + PolyStartX = segs[i].v1->x; + PolyStartY = segs[i].v1->y; + IterFindPolySegs(segs[i].v2->x, segs[i].v2->y, NULL); + + polyobjs[index].numsegs = PolySegCount; + polyobjs[index].segs = Z_Malloc(PolySegCount * sizeof(seg_t *), + PU_LEVEL, 0); + *(polyobjs[index].segs) = &segs[i]; // insert the first seg + IterFindPolySegs(segs[i].v2->x, segs[i].v2->y, + polyobjs[index].segs + 1); + polyobjs[index].crush = crush; + polyobjs[index].tag = tag; + polyobjs[index].seqType = segs[i].linedef->arg3; + if (polyobjs[index].seqType < 0 + || polyobjs[index].seqType >= SEQTYPE_NUMSEQ) + { + polyobjs[index].seqType = 0; + } + break; + } + } + if (!polyobjs[index].segs) + { // didn't find a polyobj through PO_LINE_START + psIndex = 0; + polyobjs[index].numsegs = 0; + for (j = 1; j < PO_MAXPOLYSEGS; j++) + { + psIndexOld = psIndex; + for (i = 0; i < numsegs; i++) + { + if (segs[i].linedef->special == PO_LINE_EXPLICIT && + segs[i].linedef->arg1 == tag) + { + if (!segs[i].linedef->arg2) + { + I_Error + ("SpawnPolyobj: Explicit line missing order number (probably %d) in poly %d.\n", + j + 1, tag); + } + if (segs[i].linedef->arg2 == j) + { + polySegList[psIndex] = &segs[i]; + polyobjs[index].numsegs++; + psIndex++; + if (psIndex > PO_MAXPOLYSEGS) + { + I_Error + ("SpawnPolyobj: psIndex > PO_MAXPOLYSEGS\n"); + } + } + } + } + // Clear out any specials for these segs...we cannot clear them out + // in the above loop, since we aren't guaranteed one seg per + // linedef. + for (i = 0; i < numsegs; i++) + { + if (segs[i].linedef->special == PO_LINE_EXPLICIT && + segs[i].linedef->arg1 == tag + && segs[i].linedef->arg2 == j) + { + segs[i].linedef->special = 0; + segs[i].linedef->arg1 = 0; + } + } + if (psIndex == psIndexOld) + { // Check if an explicit line order has been skipped + // A line has been skipped if there are any more explicit + // lines with the current tag value + for (i = 0; i < numsegs; i++) + { + if (segs[i].linedef->special == PO_LINE_EXPLICIT && + segs[i].linedef->arg1 == tag) + { + I_Error + ("SpawnPolyobj: Missing explicit line %d for poly %d\n", + j, tag); + } + } + } + } + if (polyobjs[index].numsegs) + { + PolySegCount = polyobjs[index].numsegs; // PolySegCount used globally + polyobjs[index].crush = crush; + polyobjs[index].tag = tag; + polyobjs[index].segs = Z_Malloc(polyobjs[index].numsegs + * sizeof(seg_t *), PU_LEVEL, 0); + for (i = 0; i < polyobjs[index].numsegs; i++) + { + polyobjs[index].segs[i] = polySegList[i]; + } + polyobjs[index].seqType = (*polyobjs[index].segs)->linedef->arg4; + } + // Next, change the polyobjs first line to point to a mirror + // if it exists + (*polyobjs[index].segs)->linedef->arg2 = + (*polyobjs[index].segs)->linedef->arg3; + } +} + +//========================================================================== +// +// TranslateToStartSpot +// +//========================================================================== + +static void TranslateToStartSpot(int tag, int originX, int originY) +{ + seg_t **tempSeg; + seg_t **veryTempSeg; + vertex_t *tempPt; + subsector_t *sub; + polyobj_t *po; + int deltaX; + int deltaY; + vertex_t avg; // used to find a polyobj's center, and hence subsector + int i; + + po = NULL; + for (i = 0; i < po_NumPolyobjs; i++) + { + if (polyobjs[i].tag == tag) + { + po = &polyobjs[i]; + break; + } + } + if (!po) + { // didn't match the tag with a polyobj tag + I_Error("TranslateToStartSpot: Unable to match polyobj tag: %d\n", + tag); + } + if (po->segs == NULL) + { + I_Error + ("TranslateToStartSpot: Anchor point located without a StartSpot point: %d\n", + tag); + } + po->originalPts = Z_Malloc(po->numsegs * sizeof(vertex_t), PU_LEVEL, 0); + po->prevPts = Z_Malloc(po->numsegs * sizeof(vertex_t), PU_LEVEL, 0); + deltaX = originX - po->startSpot.x; + deltaY = originY - po->startSpot.y; + + tempSeg = po->segs; + tempPt = po->originalPts; + avg.x = 0; + avg.y = 0; + + validcount++; + for (i = 0; i < po->numsegs; i++, tempSeg++, tempPt++) + { + if ((*tempSeg)->linedef->validcount != validcount) + { + (*tempSeg)->linedef->bbox[BOXTOP] -= deltaY; + (*tempSeg)->linedef->bbox[BOXBOTTOM] -= deltaY; + (*tempSeg)->linedef->bbox[BOXLEFT] -= deltaX; + (*tempSeg)->linedef->bbox[BOXRIGHT] -= deltaX; + (*tempSeg)->linedef->validcount = validcount; + } + for (veryTempSeg = po->segs; veryTempSeg != tempSeg; veryTempSeg++) + { + if ((*veryTempSeg)->v1 == (*tempSeg)->v1) + { + break; + } + } + if (veryTempSeg == tempSeg) + { // the point hasn't been translated, yet + (*tempSeg)->v1->x -= deltaX; + (*tempSeg)->v1->y -= deltaY; + } + avg.x += (*tempSeg)->v1->x >> FRACBITS; + avg.y += (*tempSeg)->v1->y >> FRACBITS; + // the original Pts are based off the startSpot Pt, and are + // unique to each seg, not each linedef + tempPt->x = (*tempSeg)->v1->x - po->startSpot.x; + tempPt->y = (*tempSeg)->v1->y - po->startSpot.y; + } + avg.x /= po->numsegs; + avg.y /= po->numsegs; + sub = R_PointInSubsector(avg.x << FRACBITS, avg.y << FRACBITS); + if (sub->poly != NULL) + { + I_Error + ("PO_TranslateToStartSpot: Multiple polyobjs in a single subsector.\n"); + } + sub->poly = po; +} + +//========================================================================== +// +// PO_Init +// +//========================================================================== + +void PO_Init(int lump) +{ + byte *data; + int i; + mapthing_t spawnthing; + mapthing_t *mt; + int numthings; + int polyIndex; + + polyobjs = Z_Malloc(po_NumPolyobjs * sizeof(polyobj_t), PU_LEVEL, 0); + memset(polyobjs, 0, po_NumPolyobjs * sizeof(polyobj_t)); + + data = W_CacheLumpNum(lump, PU_STATIC); + numthings = W_LumpLength(lump) / sizeof(mapthing_t); + mt = (mapthing_t *) data; + polyIndex = 0; // index polyobj number + // Find the startSpot points, and spawn each polyobj + for (i = 0; i < numthings; i++, mt++) + { + spawnthing.x = SHORT(mt->x); + spawnthing.y = SHORT(mt->y); + spawnthing.angle = SHORT(mt->angle); + spawnthing.type = SHORT(mt->type); + + // 3001 = no crush, 3002 = crushing + if (spawnthing.type == PO_SPAWN_TYPE + || spawnthing.type == PO_SPAWNCRUSH_TYPE) + { // Polyobj StartSpot Pt. + polyobjs[polyIndex].startSpot.x = spawnthing.x << FRACBITS; + polyobjs[polyIndex].startSpot.y = spawnthing.y << FRACBITS; + SpawnPolyobj(polyIndex, spawnthing.angle, + (spawnthing.type == PO_SPAWNCRUSH_TYPE)); + polyIndex++; + } + } + mt = (mapthing_t *) data; + for (i = 0; i < numthings; i++, mt++) + { + spawnthing.x = SHORT(mt->x); + spawnthing.y = SHORT(mt->y); + spawnthing.angle = SHORT(mt->angle); + spawnthing.type = SHORT(mt->type); + if (spawnthing.type == PO_ANCHOR_TYPE) + { // Polyobj Anchor Pt. + TranslateToStartSpot(spawnthing.angle, + spawnthing.x << FRACBITS, + spawnthing.y << FRACBITS); + } + } + W_ReleaseLumpNum(lump); + // check for a startspot without an anchor point + for (i = 0; i < po_NumPolyobjs; i++) + { + if (!polyobjs[i].originalPts) + { + I_Error + ("PO_Init: StartSpot located without an Anchor point: %d\n", + polyobjs[i].tag); + } + } + InitBlockMap(); +} + +//========================================================================== +// +// PO_Busy +// +//========================================================================== + +boolean PO_Busy(int polyobj) +{ + polyobj_t *poly; + + poly = GetPolyobj(polyobj); + if (!poly->specialdata) + { + return false; + } + else + { + return true; + } +} |