diff --git a/src/d_player.h b/src/d_player.h index 2f27838bf..711d2c4b3 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -849,6 +849,16 @@ struct player_t fixed_t turbineheight; // height around the turbine boolean turbinespd; // if true, we used a sneaker and get the altpath. + // clouds (AGZ, AHZ, SSZ) + tic_t cloud; // timer while on cloud before launch + tic_t cloudlaunch; // timer set after launch for visuals + tic_t cloudbuf; // make sure we can't bounce off another cloud straight away + + // tulips (AGZ) + tic_t tulip; // timer before you get launched + tic_t tuliplaunch; // timer set after launch for visuals + tic_t tulipbuf; // make sure we can't enter another tulip straight away + // SINT8 lives; diff --git a/src/deh_tables.c b/src/deh_tables.c index f1273c78b..718cf2d55 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -4788,6 +4788,20 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_BLENDEYE_PUYO_DIE", "S_BLENDEYE_PUYO_DUST", + "S_AHZCLOUD", + + "S_AGZBULB_BASE", + "S_AGZBULB_NEUTRAL", + "S_AGZBULB_ANIM1", + "S_AGZBULB_ANIM2", + "S_AGZBULB_ANIM3", + "S_AGTR", + "S_AGFL", + "S_AGFF", + "S_AGZCLOUD", + + "S_SSZCLOUD", + "S_MEGABARRIER1", "S_MEGABARRIER2", "S_MEGABARRIER3", @@ -6135,6 +6149,21 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_BLENDEYE_PUYO", "MT_BLENDEYE_PUYO_DUST", "MT_BLENDEYE_PUYO_DUST_COFFEE", + + "MT_AHZ_CLOUD", + "MT_AHZ_CLOUDCLUSTER", + + "MT_AGZ_BULB", + "MT_AGZ_BULB_PART", + "MT_AGZ_TREE", + "MT_AGZ_AGFL", + "MT_AGZ_AGFF", + "MT_AGZ_CLOUD", + "MT_AGZ_CLOUDCLUSTER", + + "MT_SSZ_CLOUD", + "MT_SSZ_CLOUDCLUSTER", + "MT_MEGABARRIER", "MT_SEASAW_VISUAL", diff --git a/src/info.c b/src/info.c index b23860213..ed67a1076 100644 --- a/src/info.c +++ b/src/info.c @@ -965,6 +965,21 @@ char sprnames[NUMSPRITES + 1][5] = "PUYC", "PUYD", "PUYE", + + // Aerial Highlands + "BCLD", + + // Avant Garden + "AGTU", + "AGTL", + "AGTS", + "AGTR", + "AGFL", + "AGFF", + "AGCL", + + // Sky Sanctuary + "SSCL", "MGSH", // Mega Barrier @@ -5666,6 +5681,23 @@ state_t states[NUMSTATES] = {SPR_PUYA, 3, -1, {A_BlendEyePuyoHack}, 0, 0, S_NULL}, // S_BLENDEYE_PUYO_SHOCK, {SPR_PUYA, 4|FF_ANIMATE, 5, {A_BlendEyePuyoHack}, 2, 2, S_NULL}, // S_BLENDEYE_PUYO_DIE, {SPR_PUYA, 5, 2, {A_BlendEyePuyoHack}, 0, 0, S_BLENDEYE_PUYO_DIE}, // S_BLENDEYE_PUYO_DUST, + + // Aerial Highlands + {SPR_BCLD, FF_ANIMATE, -1, {NULL}, 3, 6, S_AHZCLOUD}, // S_AHZCLOUD + + // Avant Garden + {SPR_AGTL, FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_AGZBULB_BASE}, // S_AGZBULB_BASE + {SPR_AGTU, 0, -1, {NULL}, 0, 0, S_AGZBULB_NEUTRAL}, // S_AGZBULB_NEUTRAL + {SPR_AGTS, FF_ANIMATE, 8, {NULL}, 3, 2, S_AGZBULB_ANIM2}, // S_AGZBULB_ANIM1 + {SPR_AGTS, 4, 8, {NULL}, 0, 0, S_AGZBULB_ANIM3}, // S_AGZBULB_ANIM2 + {SPR_AGTS, FF_ANIMATE, 8, {NULL}, 3, 2, S_AGZBULB_NEUTRAL}, // S_AGZBULB_ANIM3 + {SPR_AGTR, 0, -1, {NULL}, 0, 0, S_AGTR}, // S_AGTR + {SPR_AGFL, 0, -1, {NULL}, 0, 0, S_AGFL}, // S_AGFL + {SPR_AGFF, 0, -1, {NULL}, 0, 0, S_AGFF}, // S_AGFF + {SPR_AGCL, FF_ANIMATE, -1, {NULL}, 3, 6, S_AGZCLOUD}, // S_AGZCLOUD + + // Sky Sanctuary + {SPR_SSCL, FF_ANIMATE, -1, {NULL}, 3, 6, S_SSZCLOUD}, // S_SSZCLOUD {SPR_MGSH, 2|FF_PAPERSPRITE|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_MEGABARRIER1, {SPR_MGSH, 1|FF_PAPERSPRITE|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_MEGABARRIER2, @@ -31854,6 +31886,303 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = MF_SCENERY|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOGRAVITY, // flags S_NULL // raisestate }, + + { // MT_AHZ_CLOUD + -1, // doomednum + S_AHZCLOUD, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // 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 + 64<turbinespd); + //clouds + else if (fastcmp(field,"cloud")) + lua_pushinteger(L, plr->cloud); + else if (fastcmp(field,"cloudlaunch")) + lua_pushinteger(L, plr->cloudlaunch); + else if (fastcmp(field,"cloudbuf")) + lua_pushinteger(L, plr->cloudbuf); + + //tulips + else if (fastcmp(field,"tulip")) + lua_pushinteger(L, plr->tulip); + else if (fastcmp(field,"tuliplaunch")) + lua_pushinteger(L, plr->tuliplaunch); + else if (fastcmp(field,"tulipbuf")) + lua_pushinteger(L, plr->tulipbuf); + else if (fastcmp(field,"charflags")) lua_pushinteger(L, plr->charflags); else if (fastcmp(field,"followitem")) @@ -1035,6 +1051,22 @@ static int player_set(lua_State *L) else if (fastcmp(field,"turbinespd")) plr->turbinespd = luaL_checkinteger(L, 3); + // clouds + else if (fastcmp(field,"cloud")) + plr->cloud = luaL_checkinteger(L, 3); + else if (fastcmp(field,"cloudlaunch")) + plr->cloudlaunch = luaL_checkinteger(L, 3); + else if (fastcmp(field,"cloudbuf")) + plr->cloudbuf = luaL_checkinteger(L, 3); + + // tulips + else if (fastcmp(field,"tulip")) + plr->tulip = luaL_checkinteger(L, 3); + else if (fastcmp(field,"tuliplaunch")) + plr->tuliplaunch = luaL_checkinteger(L, 3); + else if (fastcmp(field,"tulipbuf")) + plr->tulipbuf = luaL_checkinteger(L, 3); + // else if (fastcmp(field,"charflags")) plr->charflags = (UINT32)luaL_checkinteger(L, 3); diff --git a/src/objects/CMakeLists.txt b/src/objects/CMakeLists.txt index d6b1798fd..04c4bc6b9 100644 --- a/src/objects/CMakeLists.txt +++ b/src/objects/CMakeLists.txt @@ -50,6 +50,7 @@ target_sources(SRB2SDL2 PRIVATE rocks.cpp emz-faucet.cpp trick-balloon.c + cloud.c ) add_subdirectory(versus) diff --git a/src/objects/cloud.c b/src/objects/cloud.c new file mode 100644 index 000000000..b0f011dc2 --- /dev/null +++ b/src/objects/cloud.c @@ -0,0 +1,293 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2022 by Sally "TehRealSalt" Cochenour +// Copyright (C) 2022 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 cloud.c +/// \brief Launcher clouds and tulips used for Aerial Highlands, Avant Garden, and Sky Sanctuary. + +#include "../p_local.h" +#include "../k_objects.h" +#include "../g_game.h" +#include "../info.h" +#include "../s_sound.h" +#include "../r_main.h" +#include "../m_random.h" + + +#define BULB_ZTHRUST 96*FRACUNIT +#define CLOUD_ZTHRUST 32*FRACUNIT +#define CLOUDB_ZTHRUST 16*FRACUNIT + +void Obj_CloudSpawn(mobj_t *mobj) +{ + mobjtype_t cloudtype; + + switch (mobj->type) + { + case MT_AHZ_CLOUDCLUSTER: + cloudtype = MT_AHZ_CLOUD; + break; + case MT_AGZ_CLOUDCLUSTER: + cloudtype = MT_AGZ_CLOUD; + break; + case MT_SSZ_CLOUDCLUSTER: + cloudtype = MT_SSZ_CLOUD; + break; + default: + return; + } + + if (mobj->type != MT_AGZ_CLOUDCLUSTER) + { + mobj->destscale = mapobjectscale * 4; + P_SetScale(mobj, mobj->destscale); + } + + mobj_t *cloud = P_SpawnMobj(mobj->x, mobj->y, mobj->z, cloudtype); + angle_t ang = mobj->angle; + UINT8 dist = 128; + + if (cloudtype == MT_AGZ_CLOUD) + { + cloud->destscale = cloud->scale * 2; + P_SetScale(cloud, cloud->destscale); + } + + for (UINT8 i = 0; i < 4; i++) + { + fixed_t x = mobj->x + FixedMul(mapobjectscale, dist * FINECOSINE(ang >> ANGLETOFINESHIFT)); + fixed_t y = mobj->y + FixedMul(mapobjectscale, dist * FINESINE(ang >> ANGLETOFINESHIFT)); + + cloud = P_SpawnMobj(x, y, mobj->z, cloudtype); + + if (cloudtype == MT_AGZ_CLOUD) + { + cloud->destscale = cloud->scale * 2; + P_SetScale(cloud, cloud->destscale); + cloud->frame = P_RandomRange(PR_DECORATION, 0, 3); + } + + ang += ANGLE_90; + } +} + +void Obj_TulipSpawnerThink(mobj_t *mobj) +{ + if (!mobj->tracer) + { + // I have no idea if doing it this way is correct + mobj_t *part1 = P_SpawnMobj(0, 0, 0, MT_AGZ_BULB_PART); + mobj_t *part2 = P_SpawnMobj(0, 0, 0, MT_AGZ_BULB_PART); + mobj_t *tracer = P_SpawnMobj(0, 0, 0, MT_AGZ_BULB_PART); + + P_SetTarget(&mobj->hnext, part1); + P_SetTarget(&mobj->hnext->hnext, part2); + + P_SetMobjState(mobj->hnext, S_AGZBULB_BASE); + P_SetMobjState(mobj->hnext->hnext, S_AGZBULB_BASE); + + P_SetTarget(&mobj->tracer, tracer); + P_SetMobjState(mobj->tracer, S_AGZBULB_NEUTRAL); + } + + angle_t a = mobj->angle + ANG1*45; + mobj_t *part = mobj->hnext; + + while (part) + { + P_MoveOrigin(part, mobj->x, mobj->y, mobj->z); + part->angle = a; + part->destscale = mobj->scale; + P_SetScale(part, part->destscale); + part->flags2 = mobj->flags2; + part->eflags = mobj->eflags; + a += ANG1*90; + part = part->hnext; + } + + mobj_t *b = mobj->tracer; + + P_MoveOrigin(b, mobj->x, mobj->y, mobj->z); + b->destscale = mobj->scale; + P_SetScale(b, b->destscale); + b->flags2 = mobj->flags2; + b->eflags = mobj->eflags; + b->color = SKINCOLOR_MAGENTA; + + if (b->state == &states[S_AGZBULB_ANIM2]) + { + if (leveltime & 1) + b->colorized = true; + else + b->colorized = false; + } + else + b->colorized = false; +} + +void Obj_PlayerCloudThink(player_t *player) +{ + mobj_t *mo = player->mo; + + if (player->cloudbuf) + player->cloudbuf--; + + if (player->cloudlaunch) + { + player->cloudlaunch--; + + if (leveltime % 6 == 0) + P_SpawnMobj(mo->x + P_RandomRange(PR_DECORATION, -8, 8)*mapobjectscale, mo->y + P_RandomRange(PR_DECORATION, -8, 8)*mapobjectscale, mo->z, MT_DRIFTDUST); + } + + if (player->cloud) + { + player->cloud--; + P_InstaThrust(mo, 0, 0); + mo->momz = 0; + player->fastfall = 0; + + if (!player->cloud) + { + if (P_MobjWasRemoved(mo->tracer)) + return; + + switch(mo->tracer->type) + { + case MT_AHZ_CLOUD: + P_SetObjectMomZ(mo, CLOUDB_ZTHRUST, false); + break; + case MT_AGZ_CLOUD: + mo->momz = FixedMul(mapobjectscale, CLOUD_ZTHRUST * P_MobjFlip(mo->tracer)); + break; + case MT_SSZ_CLOUD: + P_SetObjectMomZ(mo, CLOUD_ZTHRUST, false); + break; + default: + break; + } + player->cloudlaunch = TICRATE; + + P_InstaThrust(mo, mo->cusval, mo->cvmem); + } + } +} + +void Obj_PlayerBulbThink(player_t *player) +{ + mobj_t *mo = player->mo; + + if (player->tuliplaunch) + { + player->tuliplaunch--; + + if (leveltime % 2 == 0) + P_SpawnMobj(mo->x + P_RandomRange(PR_DECORATION, -8, 8)*mapobjectscale, mo->y + P_RandomRange(PR_DECORATION, -8, 8)*mapobjectscale, mo->z, MT_DRIFTDUST); + } + + if (player->tulipbuf) + player->tulipbuf--; + + if (player->tulip) + { + player->tulip--; + P_MoveOrigin(mo, mo->tracer->x, mo->tracer->y, mo->tracer->z); + mo->flags &= ~MF_SHOOTABLE; + mo->renderflags |= RF_DONTDRAW; + } + + if (player->tulip == 1) // expired + { + + S_StartSound(mo, sfx_s3k81); + + for (UINT8 i = 1; i < 16; i++) + { + mobj_t *d = P_SpawnMobj(mo->x, mo->y, mo->z, MT_DRIFTDUST); + d->angle = ANGLE_MAX/16 * i; + P_InstaThrust(d, d->angle, mapobjectscale*23); + d->momz = mapobjectscale*8*P_MobjFlip(mo->tracer); + } + + mo->renderflags &= ~RF_DONTDRAW; + mo->player->nocontrol = 0; + P_InstaThrust(mo, mo->tracer->extravalue2, mo->tracer->extravalue1); + mo->momz = FixedMul(mapobjectscale, BULB_ZTHRUST)*P_MobjFlip(mo->tracer); + + mo->flags |= MF_SHOOTABLE; + player->tuliplaunch = TICRATE; + player->tulipbuf = 8; + player->tulip = 0; + P_SetTarget(&mo->tracer->target, NULL); + P_SetTarget(&mo->tracer, NULL); + } +} + +void Obj_CloudTouched(mobj_t *special, mobj_t *toucher) +{ + player_t *player = toucher->player; + + if (player->cloudbuf || player->cloud) + return; + + player->cloud = TICRATE/8; + player->cloudbuf = TICRATE/3; + + for (UINT8 i = 1; i < 6; i++) + { + mobj_t *spawn = P_SpawnMobj(toucher->x + P_RandomRange(PR_DECORATION, -32, 32)*mapobjectscale, toucher->y + P_RandomRange(PR_DECORATION, -32, 32)*mapobjectscale, toucher->z, MT_DRIFTDUST); + spawn->angle = R_PointToAngle2(toucher->x, toucher->y, spawn->x, spawn->y); + P_InstaThrust(spawn, spawn->angle, P_RandomRange(PR_DECORATION, 1, 8)*mapobjectscale); + P_SetObjectMomZ(spawn, P_RandomRange(PR_DECORATION, 4, 10)<destscale = mapobjectscale * 3; + } + + toucher->cvmem = FixedHypot(toucher->momx, toucher->momy); + + if (toucher->cvmem) + toucher->cusval = R_PointToAngle2(0, 0, toucher->momx, toucher->momy); + + if (toucher->cvmem < mapobjectscale*8) + toucher->cvmem = mapobjectscale*8; + + P_SetTarget(&toucher->tracer, special); + S_StartSound(toucher, sfx_s3k8a); + +} + +void Obj_BulbTouched(mobj_t *special, mobj_t *toucher) +{ + if (toucher->player->tulip || toucher->player->tulipbuf) + return; + + if (special && special->target) + return; // player already using it + + if (toucher->player->respawn.timer) + return; + + toucher->player->tulip = 8*2 +1; + + fixed_t spd = FixedHypot(toucher->momx, toucher->momy); + angle_t ang = R_PointToAngle2(0, 0, toucher->momx, toucher->momy); + + P_InstaThrust(toucher, 0, 0); + P_MoveOrigin(toucher, special->x, special->y, special->z); + toucher->player->nocontrol = 1; + P_SetTarget(&toucher->tracer, special); + toucher->flags &= ~MF_SHOOTABLE; + toucher->renderflags |= RF_DONTDRAW; + P_SetTarget(&special->target, toucher); + special->extravalue1 = spd; + special->extravalue2 = ang; + + S_StartSound(special, sfx_s254); + + // set bulb state: + P_SetMobjState(special->tracer, S_AGZBULB_ANIM1); +} diff --git a/src/p_inter.c b/src/p_inter.c index 076bc6011..2b413dd3e 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -959,6 +959,16 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) Obj_DLZRocketSpecial(special, player); return; + case MT_AHZ_CLOUD: + case MT_AGZ_CLOUD: + case MT_SSZ_CLOUD: + Obj_CloudTouched(special, toucher); + return; + + case MT_AGZ_BULB: + Obj_BulbTouched(special, toucher); + return; + case MT_BALLSWITCH_BALL: { Obj_BallSwitchTouched(special, toucher); diff --git a/src/p_mobj.c b/src/p_mobj.c index 689970281..17e115b17 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -10272,6 +10272,10 @@ static boolean P_MobjRegularThink(mobj_t *mobj) Obj_GPZSeasawThink(mobj); break; + case MT_AGZ_BULB: + Obj_TulipSpawnerThink(mobj); + break; + case MT_BALLSWITCH_BALL: { Obj_BallSwitchThink(mobj); @@ -11803,6 +11807,11 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) case MT_WATERPALACETURBINE: Obj_WPZTurbineSpawn(mobj); break; + case MT_AHZ_CLOUDCLUSTER: + case MT_AGZ_CLOUDCLUSTER: + case MT_SSZ_CLOUDCLUSTER: + Obj_CloudSpawn(mobj); + break; case MT_SNEAKERPANEL: Obj_SneakerPanelSpawn(mobj); break; diff --git a/src/p_saveg.c b/src/p_saveg.c index 88bb493d7..69fde5aa7 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -625,6 +625,14 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEFIXED(save->p, players[i].turbineheight); WRITEUINT8(save->p, players[i].turbinespd); + WRITEUINT32(save->p, players[i].cloud); + WRITEUINT32(save->p, players[i].cloudlaunch); + WRITEUINT32(save->p, players[i].cloudbuf); + + WRITEUINT32(save->p, players[i].tulip); + WRITEUINT32(save->p, players[i].tuliplaunch); + WRITEUINT32(save->p, players[i].tulipbuf); + // respawnvars_t WRITEUINT8(save->p, players[i].respawn.state); WRITEUINT32(save->p, K_GetWaypointHeapIndex(players[i].respawn.wp)); @@ -1186,6 +1194,14 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].turbineheight = READFIXED(save->p); players[i].turbinespd = (boolean)READUINT8(save->p); + players[i].cloud = (tic_t)READUINT32(save->p); + players[i].cloudlaunch = (tic_t)READUINT32(save->p); + players[i].cloudbuf = (tic_t)READUINT32(save->p); + + players[i].tulip = (tic_t)READUINT32(save->p); + players[i].tuliplaunch = (tic_t)READUINT32(save->p); + players[i].tulipbuf = (tic_t)READUINT32(save->p); + // respawnvars_t players[i].respawn.state = READUINT8(save->p); players[i].respawn.wp = (waypoint_t *)(size_t)READUINT32(save->p);