diff --git a/src/k_objects.h b/src/k_objects.h index 830c9e769..922764c94 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -2,6 +2,8 @@ #ifndef k_objects_H #define k_objects_H +#include "taglist.h" + #ifdef __cplusplus extern "C" { #endif @@ -93,7 +95,10 @@ void Obj_ItemSpotAssignMonitor(mobj_t *spot, mobj_t *monitor); void Obj_ItemSpotUpdate(mobj_t *spot); /* Loops */ +mobj_t *Obj_FindLoopCenter(const mtag_t tag); +void Obj_InitLoopEndpoint(mobj_t *end, mobj_t *anchor); void Obj_InitLoopCenter(mobj_t *center); +void Obj_LinkLoopAnchor(mobj_t *anchor, mobj_t *center, UINT8 type); void Obj_LoopEndpointCollide(mobj_t *special, mobj_t *toucher); #ifdef __cplusplus diff --git a/src/objects/loops.c b/src/objects/loops.c index a8838e3df..dd9ea7c3b 100644 --- a/src/objects/loops.c +++ b/src/objects/loops.c @@ -91,6 +91,21 @@ measure_clock *radius = FixedHypot(xy, dz); } +static void +crisscross +( mobj_t * anchor, + mobj_t ** target_p, + mobj_t ** other_p) +{ + P_SetTarget(target_p, anchor); + + if (!P_MobjWasRemoved(*other_p)) + { + P_SetTarget(&anchor_other(anchor), *other_p); + P_SetTarget(&anchor_other(*other_p), anchor); + } +} + static boolean moving_toward_gate ( const player_t * player, @@ -134,6 +149,32 @@ get_binary_direction } } +mobj_t * +Obj_FindLoopCenter (const mtag_t tag) +{ + INT32 i; + + TAG_ITER_THINGS(tag, i) + { + mapthing_t *mt = &mapthings[i]; + + if (mt->type == mobjinfo[MT_LOOPCENTERPOINT].doomednum) + { + return mt->mobj; + } + } + + return NULL; +} + +void +Obj_InitLoopEndpoint +( mobj_t * end, + mobj_t * anchor) +{ + P_SetTarget(&end_anchor(end), anchor); +} + void Obj_InitLoopCenter (mobj_t *center) { @@ -143,6 +184,35 @@ Obj_InitLoopCenter (mobj_t *center) center_set_flip(center, mt->args[0]); } +void +Obj_LinkLoopAnchor +( mobj_t * anchor, + mobj_t * center, + UINT8 type) +{ + P_SetTarget(&anchor_center(anchor), center); + + anchor_type(anchor) = type; + + if (!P_MobjWasRemoved(center)) + { + switch (type) + { + case TMLOOP_ALPHA: + crisscross(anchor, + ¢er_alpha(center), + ¢er_beta(center)); + break; + + case TMLOOP_BETA: + crisscross(anchor, + ¢er_beta(center), + ¢er_alpha(center)); + break; + } + } +} + void Obj_LoopEndpointCollide ( mobj_t * end, diff --git a/src/p_mobj.c b/src/p_mobj.c index 2d128f0cd..232812765 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -13552,6 +13552,11 @@ static void P_SpawnItemRow(mapthing_t *mthing, mobjtype_t *itemtypes, UINT8 numi angle_t angle = FixedAngle(fixedangle << FRACBITS); angle_t fineangle = (angle >> ANGLETOFINESHIFT) & FINEMASK; + boolean isloopend = (mthing->type == mobjinfo[MT_LOOPENDPOINT].doomednum); + mobj_t *loopanchor; + + boolean inclusive = isloopend; + for (r = 0; r < numitemtypes; r++) { dummything = *mthing; @@ -13570,6 +13575,21 @@ static void P_SpawnItemRow(mapthing_t *mthing, mobjtype_t *itemtypes, UINT8 numi } z = P_GetMobjSpawnHeight(itemtypes[0], x, y, z, 0, mthing->options & MTF_OBJECTFLIP, mthing->scale); + if (isloopend) + { + const fixed_t length = (numitems - 1) * horizontalspacing / 2; + + mobj_t *loopcenter = Obj_FindLoopCenter(Tag_FGet(&mthing->tags)); + + // Spawn the anchor at the middle point of the line + loopanchor = P_SpawnMobjFromMapThing(&dummything, + x + FixedMul(length, FINECOSINE(fineangle)), + y + FixedMul(length, FINESINE(fineangle)), + z, MT_LOOPCENTERPOINT); + + Obj_LinkLoopAnchor(loopanchor, loopcenter, mthing->args[0]); + } + for (r = 0; r < numitems; r++) { mobjtype_t itemtype = itemtypes[r % numitemtypes]; @@ -13577,15 +13597,24 @@ static void P_SpawnItemRow(mapthing_t *mthing, mobjtype_t *itemtypes, UINT8 numi continue; dummything.type = mobjinfo[itemtype].doomednum; + if (inclusive) + mobj = P_SpawnMobjFromMapThing(&dummything, x, y, z, itemtype); + x += FixedMul(horizontalspacing, FINECOSINE(fineangle)); y += FixedMul(horizontalspacing, FINESINE(fineangle)); z += (mthing->options & MTF_OBJECTFLIP) ? -verticalspacing : verticalspacing; - mobj = P_SpawnMobjFromMapThing(&dummything, x, y, z, itemtype); + if (!inclusive) + mobj = P_SpawnMobjFromMapThing(&dummything, x, y, z, itemtype); if (!mobj) continue; + if (isloopend) + { + Obj_InitLoopEndpoint(mobj, loopanchor); + } + mobj->spawnpoint = NULL; } } diff --git a/src/p_setup.c b/src/p_setup.c index d49e82348..a348b4999 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -677,11 +677,26 @@ void P_ReloadRings(void) } } +static int cmp_loopends(const void *a, const void *b) +{ + const mapthing_t + *mt1 = *(const mapthing_t*const*)a, + *mt2 = *(const mapthing_t*const*)b; + + // weighted sorting; tag takes precedence over type + return + intsign(Tag_FGet(&mt1->tags) - Tag_FGet(&mt2->tags)) * 2 + + intsign(mt1->args[0] - mt2->args[0]); +} + static void P_SpawnMapThings(boolean spawnemblems) { size_t i; mapthing_t *mt; + mapthing_t **loopends; + size_t num_loopends = 0; + // Spawn axis points first so they are at the front of the list for fast searching. for (i = 0, mt = mapthings; i < nummapthings; i++, mt++) { @@ -690,14 +705,22 @@ static void P_SpawnMapThings(boolean spawnemblems) case 1700: // MT_AXIS case 1701: // MT_AXISTRANSFER case 1702: // MT_AXISTRANSFERLINE + case 2021: // MT_LOOPCENTERPOINT mt->mobj = NULL; P_SpawnMapThing(mt); break; + case 2020: // MT_LOOPENDPOINT + num_loopends++; + break; default: break; } } + Z_Malloc(num_loopends * sizeof *loopends, PU_STATIC, + &loopends); + num_loopends = 0; + for (i = 0, mt = mapthings; i < nummapthings; i++, mt++) { switch (mt->type) @@ -705,6 +728,7 @@ static void P_SpawnMapThings(boolean spawnemblems) case 1700: // MT_AXIS case 1701: // MT_AXISTRANSFER case 1702: // MT_AXISTRANSFERLINE + case 2021: // MT_LOOPCENTERPOINT continue; // These were already spawned } @@ -716,6 +740,13 @@ static void P_SpawnMapThings(boolean spawnemblems) mt->mobj = NULL; + if (mt->type == mobjinfo[MT_LOOPENDPOINT].doomednum) + { + loopends[num_loopends] = mt; + num_loopends++; + continue; + } + if (mt->type >= 600 && mt->type <= 611) // item patterns P_SpawnItemPattern(mt); else if (mt->type == 1713) // hoops @@ -723,6 +754,25 @@ static void P_SpawnMapThings(boolean spawnemblems) else // Everything else P_SpawnMapThing(mt); } + + qsort(loopends, num_loopends, sizeof *loopends, + cmp_loopends); + + for (i = 1; i < num_loopends; ++i) + { + mapthing_t + *mt1 = loopends[i - 1], + *mt2 = loopends[i]; + + if (Tag_FGet(&mt1->tags) == Tag_FGet(&mt2->tags) && + mt1->args[0] == mt2->args[0]) + { + P_SpawnItemLine(mt1, mt2); + i++; + } + } + + Z_Free(loopends); } // Experimental groovy write function! @@ -3931,6 +3981,8 @@ static void P_AddBinaryMapTags(void) case 292: case 294: case 780: + case 2020: // MT_LOOPENDPOINT + case 2021: // MT_LOOPCENTERPOINT Tag_FSet(&mapthings[i].tags, mapthings[i].extrainfo); break; default: @@ -6736,6 +6788,17 @@ static void P_ConvertBinaryThingTypes(void) mapthings[i].args[2] |= TMICF_INVERTSIZE; } break; + case 2020: // MT_LOOPENDPOINT + { + mapthings[i].args[0] = + mapthings[i].options & MTF_AMBUSH ? + TMLOOP_BETA : TMLOOP_ALPHA; + break; + } + case 2021: // MT_LOOPCENTERPOINT + mapthings[i].args[0] = (mapthings[i].options & MTF_AMBUSH) == MTF_AMBUSH; + mapthings[i].args[1] = mapthings[i].angle; + break; case 2050: // MT_DUELBOMB mapthings[i].args[1] = !!(mapthings[i].options & MTF_AMBUSH); break; diff --git a/src/p_spec.h b/src/p_spec.h index 6ccb328cd..908046936 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -513,6 +513,12 @@ typedef enum TMBOT_FORCEDIR = 1<<2, } textmapbotcontroller_t; +typedef enum +{ + TMLOOP_ALPHA = 0, + TMLOOP_BETA = 1, +} textmaploopendpointtype_t; + // GETSECSPECIAL (specialval, section) // // Pulls out the special # from a particular section.