//------------------------------------------------------------------------- /* Copyright (C) 1997, 2005 - 3D Realms Entertainment This file is part of Shadow Warrior version 1.2 Shadow Warrior is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Original Source: 1997 - Frank Maddin and Jim Norwood Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms */ //------------------------------------------------------------------------- #include "ns.h" #include "build.h" #include "names2.h" #include "game.h" #include "tags.h" #include "ai.h" #include "weapon.h" #include "misc.h" #include "sprite.h" BEGIN_SW_NS ANIMATOR DoHornetCircle, InitHornetCircle; DECISION HornetBattle[] = { {50, InitHornetCircle }, {798, InitActorMoveCloser }, {800, InitActorAlertNoise }, {1024, InitActorRunAway } }; DECISION HornetOffense[] = { {1022, InitActorMoveCloser }, {1024, InitActorAlertNoise } }; DECISION HornetBroadcast[] = { {3, InitActorAlertNoise }, {6, InitActorAmbientNoise }, {1024, InitActorDecide } }; DECISION HornetSurprised[] = { {100, InitHornetCircle }, {701, InitActorMoveCloser }, {1024, InitActorDecide } }; DECISION HornetEvasive[] = { {20, InitHornetCircle }, {1024, nullptr }, }; DECISION HornetLostTarget[] = { {900, InitActorFindPlayer }, {1024, InitActorWanderAround } }; DECISION HornetCloseRange[] = { {900, InitActorMoveCloser }, {1024, InitActorReposition } }; ANIMATOR InitHornetSting; DECISION HornetTouchTarget[] = { {500, InitHornetCircle }, {1024, InitHornetSting } }; PERSONALITY HornetPersonality = { HornetBattle, HornetOffense, HornetBroadcast, HornetSurprised, HornetEvasive, HornetLostTarget, HornetCloseRange, HornetTouchTarget }; ATTRIBUTE HornetAttrib = { {300, 350, 375, 400}, // Speeds {0, 0, 0, 0}, // Tic Adjusts 0, //MaxWeapons; { 0, 0, DIGI_HORNETSTING, DIGI_HORNETSTING, DIGI_HORNETDEATH, 0,0,0,0,0 } }; ////////////////////// // // HORNET RUN ////////////////////// #define HORNET_RUN_RATE 7 ANIMATOR DoHornetMove,NullHornet,DoStayOnFloor, DoActorDebris, NullHornet, DoHornetBirth; STATE s_HornetRun[5][2] = { { {HORNET_RUN_R0 + 0, HORNET_RUN_RATE, DoHornetMove, &s_HornetRun[0][1]}, {HORNET_RUN_R0 + 1, HORNET_RUN_RATE, DoHornetMove, &s_HornetRun[0][0]}, }, { {HORNET_RUN_R1 + 0, HORNET_RUN_RATE, DoHornetMove, &s_HornetRun[1][1]}, {HORNET_RUN_R1 + 1, HORNET_RUN_RATE, DoHornetMove, &s_HornetRun[1][0]}, }, { {HORNET_RUN_R2 + 0, HORNET_RUN_RATE, DoHornetMove, &s_HornetRun[2][1]}, {HORNET_RUN_R2 + 1, HORNET_RUN_RATE, DoHornetMove, &s_HornetRun[2][0]}, }, { {HORNET_RUN_R3 + 0, HORNET_RUN_RATE, DoHornetMove, &s_HornetRun[3][1]}, {HORNET_RUN_R3 + 1, HORNET_RUN_RATE, DoHornetMove, &s_HornetRun[3][0]}, }, { {HORNET_RUN_R4 + 0, HORNET_RUN_RATE, DoHornetMove, &s_HornetRun[4][1]}, {HORNET_RUN_R4 + 1, HORNET_RUN_RATE, DoHornetMove, &s_HornetRun[4][0]}, } }; STATEp sg_HornetRun[] = { &s_HornetRun[0][0], &s_HornetRun[1][0], &s_HornetRun[2][0], &s_HornetRun[3][0], &s_HornetRun[4][0] }; ////////////////////// // // HORNET STAND // ////////////////////// #define HORNET_STAND_RATE (HORNET_RUN_RATE + 5) STATE s_HornetStand[5][2] = { { {HORNET_RUN_R0 + 0, HORNET_STAND_RATE, DoHornetMove, &s_HornetStand[0][1]}, {HORNET_RUN_R0 + 1, HORNET_STAND_RATE, DoHornetMove, &s_HornetStand[0][0]} }, { {HORNET_RUN_R1 + 0, HORNET_STAND_RATE, DoHornetMove, &s_HornetStand[1][1]}, {HORNET_RUN_R1 + 1, HORNET_STAND_RATE, DoHornetMove, &s_HornetStand[1][0]} }, { {HORNET_RUN_R2 + 0, HORNET_STAND_RATE, DoHornetMove, &s_HornetStand[2][1]}, {HORNET_RUN_R2 + 1, HORNET_STAND_RATE, DoHornetMove, &s_HornetStand[2][0]} }, { {HORNET_RUN_R3 + 0, HORNET_STAND_RATE, DoHornetMove, &s_HornetStand[3][1]}, {HORNET_RUN_R3 + 1, HORNET_STAND_RATE, DoHornetMove, &s_HornetStand[3][0]} }, { {HORNET_RUN_R4 + 0, HORNET_STAND_RATE, DoHornetMove, &s_HornetStand[4][1]}, {HORNET_RUN_R4 + 1, HORNET_STAND_RATE, DoHornetMove, &s_HornetStand[4][0]} } }; STATEp sg_HornetStand[] = { &s_HornetStand[0][0], &s_HornetStand[1][0], &s_HornetStand[2][0], &s_HornetStand[3][0], &s_HornetStand[4][0] }; ////////////////////// // // HORNET DIE // ////////////////////// #define HORNET_DIE_RATE 20 ANIMATOR DoHornetDeath; STATE s_HornetDie[] = { #if 0 {HORNET_DIE + 0, HORNET_DIE_RATE, NullHornet, &s_HornetDie[1]}, {HORNET_DEAD, HORNET_DIE_RATE, DoActorDebris, &s_HornetDie[1]}, #else {HORNET_DIE + 0, HORNET_DIE_RATE, DoHornetDeath, &s_HornetDie[0]}, #endif }; STATEp sg_HornetDie[] = { s_HornetDie }; STATE s_HornetDead[] = { {HORNET_DEAD, HORNET_DIE_RATE, DoActorDebris, &s_HornetDead[0]}, }; STATEp sg_HornetDead[] = { s_HornetDead }; /* STATEp *Stand[MAX_WEAPONS]; STATEp *Run; STATEp *Jump; STATEp *Fall; STATEp *Crawl; STATEp *Swim; STATEp *Fly; STATEp *Rise; STATEp *Sit; STATEp *Look; STATEp *Climb; STATEp *Pain; STATEp *Death1; STATEp *Death2; STATEp *Dead; STATEp *DeathJump; STATEp *DeathFall; STATEp *CloseAttack[2]; STATEp *Attack[6]; STATEp *Special[2]; */ ACTOR_ACTION_SET HornetActionSet = { sg_HornetStand, sg_HornetRun, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, //climb nullptr, //pain sg_HornetDie, nullptr, sg_HornetDead, nullptr, nullptr, {nullptr}, {0}, {nullptr}, {0}, {nullptr}, nullptr, nullptr }; int DoHornetMatchPlayerZ(DSWActor* actor); int SetupHornet(DSWActor* actor) { SPRITEp sp = &actor->s(); USERp u; ANIMATOR DoActorDecide; if (TEST(sp->cstat, CSTAT_SPRITE_RESTORE)) { u = actor->u(); ASSERT(u); } else { u = SpawnUser(actor,HORNET_RUN_R0,s_HornetRun[0]); u->Health = HEALTH_HORNET; } ChangeState(actor, s_HornetRun[0]); u->Attrib = &HornetAttrib; DoActorSetSpeed(actor, NORM_SPEED); u->StateEnd = s_HornetDie; u->Rot = sg_HornetRun; EnemyDefaults(actor, &HornetActionSet, &HornetPersonality); SET(u->Flags, SPR_NO_SCAREDZ|SPR_XFLIP_TOGGLE); SET(sp->cstat, CSTAT_SPRITE_YCENTER); sp->clipdist = (100) >> 2; u->floor_dist = Z(16); u->ceiling_dist = Z(16); u->sz = sp->z; sp->xrepeat = 37; sp->yrepeat = 32; // Special looping buzz sound attached to each hornet spawned PlaySound(DIGI_HORNETBUZZ, sp, v3df_follow|v3df_init); return 0; } int NullHornet(DSWActor* actor) { USER* u = actor->u(); if (TEST(u->Flags,SPR_SLIDING)) DoActorSlide(actor); DoHornetMatchPlayerZ(actor); DoActorSectorDamage(actor); return 0; } enum { HORNET_BOB_AMT = (Z(16)) }; int DoHornetMatchPlayerZ(DSWActor* actor) { SPRITEp sp = &actor->s(); USERp u = actor->u(); SPRITEp tsp = &u->targetActor->s(); int zdiff,zdist; int loz,hiz; int bound; // actor does a sine wave about u->sz - this is the z mid point //zdiff = (SPRITEp_LOWER(tsp) - Z(8)) - u->sz; zdiff = (SPRITEp_MID(tsp)) - u->sz; // check z diff of the player and the sprite zdist = Z(20 + RandomRange(200)); // put a random amount if (labs(zdiff) > zdist) { if (zdiff > 0) // manipulate the z midpoint //u->sz += 256 * ACTORMOVETICS; u->sz += 1024 * ACTORMOVETICS; else u->sz -= 256 * ACTORMOVETICS; } // save off lo and hi z loz = u->loz; hiz = u->hiz; // adjust loz/hiz for water depth if (u->lo_sectp && SectUser[u->lo_sectp - sector].Data() && FixedToInt(SectUser[u->lo_sectp - sector]->depth_fixed)) loz -= Z(FixedToInt(SectUser[u->lo_sectp - sector]->depth_fixed)) - Z(8); // lower bound if (u->lowActor) bound = loz - u->floor_dist; else bound = loz - u->floor_dist - HORNET_BOB_AMT; if (u->sz > bound) { u->sz = bound; } // upper bound if (u->highActor) bound = hiz + u->ceiling_dist; else bound = hiz + u->ceiling_dist + HORNET_BOB_AMT; if (u->sz < bound) { u->sz = bound; } u->sz = min(u->sz, loz - u->floor_dist); u->sz = max(u->sz, hiz + u->ceiling_dist); u->Counter = (u->Counter + (ACTORMOVETICS << 3) + (ACTORMOVETICS << 1)) & 2047; sp->z = u->sz + MulScale(HORNET_BOB_AMT, bsin(u->Counter), 14); bound = u->hiz + u->ceiling_dist + HORNET_BOB_AMT; if (sp->z < bound) { // bumped something sp->z = u->sz = bound + HORNET_BOB_AMT; } return 0; } int InitHornetCircle(DSWActor* actor) { USER* u = actor->u(); SPRITEp sp = &actor->s(); u->ActorActionFunc = DoHornetCircle; NewStateGroup(actor, u->ActorActionSet->Run); // set it close DoActorSetSpeed(actor, FAST_SPEED); // set to really fast sp->xvel = 400; // angle adjuster u->Counter2 = sp->xvel/3; // random angle direction if (RANDOM_P2(1024) < 512) u->Counter2 = -u->Counter2; // z velocity u->jump_speed = 200 + RANDOM_P2(128); if (labs(u->sz - u->hiz) < labs(u->sz - u->loz)) u->jump_speed = -u->jump_speed; u->WaitTics = (RandomRange(3)+1) * 60; (*u->ActorActionFunc)(actor); return 0; } int DoHornetCircle(DSWActor* actor) { USER* u = actor->u(); SPRITEp sp = &actor->s(); int nx,ny,bound; sp->ang = NORM_ANGLE(sp->ang + u->Counter2); nx = MulScale(sp->xvel, bcos(sp->ang), 14); ny = MulScale(sp->xvel, bsin(sp->ang), 14); if (!move_actor(actor, nx, ny, 0L)) { //ActorMoveHitReact(actor); // try moving in the opposite direction u->Counter2 = -u->Counter2; sp->ang = NORM_ANGLE(sp->ang + 1024); nx = MulScale(sp->xvel, bcos(sp->ang), 14); ny = MulScale(sp->xvel, bsin(sp->ang), 14); if (!move_actor(actor, nx, ny, 0L)) { InitActorReposition(actor); return 0; } } // move in the z direction u->sz -= u->jump_speed * ACTORMOVETICS; bound = u->hiz + u->ceiling_dist + HORNET_BOB_AMT; if (u->sz < bound) { // bumped something u->sz = bound; InitActorReposition(actor); return 0; } // time out if ((u->WaitTics -= ACTORMOVETICS) < 0) { InitActorReposition(actor); u->WaitTics = 0; return 0; } return 0; } int DoHornetDeath(DSWActor* actor) { USER* u = actor->u(); SPRITEp sp = &actor->s(); int nx, ny; if (TEST(u->Flags, SPR_FALLING)) { u->loz = u->zclip; DoFall(actor); } else { RESET(sp->cstat, CSTAT_SPRITE_YCENTER); u->jump_speed = 0; u->floor_dist = 0; DoBeginFall(actor); DoFindGroundPoint(actor); u->zclip = u->loz; } if (TEST(u->Flags, SPR_SLIDING)) DoActorSlide(actor); // slide while falling nx = MulScale(sp->xvel, bcos(sp->ang), 14); ny = MulScale(sp->xvel, bsin(sp->ang), 14); u->coll = move_sprite(actor, nx, ny, 0L, u->ceiling_dist, u->floor_dist, 1, ACTORMOVETICS); // on the ground if (sp->z >= u->loz) { RESET(u->Flags, SPR_FALLING|SPR_SLIDING); RESET(sp->cstat, CSTAT_SPRITE_YFLIP); // If upside down, reset it NewStateGroup(actor, u->ActorActionSet->Dead); DeleteNoSoundOwner(actor); return 0; } return 0; } // Hornets can swarm around other hornets or whatever is tagged as swarm target int DoCheckSwarm(DSWActor* actor) { USER* u = actor->u(); int i; SPRITEp sp = &actor->s(), tsp; USERp tu; int dist, pdist, a,b,c; PLAYERp pp; if (!MoveSkip8) return 0; // Don't over check if (!u->targetActor) return 0; // Who's the closest meat!? DoActorPickClosePlayer(actor); if (u->targetActor->u()->PlayerP) { pp = u->targetActor->u()->PlayerP; DISTANCE(sp->x, sp->y, pp->posx, pp->posy, pdist, a, b, c); } else return 0; // all enemys StatIterator it(STAT_ENEMY); while ((i = it.NextIndex()) >= 0) { auto itActor = &swActors[i]; tsp = &itActor->s(); tu = itActor->u(); if (!tu) continue; if (tsp->hitag != TAG_SWARMSPOT || tsp->lotag != 2) continue; DISTANCE(sp->x, sp->y, tsp->x, tsp->y, dist, a, b, c); if (dist < pdist && u->ID == tu->ID) // Only flock to your own kind { u->targetActor = itActor; // Set target to swarm center } } return true; } int DoHornetMove(DSWActor* actor) { USER* u = actor->u(); SPRITEp sp = &actor->s(); // Check for swarming // lotag of 1 = Swarm around lotags of 2 // lotag of 0 is normal if (sp->hitag == TAG_SWARMSPOT && sp->lotag == 1) DoCheckSwarm(actor); if (TEST(u->Flags,SPR_SLIDING)) DoActorSlide(actor); if (u->track >= 0) ActorFollowTrack(actor, ACTORMOVETICS); else (*u->ActorActionFunc)(actor); DoHornetMatchPlayerZ(actor); DoActorSectorDamage(actor); return 0; } #include "saveable.h" static saveable_code saveable_hornet_code[] = { SAVE_CODE(NullHornet), SAVE_CODE(DoHornetMatchPlayerZ), SAVE_CODE(InitHornetCircle), SAVE_CODE(DoHornetCircle), SAVE_CODE(DoHornetDeath), SAVE_CODE(DoCheckSwarm), SAVE_CODE(DoHornetMove), }; static saveable_data saveable_hornet_data[] = { SAVE_DATA(HornetBattle), SAVE_DATA(HornetOffense), SAVE_DATA(HornetBroadcast), SAVE_DATA(HornetSurprised), SAVE_DATA(HornetEvasive), SAVE_DATA(HornetLostTarget), SAVE_DATA(HornetCloseRange), SAVE_DATA(HornetTouchTarget), SAVE_DATA(HornetPersonality), SAVE_DATA(HornetAttrib), SAVE_DATA(s_HornetRun), SAVE_DATA(sg_HornetRun), SAVE_DATA(s_HornetStand), SAVE_DATA(sg_HornetStand), SAVE_DATA(s_HornetDie), SAVE_DATA(sg_HornetDie), SAVE_DATA(s_HornetDead), SAVE_DATA(sg_HornetDead), SAVE_DATA(HornetActionSet), }; saveable_module saveable_hornet = { // code saveable_hornet_code, SIZ(saveable_hornet_code), // data saveable_hornet_data, SIZ(saveable_hornet_data) }; END_SW_NS