From 4c994de7224bf6417ffeff26feda67a3f5585fb2 Mon Sep 17 00:00:00 2001 From: Oni Date: Wed, 1 Mar 2023 18:28:56 +0000 Subject: [PATCH] Merge branch 'loops' into 'master' Sonic Loops See merge request KartKrew/Kart!991 --- src/CMakeLists.txt | 1 + src/Makefile | 1 + src/Sourcefile | 1 + src/d_player.h | 12 ++ src/deh_tables.c | 3 + src/info.c | 54 +++++++ src/info.h | 3 + src/k_objects.h | 14 ++ src/objects/CMakeLists.txt | 1 + src/objects/Sourcefile | 1 + src/objects/loops.c | 281 +++++++++++++++++++++++++++++++++++++ src/p_inter.c | 5 + src/p_local.h | 4 + src/p_loop.c | 176 +++++++++++++++++++++++ src/p_map.c | 18 ++- src/p_mobj.c | 40 +++++- src/p_saveg.c | 27 ++++ src/p_setup.c | 63 +++++++++ src/p_spec.h | 6 + src/p_user.c | 7 +- 20 files changed, 714 insertions(+), 4 deletions(-) create mode 100644 src/k_objects.h create mode 100644 src/objects/CMakeLists.txt create mode 100644 src/objects/Sourcefile create mode 100644 src/objects/loops.c create mode 100644 src/p_loop.c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 052d312a7..530d28414 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -83,6 +83,7 @@ endif() add_subdirectory(blua) add_subdirectory(blan) +add_subdirectory(objects) if(${SRB2_CONFIG_HAVE_GME}) if(${SRB2_CONFIG_USE_INTERNAL_LIBRARIES}) diff --git a/src/Makefile b/src/Makefile index 5eaa8341f..b22e07541 100644 --- a/src/Makefile +++ b/src/Makefile @@ -220,6 +220,7 @@ sources+=\ $(call List,Sourcefile)\ $(call List,blua/Sourcefile)\ $(call List,blan/Sourcefile)\ + $(call List,objects/Sourcefile)\ depends:=$(basename $(filter %.c %.s,$(sources))) objects:=$(basename $(filter %.c %.s %.nas,$(sources))) diff --git a/src/Sourcefile b/src/Sourcefile index 3832371d4..10bf49c4e 100644 --- a/src/Sourcefile +++ b/src/Sourcefile @@ -53,6 +53,7 @@ p_sight.c p_spec.c p_telept.c p_tick.c +p_loop.c p_user.c p_slopes.c tables.c diff --git a/src/d_player.h b/src/d_player.h index c94f2f14d..0fd123aec 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -278,6 +278,16 @@ typedef struct botvars_s } botvars_t; +// player_t struct for loop state +typedef struct { + fixed_t radius; + fixed_t revolution, min_revolution, max_revolution; + angle_t yaw; + vector3_t origin; + vector2_t shift; + boolean flip; +} sonicloopvars_t; + // ======================================================================== // PLAYER STRUCTURE // ======================================================================== @@ -543,6 +553,8 @@ typedef struct player_s fixed_t outrun; // Milky Way road effect UINT8 outruntime; // Used to bypass the speed cap for fall off + + sonicloopvars_t loop; #ifdef HWRENDER fixed_t fovadd; // adjust FOV for hw rendering diff --git a/src/deh_tables.c b/src/deh_tables.c index 9526dd580..c5931c6fe 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -5469,6 +5469,9 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_PAPERITEMSPOT", "MT_BEAMPOINT", + + "MT_LOOPENDPOINT", + "MT_LOOPCENTERPOINT", }; const char *const MOBJFLAG_LIST[] = { diff --git a/src/info.c b/src/info.c index eca62a4fe..cd78e6192 100644 --- a/src/info.c +++ b/src/info.c @@ -28104,6 +28104,60 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOCLIPTHING|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, + + { // MT_LOOPENDPOINT + 2020, // doomednum + S_INVISIBLE, // 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 + 0, // speed + MAXRADIUS, // radius + 2*MAXRADIUS, // height + 0, // display offset + 100, // mass + 1, // damage + sfx_None, // activesound + MF_SPECIAL|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + + { // MT_LOOPCENTERPOINT + 2021, // doomednum + S_INVISIBLE, // 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 + 0, // speed + 48*FRACUNIT, // radius + 32*FRACUNIT, // height + 0, // display offset + 100, // mass + 1, // damage + sfx_None, // activesound + MF_NOSECTOR|MF_NOBLOCKMAP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, }; skincolor_t skincolors[MAXSKINCOLORS] = { diff --git a/src/info.h b/src/info.h index cd96242ca..2543124f3 100644 --- a/src/info.h +++ b/src/info.h @@ -6487,6 +6487,9 @@ typedef enum mobj_type MT_BEAMPOINT, + MT_LOOPENDPOINT, + MT_LOOPCENTERPOINT, + MT_FIRSTFREESLOT, MT_LASTFREESLOT = MT_FIRSTFREESLOT + NUMMOBJFREESLOTS - 1, NUMMOBJTYPES diff --git a/src/k_objects.h b/src/k_objects.h new file mode 100644 index 000000000..b8516b2e1 --- /dev/null +++ b/src/k_objects.h @@ -0,0 +1,14 @@ +/* object-specific code */ +#ifndef k_objects_H +#define k_objects_H + +#include "taglist.h" + +/* 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); + +#endif/*k_objects_H*/ diff --git a/src/objects/CMakeLists.txt b/src/objects/CMakeLists.txt new file mode 100644 index 000000000..4e9c67d2f --- /dev/null +++ b/src/objects/CMakeLists.txt @@ -0,0 +1 @@ +target_sourcefile(c) diff --git a/src/objects/Sourcefile b/src/objects/Sourcefile new file mode 100644 index 000000000..23c6b035b --- /dev/null +++ b/src/objects/Sourcefile @@ -0,0 +1 @@ +loops.c \ No newline at end of file diff --git a/src/objects/loops.c b/src/objects/loops.c new file mode 100644 index 000000000..dd9ea7c3b --- /dev/null +++ b/src/objects/loops.c @@ -0,0 +1,281 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by James R. +// Copyright (C) 2023 by Kart Krew +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file loop-endpoint.c +/// \brief Sonic loops, start and end points + +#include "../doomdef.h" +#include "../k_kart.h" +#include "../taglist.h" +#include "../p_local.h" +#include "../p_setup.h" +#include "../p_spec.h" +#include "../r_main.h" +#include "../k_objects.h" + +#define end_anchor(o) ((o)->target) + +#define center_max_revolution(o) ((o)->threshold) +#define center_alpha(o) ((o)->target) +#define center_beta(o) ((o)->tracer) + +static inline boolean +center_has_flip (const mobj_t *center) +{ + return (center->flags2 & MF2_AMBUSH) == MF2_AMBUSH; +} + +static inline void +center_set_flip +( mobj_t * center, + boolean mode) +{ + center->flags2 = (center->flags2 & ~(MF2_AMBUSH)) | + ((mode != false) * MF2_AMBUSH); +} + +#define anchor_center(o) ((o)->target) +#define anchor_other(o) ((o)->tracer) +#define anchor_type(o) ((o)->reactiontime) + +static void +set_shiftxy +( player_t * player, + const mobj_t * a) +{ + const mobj_t *b = anchor_other(a); + + const fixed_t dx = (b->x - a->x); + const fixed_t dy = (b->y - a->y); + + const angle_t th = + (R_PointToAngle2(0, 0, dx, dy) - a->angle); + + const fixed_t adj = FixedMul( + abs(FCOS(AbsAngle(th - ANGLE_90))), + FixedHypot(dx, dy)) / 2; + + vector2_t *xy = &player->loop.shift; + + xy->x = FixedMul(FSIN(a->angle), adj); + xy->y = FixedMul(FCOS(a->angle), adj); +} + +static void +measure_clock +( const mobj_t * center, + const mobj_t * anchor, + angle_t * pitch, + fixed_t * radius) +{ + const fixed_t dx = (anchor->x - center->x); + const fixed_t dy = (anchor->y - center->y); + const fixed_t dz = (anchor->z - center->z); + + /* Translate the anchor point to be along a center line. + This makes the horizontal position one dimensional + relative to the center point. */ + const fixed_t xy = ( + FixedMul(dx, FCOS(anchor->angle)) + + FixedMul(dy, FSIN(anchor->angle))); + + /* The 3d position of the anchor point is then reduced to + two axes and can be measured as an angle. */ + *pitch = R_PointToAngle2(0, 0, xy, dz) + ANGLE_90; + *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, + const mobj_t * anchor, + angle_t pitch) +{ + const fixed_t + x = player->mo->momx, + y = player->mo->momy, + z = player->mo->momz, + + zx = FixedMul(FCOS(anchor->angle), z), + zy = FixedMul(FSIN(anchor->angle), z), + + co = abs(FCOS(pitch)), + si = abs(FSIN(pitch)), + + dx = FixedMul(co, x) + FixedMul(si, zx), + dy = FixedMul(co, y) + FixedMul(si, zy); + + return AngleDelta(anchor->angle, + R_PointToAngle2(0, 0, dx, dy)) < ANG60; +} + +static SINT8 +get_binary_direction +( angle_t pitch, + mobj_t * toucher) +{ + const fixed_t si = FSIN(pitch); + + if (abs(si) < abs(FCOS(pitch))) + { + // pitch = 0 points downward so offset 90 degrees + // clockwise so 180 occurs at horizon + return ((pitch + ANGLE_90) < ANGLE_180) ? 1 : -(1); + } + else + { + return intsign(si) * P_MobjFlip(toucher); + } +} + +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) +{ + const mapthing_t *mt = center->spawnpoint; + + center_max_revolution(center) = mt->args[1] * FRACUNIT / 360; + 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, + mobj_t * toucher) +{ + player_t *player = toucher->player; + sonicloopvars_t *s = &player->loop; + + mobj_t *anchor = end_anchor(end); + mobj_t *center = anchor ? anchor_center(anchor) : NULL; + + angle_t pitch; + fixed_t radius; + + /* predict movement for a smooth transition */ + const fixed_t px = toucher->x + toucher->momx; + const fixed_t py = toucher->y + toucher->momy; + + SINT8 flip; + + if (P_MobjWasRemoved(center)) + { + return; + } + + if (player->loop.radius != 0) + { + return; + } + + measure_clock(center, anchor, &pitch, &radius); + + if (!moving_toward_gate(player, anchor, pitch)) + { + return; + } + + if (!P_MobjWasRemoved(anchor_other(anchor))) + { + set_shiftxy(player, anchor); + } + + flip = get_binary_direction(pitch, toucher); + + s->yaw = anchor->angle; + + s->origin.x = center->x - (anchor->x - px); + s->origin.y = center->y - (anchor->y - py); + s->origin.z = center->z; + + s->radius = radius * flip; + s->revolution = AngleFixed(pitch) / 360; + + s->min_revolution = s->revolution; + s->max_revolution = s->revolution + + center_max_revolution(center) * flip; + + s->flip = center_has_flip(center); + + player->speed = + 3 * (player->speed + toucher->momz) / 2; + + /* cancel the effects of K_Squish */ + toucher->spritexscale = FRACUNIT; + toucher->spriteyscale = FRACUNIT; +} diff --git a/src/p_inter.c b/src/p_inter.c index d2f66482f..5553bfbd3 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -37,6 +37,7 @@ #include "k_grandprix.h" #include "k_boss.h" #include "p_spec.h" +#include "k_objects.h" // CTF player names #define CTFTEAMCODE(pl) pl->ctfteam ? (pl->ctfteam == 1 ? "\x85" : "\x84") : "" @@ -584,6 +585,10 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } return; + case MT_LOOPENDPOINT: + Obj_LoopEndpointCollide(special, toucher); + return; + default: // SOC or script pickup P_SetTarget(&special->target, toucher); break; diff --git a/src/p_local.h b/src/p_local.h index 06cc603c4..54bb0733e 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -195,6 +195,10 @@ boolean P_AutoPause(void); void P_ElementalFire(player_t *player, boolean cropcircle); void P_SpawnSkidDust(player_t *player, fixed_t radius, boolean sound); +void P_HaltPlayerOrbit(player_t *player); +void P_ExitPlayerOrbit(player_t *player); +boolean P_PlayerOrbit(player_t *player); + void P_MovePlayer(player_t *player); void P_PlayerThink(player_t *player); void P_PlayerAfterThink(player_t *player); diff --git a/src/p_loop.c b/src/p_loop.c new file mode 100644 index 000000000..66a8f7db4 --- /dev/null +++ b/src/p_loop.c @@ -0,0 +1,176 @@ +// SONIC ROBO BLAST 2 KART +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by Kart Krew +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file p_loop.c +/// \brief Sonic loop physics + +#include "doomdef.h" +#include "d_player.h" +#include "k_kart.h" +#include "p_local.h" +#include "p_setup.h" +#include "p_slopes.h" +#include "r_main.h" + +static inline angle_t +get_pitch (fixed_t revolution) +{ + return FixedAngle((revolution & FRACMASK) * 360); +} + +static inline fixed_t +get_shift_curve (const sonicloopvars_t *s) +{ + const angle_t th = get_pitch(FixedDiv( + s->revolution - s->min_revolution, + s->max_revolution - s->min_revolution)); + + // XY shift is transformed on wave scale; less movement + // at start and end of rotation, more halfway. + return FSIN((th / 2) - ANGLE_90); +} + +void P_HaltPlayerOrbit(player_t *player) +{ + // see P_PlayerOrbit + player->mo->flags &= ~(MF_NOCLIPHEIGHT); + + player->loop.radius = 0; +} + +void P_ExitPlayerOrbit(player_t *player) +{ + sonicloopvars_t *s = &player->loop; + + angle_t pitch = get_pitch(s->revolution); + angle_t yaw = s->yaw; + + fixed_t co, si; + fixed_t speed; + + if (s->radius < 0) + { + pitch += ANGLE_180; + } + + co = FCOS(pitch); + si = FSIN(pitch); + + speed = FixedMul(co, player->speed); + + P_InstaThrust(player->mo, yaw, speed); + + player->mo->momz = FixedMul(si, player->speed); + + if (speed < 0) + { + yaw += ANGLE_180; + } + + // excludes only extremely vertical angles + if (abs(co) * 4 > abs(si)) + { + P_SetPlayerAngle(player, yaw); + } + + if (s->flip) + { + player->mo->eflags ^= MFE_VERTICALFLIP; + player->mo->flags2 ^= MF2_OBJECTFLIP; + + P_SetPitchRoll(player->mo, + pitch + ANGLE_180, s->yaw); + } + + P_HaltPlayerOrbit(player); +} + +boolean P_PlayerOrbit(player_t *player) +{ + sonicloopvars_t *s = &player->loop; + + angle_t pitch; + + fixed_t xy, z; + fixed_t xs, ys; + + fixed_t step, th, left; + + fixed_t grav; + + if (s->radius == 0) + { + return false; + } + + grav = abs(P_GetMobjGravity(player->mo)); + + // Lose speed on the way up. revolution = 0.5 always + // points straight up. + if (abs(s->revolution & FRACMASK) < FRACUNIT/2) + { + player->speed -= grav; + } + else + { + player->speed += 4 * grav; + } + + pitch = get_pitch(s->revolution); + + xy = FixedMul(abs(s->radius), FSIN(pitch)); + z = FixedMul(abs(s->radius), -(FCOS(pitch))); + + th = get_shift_curve(s); + + xs = FixedMul(s->shift.x, th); + ys = FixedMul(s->shift.y, th); + + xs += FixedMul(xy, FCOS(s->yaw)); + ys += FixedMul(xy, FSIN(s->yaw)); + + P_MoveOrigin(player->mo, + s->origin.x + xs, + s->origin.y + ys, + s->origin.z + z); + + // Match rollangle to revolution + P_SetPitchRoll(player->mo, + s->radius < 0 ? (ANGLE_180 + pitch) : pitch, + s->yaw); + + // circumfrence = (2r)PI + step = FixedDiv(player->speed, + FixedMul(s->radius, M_TAU_FIXED)); + + left = (s->max_revolution - s->revolution); + + if (abs(left) < abs(step)) + { + P_ExitPlayerOrbit(player); + + return false; + } + + // If player slows down by too much, throw them out of + // the loop + if (player->speed < player->mo->scale) + { + P_HaltPlayerOrbit(player); + + return false; + } + + s->revolution += step; + + // We need to not clip the ground. It sucks but setting + // this flag is the only way to do that. + player->mo->flags |= (MF_NOCLIPHEIGHT); + + return true; +} diff --git a/src/p_map.c b/src/p_map.c index da446e095..28ce3f394 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -2386,6 +2386,22 @@ fixed_t P_GetThingStepUp(mobj_t *thing, fixed_t destX, fixed_t destY) return maxstep; } +static boolean P_UsingStepUp(mobj_t *thing) +{ + if (thing->flags & MF_NOCLIP) + { + return false; + } + + // orbits have no collision + if (thing->player && thing->player->loop.radius) + { + return false; + } + + return true; +} + static boolean increment_move ( mobj_t * thing, @@ -2438,7 +2454,7 @@ increment_move // copy into the spechitint buffer from spechit spechitint_copyinto(); - if (!(thing->flags & MF_NOCLIP)) + if (P_UsingStepUp(thing)) { //All things are affected by their scale. fixed_t maxstep = P_GetThingStepUp(thing, tryx, tryy); diff --git a/src/p_mobj.c b/src/p_mobj.c index 9101e72cd..89467575a 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -48,6 +48,7 @@ #include "k_bot.h" #include "k_terrain.h" #include "k_collide.h" +#include "k_objects.h" // BlanKart #include "blan/b_soc.h" @@ -3770,7 +3771,8 @@ static void P_PlayerMobjThinker(mobj_t *mobj) mobj->eflags &= ~MFE_JUSTSTEPPEDDOWN; // Zoom tube - if ((mobj->player->carry == CR_ZOOMTUBE && mobj->tracer && !P_MobjWasRemoved(mobj->tracer))) + if ((mobj->player->carry == CR_ZOOMTUBE && mobj->tracer && !P_MobjWasRemoved(mobj->tracer)) + || mobj->player->loop.radius != 0) { P_HitSpecialLines(mobj, mobj->x, mobj->y, mobj->momx, mobj->momy); P_UnsetThingPosition(mobj); @@ -12590,6 +12592,11 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean // Increment no. of capsules on the map counter maptargets++; } + case MT_LOOPCENTERPOINT: + { + Obj_InitLoopCenter(mobj); + break; + } default: break; } @@ -12819,6 +12826,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; @@ -12837,6 +12849,21 @@ static void P_SpawnItemRow(mapthing_t *mthing, mobjtype_t *itemtypes, UINT8 numi } z = P_GetMobjSpawnHeight(itemtypes[0], x, y, z, 0, mthing->layer, 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]; @@ -12844,15 +12871,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_saveg.c b/src/p_saveg.c index ad02f6455..1fbe6d1d6 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -356,6 +356,20 @@ static void P_NetArchivePlayers(void) WRITEFIXED(save_p, players[i].outrun); WRITEUINT8(save_p, players[i].outruntime); + + // sonicloopsvars_t + WRITEFIXED(save_p, players[i].loop.radius); + WRITEFIXED(save_p, players[i].loop.revolution); + WRITEFIXED(save_p, players[i].loop.min_revolution); + WRITEFIXED(save_p, players[i].loop.max_revolution); + WRITEANGLE(save_p, players[i].loop.yaw); + WRITEFIXED(save_p, players[i].loop.origin.x); + WRITEFIXED(save_p, players[i].loop.origin.y); + WRITEFIXED(save_p, players[i].loop.origin.z); + WRITEFIXED(save_p, players[i].loop.shift.x); + WRITEFIXED(save_p, players[i].loop.shift.y); + WRITEUINT8(save_p, players[i].loop.flip); + } } @@ -621,6 +635,19 @@ static void P_NetUnArchivePlayers(void) players[i].outrun = READFIXED(save_p); players[i].outruntime = READUINT8(save_p); + // sonicloopsvars_t + players[i].loop.radius = READFIXED(save_p); + players[i].loop.revolution = READFIXED(save_p); + players[i].loop.min_revolution = READFIXED(save_p); + players[i].loop.max_revolution = READFIXED(save_p); + players[i].loop.yaw = READANGLE(save_p); + players[i].loop.origin.x = READFIXED(save_p); + players[i].loop.origin.y = READFIXED(save_p); + players[i].loop.origin.z = READFIXED(save_p); + players[i].loop.shift.x = READFIXED(save_p); + players[i].loop.shift.y = READFIXED(save_p); + players[i].loop.flip = READUINT8(save_p); + //players[i].viewheight = P_GetPlayerViewHeight(players[i]); // scale cannot be factored in at this point } } diff --git a/src/p_setup.c b/src/p_setup.c index d63c5f334..b6142a9a2 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -859,11 +859,26 @@ void P_ScanThings(INT16 mapnum, INT16 wadnum, INT16 lumpnum) } #endif +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++) { @@ -872,14 +887,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) @@ -887,6 +910,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 } @@ -898,6 +922,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 @@ -905,6 +936,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! @@ -4319,6 +4369,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: @@ -7017,6 +7069,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 e57c1b21f..5a2974ada 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -510,6 +510,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. diff --git a/src/p_user.c b/src/p_user.c index 0c5938cda..991e6c6f6 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -4373,6 +4373,11 @@ void P_PlayerThink(player_t *player) P_DoZoomTube(player); player->rmomx = player->rmomy = 0; } + else if (player->loop.radius != 0) + { + P_PlayerOrbit(player); + player->rmomx = player->rmomy = 0; + } else { // Move around. @@ -4506,7 +4511,7 @@ void P_PlayerThink(player_t *player) player->typing_timer = 0; player->typing_duration = 0; } - + K_KartPlayerThink(player, cmd); // SRB2kart DoABarrelRoll(player);