Hyudoro: carry item capsule back to player, use MT_EMERALD for collect animation

- Carry back item capsule, painted with the skincolor of
  the player who was stolen from
- Give player item by orbiting and shrinking into the
  player like an emerald
  - Lock player item box while animation plays, draw an
    empty item box on the HUD
  - At end of animation, burst item capsule to place item
    in player's hotbar
- Hyudoros can no longer stack items in the hotbar --
  consistent with item capsules
  - (If you have a Super Ring in your hotbar and Hyudoro
    is holding a Super Ring too, it will wait for you to
    use yours)

---

MT_ITEMCAPSULE changes:

- extravalue2 != 0: use this color on caps
- MF2_STRONGBOX: award items like the roulette
  - Super Ring is placed in the hotbar instead of awarding
    rings directly
  - SPB is placed in the hotbar instead of throwing it
    immediately
- DMG_INSTAKILL: don't respawn
This commit is contained in:
James R. 2023-09-17 21:31:50 -07:00
parent 3eeaaefc5f
commit 0a6fedc48a
4 changed files with 185 additions and 71 deletions

View file

@ -304,13 +304,8 @@ void Obj_BeginEmeraldOrbit(mobj_t *emerald, mobj_t *target, fixed_t radius, INT3
spawn_lens_flare(emerald);
}
void Obj_GiveEmerald(mobj_t *emerald)
static void give_player(mobj_t *emerald)
{
if (P_MobjWasRemoved(emerald_orbit(emerald)) || P_MobjWasRemoved(emerald_award(emerald)))
{
return;
}
player_t *player = emerald_award(emerald)->player;
if (!player)
@ -324,6 +319,37 @@ void Obj_GiveEmerald(mobj_t *emerald)
S_StartSound(emerald_award(emerald), emerald->info->deathsound);
}
void Obj_GiveEmerald(mobj_t *emerald)
{
if (P_MobjWasRemoved(emerald_orbit(emerald)) || P_MobjWasRemoved(emerald_award(emerald)))
{
return;
}
// FIXME: emerald orbiting behavior should become its own object. For now,
// though, enjoy these special conditions!
switch (emerald_award(emerald)->type)
{
case MT_PLAYER:
give_player(emerald);
break;
case MT_ITEMCAPSULE: // objects/hyudoro.c
// DMG_INSTAKILL to kill it without respawning later
P_KillMobj(emerald_award(emerald), emerald_orbit(emerald), emerald_orbit(emerald), DMG_INSTAKILL);
if (emerald_orbit(emerald)->player)
{
// Unlock item for stacked Hyudoros
emerald_orbit(emerald)->player->itemRoulette.reserved = 0;
}
break;
default:
break;
}
}
void Obj_SetEmeraldAwardee(mobj_t *emerald, mobj_t *awardee)
{
P_SetTarget(&emerald_award(emerald), awardee);

View file

@ -28,6 +28,7 @@ enum {
HYU_PATROL,
HYU_RETURN,
HYU_HOVER,
HYU_ORBIT,
};
// TODO: make these general functions
@ -38,30 +39,19 @@ K_GetSpeed (mobj_t *mobj)
return FixedHypot(mobj->momx, mobj->momy);
}
static void
K_ChangePlayerItem
( player_t * player,
INT32 itemtype,
INT32 itemamount)
{
player->itemtype = itemtype;
player->itemamount = itemamount;
K_UnsetItemOut(player);
}
#define hyudoro_mode(o) ((o)->extravalue1)
#define hyudoro_itemtype(o) ((o)->movefactor)
#define hyudoro_itemcount(o) ((o)->movecount)
#define hyudoro_hover_stack(o) ((o)->threshold)
#define hyudoro_next(o) ((o)->tracer)
#define hyudoro_stackpos(o) ((o)->reactiontime)
#define hyudoro_delivered(o) (hyudoro_itemtype(o) == KITEM_NONE)
// cannot be combined
#define hyudoro_center(o) ((o)->target)
#define hyudoro_target(o) ((o)->target)
#define hyudoro_stolefrom(o) ((o)->hnext)
#define hyudoro_capsule(o) ((o)->hprev)
#define hyudoro_timer(o) ((o)->movedir)
#define hyudoro_center_max_radius(o) ((o)->threshold)
@ -222,6 +212,30 @@ project_hyudoro_hover (mobj_t *hyu)
bob_in_place(hyu, 64);
}
static boolean
project_hyudoro_orbit (mobj_t *hyu)
{
mobj_t *orbit = hyudoro_target(hyu);
if (P_MobjWasRemoved(orbit))
{
return false;
}
P_MoveOrigin(hyu, orbit->x, orbit->y, orbit->z);
hyu->destscale = orbit->scale;
mobj_t *facing = orbit->target;
if (!P_MobjWasRemoved(facing))
{
hyu->angle = R_PointToAngle2(
hyu->x, hyu->y, facing->x, facing->y);
}
return true;
}
static mobj_t *
find_duel_target (mobj_t *ignore)
{
@ -276,12 +290,6 @@ do_confused (mobj_t *hyu)
// Hyudoro is confused.
// Spin around, try to find a new target.
if (hyudoro_delivered(hyu))
{
// Already delivered, not confused
return;
}
// Try to find new target
P_SetTarget(&hyudoro_target(hyu),
find_duel_target(hyudoro_stolefrom(hyu)));
@ -336,32 +344,31 @@ move_to_player (mobj_t *hyu)
static void
deliver_item (mobj_t *hyu)
{
mobj_t *target = hyudoro_target(hyu);
player_t *player = target->player;
/* set physical position to visual position in stack */
hyu->z += hyu->momz;
hyu->momz = 0;
P_SetTarget(&hyudoro_target(hyu), NULL);
mobj_t *emerald = P_SpawnMobjFromMobj(
hyu, 0, 0, 0, MT_EMERALD);
if (player)
{
K_ChangePlayerItem(player,
hyudoro_itemtype(hyu),
player->itemamount + hyudoro_itemcount(hyu));
}
/* only want emerald for its orbiting behavior, so make
it invisible */
P_SetMobjState(emerald, S_INVISIBLE);
S_StartSound(target, sfx_itpick);
Obj_BeginEmeraldOrbit(
emerald, hyudoro_target(hyu), 0, 64, 128);
// Stop moving here
hyu->momx = 0;
hyu->momy = 0;
/* See Obj_GiveEmerald. I won't risk relying on the
Hyudoro object in case it is removed first. So go
through the capsule instead. */
Obj_SetEmeraldAwardee(emerald, hyudoro_capsule(hyu));
hyu->tics = 4;
/* hyudoro will teleport to emerald (orbit the player) */
hyudoro_mode(hyu) = HYU_ORBIT;
P_SetTarget(&hyudoro_target(hyu), emerald);
hyu->destscale = target->scale / 4;
hyu->scalespeed =
abs(hyu->scale - hyu->destscale) / hyu->tics;
// sets as already delivered
hyudoro_itemtype(hyu) = KITEM_NONE;
hyu->renderflags &= ~(RF_DONTDRAW | RF_BLENDMASK);
reset_shadow(hyu);
}
static void
@ -431,9 +438,71 @@ pop_hyudoro (mobj_t **head)
while (is_hyudoro(hyu));
}
static void hyudoro_set_held_item_from_player
static mobj_t *
spawn_capsule (mobj_t *hyu)
{
mobj_t *caps = P_SpawnMobjFromMobj(
hyu, 0, 0, 0, MT_ITEMCAPSULE);
/* hyudoro only needs its own shadow */
caps->shadowscale = 0;
caps->flags |=
MF_NOGRAVITY |
MF_NOCLIP |
MF_NOCLIPTHING |
MF_NOCLIPHEIGHT;
/* signal that this item capsule always puts items in the HUD */
caps->flags2 |= MF2_STRONGBOX;
P_SetTarget(&hyudoro_capsule(hyu), caps);
/* capsule teleports to hyudoro */
P_SetTarget(&caps->target, hyu);
/* so it looks like hyudoro is holding it */
caps->sprzoff = 20 * hyu->scale;
return caps;
}
static void
update_capsule_position (mobj_t *hyu)
{
mobj_t *caps = hyudoro_capsule(hyu);
if (P_MobjWasRemoved(caps))
return;
caps->extravalue1 = hyu->scale / 3;
/* hold it in the hyudoro's hands */
const fixed_t r = hyu->radius;
caps->sprxoff = FixedMul(r, FCOS(hyu->angle));
caps->spryoff = FixedMul(r, FSIN(hyu->angle));
}
static void
set_item
( mobj_t * hyu,
player_t *player)
INT32 item,
INT32 amount)
{
mobj_t *caps = P_MobjWasRemoved(hyudoro_capsule(hyu))
? spawn_capsule(hyu) : hyudoro_capsule(hyu);
hyudoro_itemtype(hyu) = item;
hyudoro_itemcount(hyu) = amount;
caps->threshold = hyudoro_itemtype(hyu);
caps->movecount = hyudoro_itemcount(hyu);
}
static void
hyudoro_set_held_item_from_player
( mobj_t * hyu,
player_t * player)
{
if (K_ItemEnabled(KITEM_KITCHENSINK))
{
@ -459,15 +528,13 @@ static void hyudoro_set_held_item_from_player
itemCooldowns[player->itemtype - 1] = 0;
}
hyudoro_itemtype(hyu) = KITEM_KITCHENSINK;
hyudoro_itemcount(hyu) = 1;
set_item(hyu, KITEM_KITCHENSINK, 1);
return;
}
}
hyudoro_itemtype(hyu) = player->itemtype;
hyudoro_itemcount(hyu) = player->itemamount;
set_item(hyu, player->itemtype, player->itemamount);
}
static boolean
@ -503,6 +570,11 @@ hyudoro_patrol_hit_player
hyudoro_set_held_item_from_player(hyu, player);
if (!P_MobjWasRemoved(hyudoro_capsule(hyu)))
{
hyudoro_capsule(hyu)->extravalue2 = player->skincolor;
}
K_StripItems(player);
player->hyudorotimer = hyudorotime;
@ -544,16 +616,13 @@ award_immediately (mobj_t *hyu)
return false;
}
if (player->itemamount &&
player->itemtype != hyudoro_itemtype(hyu))
{
if (!P_CanPickupItem(player, 1))
return false;
}
// Same as picking up paper items; get stacks
// immediately
if (!P_CanPickupItem(player, 3))
return false;
// Prevent receiving any more items or even stacked
// Hyudoros! Put on a timer so roulette cannot become
// locked permanently.
player->itemRoulette.reserved = 2*TICRATE;
}
deliver_item(hyu);
@ -770,8 +839,18 @@ Obj_HyudoroThink (mobj_t *hyu)
}
blend_hover_hyudoro(hyu);
break;
case HYU_ORBIT:
if (!project_hyudoro_orbit(hyu))
{
P_RemoveMobj(hyu);
return;
}
break;
}
update_capsule_position(hyu);
if (hyudoro_timer(hyu) > 0)
hyudoro_timer(hyu)--;
}

View file

@ -1687,7 +1687,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
INT16 spacing = (target->radius >> 1) / target->scale;
// set respawn fuse
if (K_CapsuleTimeAttackRules() == true) // no respawns
if (damagetype == DMG_INSTAKILL || K_CapsuleTimeAttackRules() == true) // no respawns
;
else if (target->threshold == KITEM_SUPERRING)
target->fuse = 20*TICRATE;
@ -1752,18 +1752,22 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
{
player_t *player = source->player;
// special behavior for ring capsules
if (target->threshold == KITEM_SUPERRING)
// MF2_STRONGBOX: always put the item right in the hotbar!
if (!(target->flags2 & MF2_STRONGBOX))
{
K_AwardPlayerRings(player, 5 * target->movecount, true);
break;
}
// special behavior for ring capsules
if (target->threshold == KITEM_SUPERRING)
{
K_AwardPlayerRings(player, 5 * target->movecount, true);
break;
}
// special behavior for SPB capsules
if (target->threshold == KITEM_SPB)
{
K_ThrowKartItem(player, true, MT_SPB, 1, 0, 0);
break;
// special behavior for SPB capsules
if (target->threshold == KITEM_SPB)
{
K_ThrowKartItem(player, true, MT_SPB, 1, 0, 0);
break;
}
}
if (target->threshold < 1 || target->threshold >= NUMKARTITEMS) // bruh moment prevention

View file

@ -4443,7 +4443,9 @@ static void P_RefreshItemCapsuleParts(mobj_t *mobj)
}
// update cap colors
if (itemType == KITEM_SUPERRING)
if (mobj->extravalue2)
color = mobj->extravalue2;
else if (itemType == KITEM_SUPERRING)
{
color = SKINCOLOR_GOLD;
newRenderFlags |= RF_SEMIBRIGHT;
@ -4487,7 +4489,10 @@ static void P_RefreshItemCapsuleParts(mobj_t *mobj)
count = mobj->movecount;
break;
case KITEM_SUPERRING: // always display the number, and multiply it by 5
count = mobj->movecount * 5;
if (mobj->flags2 & MF2_STRONGBOX)
count = mobj->movecount * 20; // give Super Rings
else
count = mobj->movecount * 5;
break;
case KITEM_SAD: // never display the number
case KITEM_SPB: