// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 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. // // 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 -------------------------------------------- // 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 ---------------------------------------------- // 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 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; } }