diff --git a/src/dehacked.c b/src/dehacked.c index 9e221d995..bae218085 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -1937,6 +1937,15 @@ static actionpointer_t actionpointers[] = {{A_NapalmScatter}, "A_NAPALMSCATTER"}, {{A_SpawnFreshCopy}, "A_SPAWNFRESHCOPY"}, {{A_FlickySpawn}, "A_FLICKYSPAWN"}, + {{A_FlickyAim}, "A_FLICKYAIM"}, + {{A_FlickyFly}, "A_FLICKYFLY"}, + {{A_FlickySoar}, "A_FLICKYSOAR"}, + {{A_FlickyCoast}, "A_FLICKYCOAST"}, + {{A_FlickyHop}, "A_FLICKYHOP"}, + {{A_FlickyFlounder}, "A_FLICKYFLOUNDER"}, + {{A_FlickyCheck}, "A_FLICKYCHECK"}, + {{A_FlickyHeightCheck}, "A_FLICKYHEIGHTCHECK"}, + {{A_FlickyFlutter}, "A_FLICKYFLUTTER"}, {{NULL}, "NONE"}, diff --git a/src/info.c b/src/info.c index e7afde710..777d5a358 100644 --- a/src/info.c +++ b/src/info.c @@ -3602,7 +3602,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_s3k7b, // painsound S_NULL, // meleestate S_NULL, // missilestate - S_XPLD_FLICKY, // deathstate + S_XPLD1, // deathstate S_NULL, // xdeathstate sfx_wbreak, // deathsound 3, // speed diff --git a/src/info.h b/src/info.h index 1b337d417..550869ef6 100644 --- a/src/info.h +++ b/src/info.h @@ -215,6 +215,15 @@ void A_BrakLobShot(); void A_NapalmScatter(); void A_SpawnFreshCopy(); void A_FlickySpawn(); +void A_FlickyAim(); +void A_FlickyFly(); +void A_FlickySoar(); +void A_FlickyCoast(); +void A_FlickyHop(); +void A_FlickyFlounder(); +void A_FlickyCheck(); +void A_FlickyHeightCheck(); +void A_FlickyFlutter(); // ratio of states to sprites to mobj types is roughly 6 : 1 : 1 #define NUMMOBJFREESLOTS 256 diff --git a/src/p_enemy.c b/src/p_enemy.c index d3592b12b..ea6eab676 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -243,6 +243,15 @@ void A_BrakLobShot(mobj_t *actor); void A_NapalmScatter(mobj_t *actor); void A_SpawnFreshCopy(mobj_t *actor); void A_FlickySpawn(mobj_t *actor); +void A_FlickyAim(mobj_t *actor); +void A_FlickyFly(mobj_t *actor); +void A_FlickySoar(mobj_t *actor); +void A_FlickyCoast(mobj_t *actor); +void A_FlickyHop(mobj_t *actor); +void A_FlickyFlounder(mobj_t *actor); +void A_FlickyCheck(mobj_t *actor); +void A_FlickyHeightCheck(mobj_t *actor); +void A_FlickyFlutter(mobj_t *actor); // // ENEMY THINKING @@ -10339,7 +10348,6 @@ void A_SpawnFreshCopy(mobj_t *actor) } // Internal Flicky spawning function. - mobj_t *P_InternalFlickySpawn(mobj_t *actor, mobjtype_t flickytype, fixed_t momz, boolean lookforplayers) { mobj_t *flicky; @@ -10371,9 +10379,13 @@ mobj_t *P_InternalFlickySpawn(mobj_t *actor, mobjtype_t flickytype, fixed_t momz flicky = P_SpawnMobjFromMobj(actor, 0, 0, 0, flickytype); flicky->angle = actor->angle; + if (actor->eflags & MFE_UNDERWATER) + momz = FixedDiv(momz, FixedSqrt(3*FRACUNIT)); + P_SetObjectMomZ(flicky, momz, false); - flicky->movedir = P_RandomChance(FRACUNIT/2) ? -1 : 1; + flicky->movedir = (P_RandomChance(FRACUNIT/2) ? -1 : 1); flicky->fuse = P_RandomRange(595, 700); // originally 300, 350 + flicky->threshold = 0; if (lookforplayers) P_LookForPlayers(flicky, true, false, 0); @@ -10406,3 +10418,339 @@ void A_FlickySpawn(mobj_t *actor) P_InternalFlickySpawn(actor, locvar1, ((locvar2) ? locvar2 : 8*FRACUNIT), true); } + +#define FLICKYHITWALL if (actor->momx == actor->momy && actor->momy == 0) actor->threshold = 1; + +// Internal Flicky bubbling function. +void P_InternalFlickyBubble(mobj_t *actor) +{ + if (actor->eflags & MFE_UNDERWATER) + { + mobj_t *overlay; + + if (!((actor->z + 3*actor->height/2) < actor->watertop) || !mobjinfo[actor->type].raisestate || actor->tracer) + return; + + overlay = P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY); + P_SetMobjStateNF(overlay, mobjinfo[actor->type].raisestate); + P_SetTarget(&actor->tracer, overlay); + P_SetTarget(&overlay->target, actor); + return; + } + + if (!actor->tracer || P_MobjWasRemoved(actor->tracer)) + return; + + P_RemoveMobj(actor->tracer); + P_SetTarget(&actor->tracer, NULL); +} + +// Function: A_FlickyAim +// +// Description: Flicky aiming function. +// +// var1 = how far around the target (in angle constants) the flicky should look +// var2 = distance from target to aim for +// +void A_FlickyAim(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_FlickyAim", actor)) + return; +#endif + P_InternalFlickyBubble(actor); + P_InstaThrust(actor, 0, 0); + + if (!actor->target) + { + P_LookForPlayers(actor, true, false, 0); + actor->angle = P_RandomKey(36)*ANG10; + return; + } + + if (actor->fuse > 2*TICRATE) + { + angle_t posvar; + fixed_t chasevar, chasex, chasey; + + if (actor->threshold) + { + actor->movedir *= -1; + actor->threshold = 0; + } + + posvar = ((R_PointToAngle2(actor->target->x, actor->target->y, actor->x, actor->y) + actor->movedir*locvar1) >> ANGLETOFINESHIFT) & FINEMASK; + chasevar = FixedSqrt(max(FRACUNIT, P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y) - locvar2)) + locvar2; + + chasex = actor->target->x + FixedMul(FINECOSINE(posvar), chasevar); + chasey = actor->target->y + FixedMul(FINESINE(posvar), chasevar); + + if (P_AproxDistance(chasex - actor->x, chasey - actor->y)) + actor->angle = R_PointToAngle2(actor->x, actor->y, chasex, chasey); + } + else if (actor->threshold) + { + actor->angle += ANGLE_180; + actor->threshold = 0; + } +} + +//Internal Flicky flying function. Also usuable as an underwater swim thrust. +void P_InternalFlickyFly(mobj_t *actor, fixed_t flyspeed, fixed_t targetdist, fixed_t chasez) +{ + angle_t vertangle; + + flyspeed = FixedMul(flyspeed, actor->scale); + actor->flags |= MF_NOGRAVITY; + + FLICKYHITWALL + + var1 = ANG30; + var2 = 32*FRACUNIT; + A_FlickyAim(actor); + + chasez *= 8; + if (!actor->target || !(actor->fuse > 2*TICRATE)) + chasez += ((actor->eflags & MFE_VERTICALFLIP) ? actor->ceilingz - 24*FRACUNIT : actor->floorz + 24*FRACUNIT); + else + { + fixed_t add = actor->target->z + (actor->target->height - actor->height)/2; + if (add > (actor->ceilingz - 24*actor->scale - actor->height)) + add = actor->ceilingz - 24*actor->scale - actor->height; + else if (add < (actor->floorz + 24*actor->scale)) + add = actor->floorz + 24*actor->scale; + chasez += add; + } + + if (!targetdist) + targetdist = 16*FRACUNIT; //Default! + + if (abs(chasez - actor->z) > targetdist) + targetdist = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y); + + vertangle = (R_PointToAngle2(0, actor->z, targetdist, chasez) >> ANGLETOFINESHIFT) & FINEMASK; + P_InstaThrust(actor, actor->angle, FixedMul(FINECOSINE(vertangle), flyspeed)); + actor->momz = FixedMul(FINESINE(vertangle), flyspeed); +} + +// Function: A_FlickyFly +// +// Description: Flicky flying function. +// +// var1 = how fast to fly +// var2 = how far ahead the target should be considered +// +void A_FlickyFly(mobj_t *actor) +{ + // We're not setting up locvars here - it passes var1 and var2 through to P_InternalFlickyFly instead. + //INT32 locvar1 = var1; + //INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_FlickyFly", actor)) + return; +#endif + P_InternalFlickyFly(actor, var1, var2, + FINECOSINE((((actor->fuse % 36) * ANG10) >> ANGLETOFINESHIFT) & FINEMASK) + ); +} + +// Function: A_FlickySoar +// +// Description: Flicky soaring function - specific to puffin. +// +// var1 = how fast to fly +// var2 = how far ahead the target should be considered +// +void A_FlickySoar(mobj_t *actor) +{ + // We're not setting up locvars here - it passes var1 and var2 through to P_InternalFlickyFly instead. + //INT32 locvar1 = var1; + //INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_FlickySoar", actor)) + return; +#endif + P_InternalFlickyFly(actor, var1, var2, + 2*(FRACUNIT/2 - abs(FINECOSINE((((actor->fuse % 144) * 5*ANG1/2) >> ANGLETOFINESHIFT) & FINEMASK))) + ); + + if (P_MobjFlip(actor)*actor->momz > 0 && actor->frame == 1 && actor->sprite == SPR_NULL)//SPR_FLKJ) + actor->frame = 3; +} + +//Function: A_FlickyCoast +// +// Description: Flicky swim-coasting function. +// +// var1 = speed to change state upon reaching +// var2 = state to change to upon slowing down +// the spawnstate of the mobj = state to change to when above water +// +void A_FlickyCoast(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_FlickyCoast", actor)) + return; +#endif + if (actor->eflags & MFE_UNDERWATER) + { + FLICKYHITWALL + + actor->momx = (11*actor->momx)/12; + actor->momy = (11*actor->momy)/12; + actor->momz = (11*actor->momz)/12; + + if (P_AproxDistance(P_AproxDistance(actor->momx, actor->momy), actor->momz) < locvar1) + P_SetMobjState(actor, locvar2); + + return; + } + + actor->flags &= ~MF_NOGRAVITY; + P_SetMobjState(actor, mobjinfo[actor->type].spawnstate); +} + +// Internal Flicky hopping function. +void P_InternalFlickyHop(mobj_t *actor, fixed_t momz, fixed_t momh, angle_t angle) +{ + if (((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz) + || ((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz))) + { + if (momz) + { + if (actor->eflags & MFE_UNDERWATER) + momz = FixedDiv(momz, FixedSqrt(3*FRACUNIT)); + P_SetObjectMomZ(actor, momz, false); + } + P_InstaThrust(actor, angle, FixedMul(momh, actor->scale)); + } +} + +// Function: A_FlickyHop +// +// Description: Flicky hopping function. +// +// var1 = vertical thrust +// var2 = horizontal thrust +// +void A_FlickyHop(mobj_t *actor) +{ + // We're not setting up locvars here - it passes var1 and var2 through to P_InternalFlickyHop instead. + //INT32 locvar1 = var1; + //INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_FlickyHop", actor)) + return; +#endif + P_InternalFlickyHop(actor, var1, var2, actor->angle); +} + +// Function: A_FlickyFlounder +// +// Description: Flicky floundering function. +// +// var1 = intended vertical thrust +// var2 = intended horizontal thrust +// +void A_FlickyFlounder(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + angle_t hopangle; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_FlickyFlounder", actor)) + return; +#endif + locvar1 *= (P_RandomKey(2) + 1); + locvar2 *= (P_RandomKey(2) + 1); + hopangle = (actor->angle + (P_RandomKey(9) - 4)*ANG2); + P_InternalFlickyHop(actor, locvar1, locvar2, hopangle); +} + +// Function: A_FlickyCheck +// +// Description: Flicky airtime check function. +// +// var1 = state to change to upon touching the floor +// var2 = state to change to upon falling +// the meleestate of the mobj = state to change to when underwater +// +void A_FlickyCheck(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_FlickyCheck", actor)) + return; +#endif + + FLICKYHITWALL + + if (locvar2 && P_MobjFlip(actor)*actor->momz < 1) + P_SetMobjState(actor, locvar2); + else if (locvar1 && ((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz) + || ((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz))) + P_SetMobjState(actor, locvar1); + else if (mobjinfo[actor->type].meleestate && (actor->eflags & MFE_UNDERWATER)) + P_SetMobjState(actor, mobjinfo[actor->type].meleestate); + P_InternalFlickyBubble(actor); +} + +// Function: A_FlickyHeightCheck +// +// Description: Flicky height check function. +// +// var1 = state to change to when falling below height relative to target +// var2 = height relative to target to change state at +// +void A_FlickyHeightCheck(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_FlickyHeightCheck", actor)) + return; +#endif + + FLICKYHITWALL + + if (locvar1 && actor->target && P_MobjFlip(actor)*actor->momz < 1 + && ((P_MobjFlip(actor)*((actor->z + actor->height/2) - (actor->target->z + actor->target->height/2)) < locvar2) + || (actor->z - actor->height < actor->floorz) || (actor->z + 2*actor->height > actor->ceilingz))) + P_SetMobjState(actor, locvar1); + P_InternalFlickyBubble(actor); +} + +// Function: A_FlickyFlutter +// +// Description: Flicky fluttering function - specific to chicken. +// +// var1 = state to change to upon touching the floor +// var2 = state to change to upon falling +// the meleestate of the mobj = state to change to when underwater +// +void A_FlickyFlutter(mobj_t *actor) +{ + // We're not setting up locvars here - it passes var1 and var2 through to A_FlickyCheck instead. + //INT32 locvar1 = var1; + //INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_FlickyFlutter", actor)) + return; +#endif + A_FlickyCheck(actor); + + var1 = ANG30; + var2 = 32*FRACUNIT; + A_FlickyAim(actor); + + P_InstaThrust(actor, actor->angle, 2*actor->scale); + if (P_MobjFlip(actor)*actor->momz < -FRACUNIT/2) + actor->momz = -P_MobjFlip(actor)*actor->scale/2; +} + +#undef FLICKYHITWALL \ No newline at end of file diff --git a/src/p_local.h b/src/p_local.h index aeb14d480..9b7c16702 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -296,6 +296,9 @@ void P_NewChaseDir(mobj_t *actor); boolean P_LookForPlayers(mobj_t *actor, boolean allaround, boolean tracer, fixed_t dist); mobj_t *P_InternalFlickySpawn(mobj_t *actor, mobjtype_t flickytype, fixed_t momz, boolean lookforplayers); +void P_InternalFlickyBubble(mobj_t *actor); +void P_InternalFlickyFly(mobj_t *actor, fixed_t flyspeed, fixed_t targetdist, fixed_t chasez); +void P_InternalFlickyHop(mobj_t *actor, fixed_t momz, fixed_t momh, angle_t angle); // // P_MAP diff --git a/src/p_mobj.c b/src/p_mobj.c index 1f163a958..290b81893 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -7213,10 +7213,11 @@ void P_MobjThinker(mobj_t *mobj) y = mobj->y + FixedMul(FINECOSINE(fa),ns); mo2 = P_SpawnMobj(x, y, z, MT_EXPLODE); - P_SetMobjStateNF(mo2, S_XPLD_EGGTRAP); + P_SetMobjStateNF(mo2, S_XPLD_EGGTRAP); // so the flickies don't lose their target if they spawn ns = 4 * FRACUNIT; mo2->momx = FixedMul(FINESINE(fa),ns); mo2->momy = FixedMul(FINECOSINE(fa),ns); + mo2->angle = fa << ANGLETOFINESHIFT; if (P_RandomChance(FRACUNIT/4)) // I filled a spreadsheet trying to get the equivalent chance to the original P_RandomByte hack! S_StartSound(mo2, mobj->info->deathsound);