diff --git a/src/dehacked.c b/src/dehacked.c index 1fa855c9a..cc0c336d3 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -10430,6 +10430,8 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_WATERTRAIL", "MT_WATERTRAILUNDERLAY", + "MT_PAPERITEMSPOT", + #ifdef SEENAMES "MT_NAMECHECK", #endif diff --git a/src/doomstat.h b/src/doomstat.h index b8d82f379..57738aaa2 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -459,27 +459,28 @@ enum GameTypeRules GTR_BOTS = 1<<2, // Allows bots in this gametype. Combine with BotTiccmd hooks to make bots support your gametype. // Battle gametype rules - GTR_SPHERES = 1<<3, // Replaces rings with blue spheres - GTR_BUMPERS = 1<<4, // Enables the bumper health system - GTR_WANTED = 1<<5, // Enables the wanted anti-camping system - GTR_KARMA = 1<<6, // Enables the Karma system if you're out of bumpers - GTR_ITEMARROWS = 1<<7, // Show item box arrows above players - GTR_CAPSULES = 1<<8, // Enables the wanted anti-camping system - GTR_BATTLESTARTS = 1<<9, // Use Battle Mode start positions. + GTR_BUMPERS = 1<<3, // Enables the bumper health system + GTR_SPHERES = 1<<4, // Replaces rings with blue spheres + GTR_PAPERITEMS = 1<<5, // Replaces item boxes with paper item spawners + GTR_WANTED = 1<<6, // Enables the wanted anti-camping system + GTR_KARMA = 1<<7, // Enables the Karma system if you're out of bumpers + GTR_ITEMARROWS = 1<<8, // Show item box arrows above players + GTR_CAPSULES = 1<<9, // Enables the wanted anti-camping system + GTR_BATTLESTARTS = 1<<10, // Use Battle Mode start positions. - GTR_POINTLIMIT = 1<<10, // Reaching point limit ends the round - GTR_TIMELIMIT = 1<<11, // Reaching time limit ends the round - GTR_OVERTIME = 1<<12, // Allow overtime behavior + GTR_POINTLIMIT = 1<<11, // Reaching point limit ends the round + GTR_TIMELIMIT = 1<<12, // Reaching time limit ends the round + GTR_OVERTIME = 1<<13, // Allow overtime behavior // Custom gametype rules - GTR_TEAMS = 1<<13, // Teams are forced on - GTR_NOTEAMS = 1<<14, // Teams are forced off - GTR_TEAMSTARTS = 1<<15, // Use team-based start positions + GTR_TEAMS = 1<<14, // Teams are forced on + GTR_NOTEAMS = 1<<15, // Teams are forced off + GTR_TEAMSTARTS = 1<<16, // Use team-based start positions // Grand Prix rules - GTR_CAMPAIGN = 1<<16, // Handles cup-based progression - GTR_LIVES = 1<<17, // Lives system, players are forced to spectate during Game Over. - GTR_SPECIALBOTS = 1<<18, // Bot difficulty gets stronger between rounds, and the rival system is enabled. + GTR_CAMPAIGN = 1<<17, // Handles cup-based progression + GTR_LIVES = 1<<18, // Lives system, players are forced to spectate during Game Over. + GTR_SPECIALBOTS = 1<<19, // Bot difficulty gets stronger between rounds, and the rival system is enabled. // free: to and including 1<<31 }; diff --git a/src/g_game.c b/src/g_game.c index 4378b014e..793ec8dbb 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2793,7 +2793,7 @@ UINT32 gametypedefaultrules[NUMGAMETYPES] = // Race GTR_CIRCUIT|GTR_BOTS, // Battle - GTR_SPHERES|GTR_BUMPERS|GTR_WANTED|GTR_KARMA|GTR_ITEMARROWS|GTR_CAPSULES|GTR_BATTLESTARTS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME + GTR_SPHERES|GTR_BUMPERS|GTR_PAPERITEMS|GTR_WANTED|GTR_KARMA|GTR_ITEMARROWS|GTR_CAPSULES|GTR_BATTLESTARTS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME }; // diff --git a/src/info.c b/src/info.c index 8bd117731..90da90918 100644 --- a/src/info.c +++ b/src/info.c @@ -28680,6 +28680,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_PAPERITEMSPOT + -1, // 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_NOBLOCKMAP|MF_NOCLIPTHING|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + // ============================================================================================================================// #ifdef SEENAMES diff --git a/src/info.h b/src/info.h index b56f436b2..e7d21744e 100644 --- a/src/info.h +++ b/src/info.h @@ -6312,6 +6312,8 @@ typedef enum mobj_type MT_WATERTRAIL, MT_WATERTRAILUNDERLAY, + MT_PAPERITEMSPOT, + #ifdef SEENAMES MT_NAMECHECK, #endif diff --git a/src/k_battle.c b/src/k_battle.c index 518ae260f..b3939dad8 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -277,6 +277,127 @@ void K_CheckBumpers(void) P_DoPlayerExit(&players[i]); } +void K_RunPaperItemSpawners(void) +{ + const boolean overtime = (battleovertime.enabled >= 10*TICRATE); + tic_t interval = 8*TICRATE; + + if (leveltime <= starttime) + { + return; + } + + if ((battleovertime.enabled > 0) && (battleovertime.radius < 256*mapobjectscale)) + { + return; + } + + if (overtime == true) + { + interval /= 2; + } + + if (((leveltime - starttime - (interval / 2)) % interval) != 0) + { + return; + } + + if (overtime == true) + { + SINT8 flip = 1; + + K_CreatePaperItem( + battleovertime.x, battleovertime.y, battleovertime.z + (128 * mapobjectscale * flip), + FixedAngle(P_RandomRange(0, 359) * FRACUNIT), flip, + 0, 0 + ); + } + else + { + UINT8 pcount = 0; + UINT8 i; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator + || players[i].exiting > 0 + || players[i].eliminated) + { + continue; + } + + if ((gametyperules & GTR_BUMPERS) && players[i].bumpers <= 0) + { + continue; + } + + pcount++; + } + + if (pcount > 0) + { +#define MAXITEM 64 + UINT16 item = 0; + mobj_t *spotList[MAXITEM]; + boolean spotUsed[MAXITEM]; + + thinker_t *th; + mobj_t *mo; + + for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) + { + if (item >= MAXITEM) + break; + + if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) + continue; + + mo = (mobj_t *)th; + + if (mo->type == MT_PAPERITEMSPOT) + { + spotList[item] = mo; + item++; + } + } + + if (item <= 0) + { + return; + } + + for (i = 0; i < min(item, pcount); i++) + { + UINT8 r = P_RandomRange(0, item-1); + UINT8 recursion = 0; + mobj_t *drop = NULL; + SINT8 flip = 1; + + while (spotUsed[r] == true) + { + r = P_RandomRange(0, item-1); + + if ((recursion++) > 32) + { + break; + } + } + + flip = P_MobjFlip(spotList[r]); + + drop = K_CreatePaperItem( + spotList[r]->x, spotList[r]->y, spotList[r]->z + (128 * mapobjectscale * flip), + FixedAngle(P_RandomRange(0, 359) * FRACUNIT), flip, + 0, 0 + ); + + K_FlipFromObject(drop, spotList[r]); + spotUsed[r] = true; + } + } + } +} + static void K_SpawnOvertimeLaser(fixed_t x, fixed_t y, fixed_t scale) { UINT8 i, j; @@ -359,8 +480,6 @@ static void K_SpawnOvertimeLaser(fixed_t x, fixed_t y, fixed_t scale) } } -#undef MAXPLANESPERSECTOR - void K_RunBattleOvertime(void) { if (battleovertime.enabled < 10*TICRATE) diff --git a/src/k_battle.h b/src/k_battle.h index 3f63e25f2..90ee20df7 100644 --- a/src/k_battle.h +++ b/src/k_battle.h @@ -20,6 +20,7 @@ boolean K_IsPlayerWanted(player_t *player); void K_CalculateBattleWanted(void); void K_SpawnBattlePoints(player_t *source, player_t *victim, UINT8 amount); void K_CheckBumpers(void); +void K_RunPaperItemSpawners(void); void K_RunBattleOvertime(void); void K_SetupMovingCapsule(mapthing_t *mt, mobj_t *mobj); void K_SpawnBattleCapsules(void); diff --git a/src/k_kart.c b/src/k_kart.c index cc9824722..273903ff0 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -4342,6 +4342,118 @@ void K_DropHnextList(player_t *player, boolean keepshields) } } +mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT8 type, UINT8 amount) +{ + mobj_t *drop = P_SpawnMobj(x, y, z, MT_FLOATINGITEM); + P_SetScale(drop, drop->scale>>4); + drop->destscale = (3*drop->destscale)/2; + + drop->angle = angle; + P_Thrust(drop, + FixedAngle(P_RandomFixed() * 180) + angle, + 16*mapobjectscale); + + drop->momz = flip * 3 * mapobjectscale; + if (drop->eflags & MFE_UNDERWATER) + drop->momz = (117 * drop->momz) / 200; + + if (type == 0) + { + UINT8 useodds = 0; + INT32 spawnchance[NUMKARTRESULTS]; + INT32 totalspawnchance = 0; + INT32 i; + + memset(spawnchance, 0, sizeof (spawnchance)); + + useodds = amount; + + for (i = 1; i < NUMKARTRESULTS; i++) + spawnchance[i] = (totalspawnchance += K_KartGetItemOdds(useodds, i, 0, false, false, false)); + + if (totalspawnchance > 0) + { + UINT8 newType; + UINT8 newAmount; + + totalspawnchance = P_RandomKey(totalspawnchance); + for (i = 0; i < NUMKARTRESULTS && spawnchance[i] <= totalspawnchance; i++); + + // TODO: this is bad! + // K_KartGetItemResult requires a player + // but item roulette will need rewritten to change this + + switch (i) + { + // Special roulettes first, then the generic ones are handled by default + case KRITEM_DUALSNEAKER: // Sneaker x2 + newType = KITEM_SNEAKER; + newAmount = 2; + break; + case KRITEM_TRIPLESNEAKER: // Sneaker x3 + newType = KITEM_SNEAKER; + newAmount = 3; + break; + case KRITEM_TRIPLEBANANA: // Banana x3 + newType = KITEM_BANANA; + newAmount = 3; + break; + case KRITEM_TENFOLDBANANA: // Banana x10 + newType = KITEM_BANANA; + newAmount = 10; + break; + case KRITEM_TRIPLEORBINAUT: // Orbinaut x3 + newType = KITEM_ORBINAUT; + newAmount = 3; + break; + case KRITEM_QUADORBINAUT: // Orbinaut x4 + newType = KITEM_ORBINAUT; + newAmount = 4; + break; + case KRITEM_DUALJAWZ: // Jawz x2 + newType = KITEM_JAWZ; + newAmount = 2; + break; + default: + newType = i; + newAmount = 1; + break; + } + + if (newAmount > 1) + { + UINT8 j; + + for (j = 0; j < newAmount-1; j++) + { + K_CreatePaperItem( + x, y, z, + angle, flip, + newType, 1 + ); + } + } + + drop->threshold = newType; + drop->movecount = 1; + } + else + { + drop->threshold = 1; + drop->movecount = 1; + } + } + else + { + drop->threshold = type; + drop->movecount = amount; + } + + drop->flags |= MF_NOCLIPTHING; + + return drop; +} + // For getting EXTRA hit! void K_DropItems(player_t *player) { @@ -4349,24 +4461,13 @@ void K_DropItems(player_t *player) if (player->mo && !P_MobjWasRemoved(player->mo) && player->kartstuff[k_itemamount] > 0) { - mobj_t *drop = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, MT_FLOATINGITEM); - P_SetScale(drop, drop->scale>>4); - drop->destscale = (3*drop->destscale)/2; - - drop->angle = player->mo->angle + ANGLE_90; - P_Thrust(drop, - FixedAngle(P_RandomFixed()*180) + player->mo->angle + ANGLE_90, - 16*mapobjectscale); - drop->momz = P_MobjFlip(player->mo)*3*mapobjectscale; - if (drop->eflags & MFE_UNDERWATER) - drop->momz = (117 * drop->momz) / 200; - - drop->threshold = player->kartstuff[k_itemtype]; - drop->movecount = player->kartstuff[k_itemamount]; + mobj_t *drop = K_CreatePaperItem( + player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, + player->mo->angle + ANGLE_90, P_MobjFlip(player->mo), + player->kartstuff[k_itemtype], player->kartstuff[k_itemamount] + ); K_FlipFromObject(drop, player->mo); - - drop->flags |= MF_NOCLIPTHING; } K_StripItems(player); diff --git a/src/k_kart.h b/src/k_kart.h index 0a4765b32..115b7f47c 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -70,6 +70,7 @@ INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue); INT32 K_GetKartDriftSparkValue(player_t *player); void K_SpawnDriftBoostExplosion(player_t *player, int stage); void K_KartUpdatePosition(player_t *player); +mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT8 type, UINT8 amount); void K_DropItems(player_t *player); void K_DropRocketSneaker(player_t *player); void K_DropKitchenSink(player_t *player); diff --git a/src/p_mobj.c b/src/p_mobj.c index b1b415114..7d5d04bdc 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -10512,6 +10512,9 @@ static mobjtype_t P_GetMobjtypeSubstitute(mapthing_t *mthing, mobjtype_t i) if ((gametyperules & GTR_SPHERES) && (i == MT_RING)) return MT_BLUESPHERE; + if ((gametyperules & GTR_PAPERITEMS) && (i == MT_RANDOMITEM)) + return MT_PAPERITEMSPOT; + return i; } diff --git a/src/p_tick.c b/src/p_tick.c index 79e71c655..5a9acefa1 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -595,6 +595,9 @@ void P_Ticker(boolean run) if ((gametyperules & GTR_BUMPERS) && battleovertime.enabled) K_RunBattleOvertime(); + if (gametyperules & GTR_PAPERITEMS) + K_RunPaperItemSpawners(); + ps_lua_thinkframe_time = I_GetTimeMicros(); LUAh_ThinkFrame(); ps_lua_thinkframe_time = I_GetTimeMicros() - ps_lua_thinkframe_time; @@ -755,6 +758,9 @@ void P_PreTicker(INT32 frames) if ((gametyperules & GTR_BUMPERS) && battleovertime.enabled) K_RunBattleOvertime(); + if (gametyperules & GTR_PAPERITEMS) + K_RunPaperItemSpawners(); + LUAh_ThinkFrame(); // Run shield positioning