diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ea8ebcb85..26871cf1e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -741,6 +741,7 @@ set( NOT_COMPILED_SOURCE_FILES g_hexen/a_fighterquietus.cpp g_hexen/a_firedemon.cpp g_hexen/a_flechette.cpp + g_hexen/a_flies.cpp g_hexen/a_fog.cpp g_hexen/a_healingradius.cpp g_hexen/a_heresiarch.cpp diff --git a/src/actor.h b/src/actor.h index 78c82066c..862858b32 100644 --- a/src/actor.h +++ b/src/actor.h @@ -1035,7 +1035,7 @@ public: virtual bool UpdateWaterLevel (fixed_t oldz, bool splash=true); bool isFast(); bool isSlow(); - void SetIdle(); + void SetIdle(bool nofunction=false); void ClearCounters(); FState *GetRaiseState(); void Revive(); diff --git a/src/g_hexen/a_flies.cpp b/src/g_hexen/a_flies.cpp new file mode 100644 index 000000000..3c677d5cb --- /dev/null +++ b/src/g_hexen/a_flies.cpp @@ -0,0 +1,106 @@ +static FRandom pr_fly("GetOffMeFly"); + +//=========================================================================== +// +// FindCorpse +// +// Finds a corpse to buzz around. We can't use a blockmap check because +// corpses generally aren't linked into the blockmap. +// +//=========================================================================== + +static AActor *FindCorpse(AActor *fly, sector_t *sec, int recurselimit) +{ + AActor *fallback = NULL; + sec->validcount = validcount; + + // Search the current sector + for (AActor *check = sec->thinglist; check != NULL; check = check->snext) + { + if (check == fly) + continue; + if (!(check->flags & MF_CORPSE)) + continue; + if (!P_CheckSight(fly, check)) + continue; + fallback = check; + if (pr_fly(2)) // 50% chance to try to pick a different corpse + continue; + return check; + } + if (--recurselimit <= 0 || (fallback != NULL && pr_fly(2))) + { + return fallback; + } + // Try neighboring sectors + for (int i = 0; i < sec->linecount; ++i) + { + line_t *line = sec->lines[i]; + sector_t *sec2 = (line->frontsector == sec) ? line->backsector : line->frontsector; + if (sec2 != NULL && sec2->validcount != validcount) + { + AActor *neighbor = FindCorpse(fly, sec2, recurselimit); + if (neighbor != NULL) + { + return neighbor; + } + } + } + return fallback; +} + +DEFINE_ACTION_FUNCTION(AActor, A_FlySearch) +{ + // The version from the retail beta is not so great for general use: + // 1. Pick one of the first fifty thinkers at random. + // 2. Starting from that thinker, find one that is an actor, not itself, + // and within sight. Give up after 100 sequential thinkers. + // It's effectively useless if there are more than 150 thinkers on a map. + // + // So search the sectors instead. We can't potentially find something all + // the way on the other side of the map and we can't find invisible corpses, + // but at least we aren't crippled on maps with lots of stuff going on. + validcount++; + AActor *other = FindCorpse(self, self->Sector, 5); + if (other != NULL) + { + self->target = other; + self->SetState(self->FindState("Buzz")); + } +} + +DEFINE_ACTION_FUNCTION(AActor, A_FlyBuzz) +{ + AActor *targ = self->target; + + if (targ == NULL || !(targ->flags & MF_CORPSE) || pr_fly() < 5) + { + self->SetIdle(); + return; + } + + angle_t ang = R_PointToAngle2(self->x, self->y, targ->x, targ->y); + self->angle = ang; + self->args[0]++; + ang >>= ANGLETOFINESHIFT; + if (!P_TryMove(self, self->x + 6 * finecosine[ang], self->y + 6 * finesine[ang], true)) + { + self->SetIdle(true); + return; + } + if (self->args[0] & 2) + { + self->velx += (pr_fly() - 128) << BOBTOFINESHIFT; + self->vely += (pr_fly() - 128) << BOBTOFINESHIFT; + } + int zrand = pr_fly(); + if (targ->z + 5*FRACUNIT < self->z && zrand > 150) + { + zrand = -zrand; + } + self->velz = zrand << BOBTOFINESHIFT; + if (pr_fly() < 40) + { + S_Sound(self, CHAN_VOICE, self->ActiveSound, 0.5f, ATTN_STATIC); + } +} diff --git a/src/g_hexen/a_hexenmisc.cpp b/src/g_hexen/a_hexenmisc.cpp index fa07a6759..1141f381f 100644 --- a/src/g_hexen/a_hexenmisc.cpp +++ b/src/g_hexen/a_hexenmisc.cpp @@ -38,6 +38,7 @@ #include "a_fighterquietus.cpp" #include "a_firedemon.cpp" #include "a_flechette.cpp" +#include "a_flies.cpp" #include "a_fog.cpp" #include "a_healingradius.cpp" #include "a_heresiarch.cpp" diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index c12bd53db..9dd9f1ddc 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -6236,11 +6236,11 @@ void AActor::Crash() } } -void AActor::SetIdle() +void AActor::SetIdle(bool nofunction) { FState *idle = FindState (NAME_Idle); if (idle == NULL) idle = SpawnState; - SetState(idle); + SetState(idle, nofunction); } int AActor::SpawnHealth() diff --git a/wadsrc/static/actors/hexen/flies.txt b/wadsrc/static/actors/hexen/flies.txt new file mode 100644 index 000000000..6be4a749f --- /dev/null +++ b/wadsrc/static/actors/hexen/flies.txt @@ -0,0 +1,28 @@ + +// Buzzy fly ---------------------------------------------------------------- + +ACTOR LittleFly 112 +{ + Game Hexen + +NOBLOCKMAP +NOGRAVITY + +CANPASS + + Speed 6 + Radius 5 + Height 5 + Mass 2 + ActiveSound "FlyBuzz" + + action native A_FlySearch(); + action native A_FlyBuzz(); + + States + { + Spawn: + TNT1 A 20 A_FlySearch // [RH] Invisible when not flying + Loop + Buzz: + AFLY ABCD 3 A_FlyBuzz + Loop + } +} diff --git a/wadsrc/static/decorate.txt b/wadsrc/static/decorate.txt index ae4b6f851..46f1854b9 100644 --- a/wadsrc/static/decorate.txt +++ b/wadsrc/static/decorate.txt @@ -93,6 +93,7 @@ #include "actors/hexen/mageplayer.txt" #include "actors/hexen/pig.txt" #include "actors/hexen/flame.txt" +#include "actors/hexen/flies.txt" #include "actors/hexen/hexenarmor.txt" #include "actors/hexen/hexendecorations.txt" #include "actors/hexen/hexenkeys.txt" diff --git a/zdoom.vcproj b/zdoom.vcproj index a6a94cafe..9c3367ff2 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -5228,6 +5228,42 @@ /> + + + + + + + + + + + + + +