//------------------------------------------------------------------------- /* 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 "misc.h" #include "tags.h" #include "ai.h" #include "weapon.h" BEGIN_SW_NS int InitSpriteGrenade(DSWActor* actor); int InitSpriteChemBomb(short SpriteNum); int InitFlashBomb(DSWActor* actor); int InitCaltrops(short SpriteNum); int InitPhosphorus(int16_t SpriteNum); bool SpriteOverlapZ(int16_t, int16_t, int); ////////////////////// // // SKULL Wait // ////////////////////// extern DAMAGE_DATA DamageData[]; ANIMATOR DoSkullMove,DoActorDebris; #define SKULL_RATE 10 ANIMATOR DoSkullWait; STATE s_SkullWait[5][1] = { { {SKULL_R0 + 0, SKULL_RATE, DoSkullWait, &s_SkullWait[0][0]}, }, { {SKULL_R1 + 0, SKULL_RATE, DoSkullWait, &s_SkullWait[1][0]}, }, { {SKULL_R2 + 0, SKULL_RATE, DoSkullWait, &s_SkullWait[2][0]}, }, { {SKULL_R3 + 0, SKULL_RATE, DoSkullWait, &s_SkullWait[3][0]}, }, { {SKULL_R4 + 0, SKULL_RATE, DoSkullWait, &s_SkullWait[4][0]}, } }; STATEp sg_SkullWait[] = { &s_SkullWait[0][0], &s_SkullWait[1][0], &s_SkullWait[2][0], &s_SkullWait[3][0], &s_SkullWait[4][0] }; ATTRIBUTE SkullAttrib = { {60, 80, 100, 130}, // Speeds {3, 0, -2, -3}, // Tic Adjusts 3, // MaxWeapons; { DIGI_AHAMBIENT, 0, 0, 0, DIGI_AHSCREAM, DIGI_AHEXPLODE,0,0,0,0 } }; ////////////////////// // // SKULL for Serp God // ////////////////////// ANIMATOR DoSerpRing; STATE s_SkullRing[5][1] = { { {SKULL_R0 + 0, SKULL_RATE, DoSerpRing, &s_SkullRing[0][0]}, }, { {SKULL_R1 + 0, SKULL_RATE, DoSerpRing, &s_SkullRing[1][0]}, }, { {SKULL_R2 + 0, SKULL_RATE, DoSerpRing, &s_SkullRing[2][0]}, }, { {SKULL_R3 + 0, SKULL_RATE, DoSerpRing, &s_SkullRing[3][0]}, }, { {SKULL_R4 + 0, SKULL_RATE, DoSerpRing, &s_SkullRing[4][0]}, } }; STATEp sg_SkullRing[] = { &s_SkullRing[0][0], &s_SkullRing[1][0], &s_SkullRing[2][0], &s_SkullRing[3][0], &s_SkullRing[4][0] }; ////////////////////// // // SKULL Jump // ////////////////////// ANIMATOR DoSkullJump; STATE s_SkullJump[5][1] = { { {SKULL_R0 + 0, SKULL_RATE, DoSkullJump, &s_SkullJump[0][0]}, }, { {SKULL_R1 + 0, SKULL_RATE, DoSkullJump, &s_SkullJump[1][0]}, }, { {SKULL_R2 + 0, SKULL_RATE, DoSkullJump, &s_SkullJump[2][0]}, }, { {SKULL_R3 + 0, SKULL_RATE, DoSkullJump, &s_SkullJump[3][0]}, }, { {SKULL_R4 + 0, SKULL_RATE, DoSkullJump, &s_SkullJump[4][0]}, } }; STATEp sg_SkullJump[] = { &s_SkullJump[0][0], &s_SkullJump[1][0], &s_SkullJump[2][0], &s_SkullJump[3][0], &s_SkullJump[4][0] }; ////////////////////// // // SKULL Explode // ////////////////////// #define SKULL_EXPLODE_RATE 11 ANIMATOR DoSuicide; ANIMATOR DoSkullSpawnShrap; STATE s_SkullExplode[] = { {SKULL_EXPLODE + 0, 1, NullAnimator, &s_SkullExplode[1]}, {SKULL_EXPLODE + 0, SF_QUICK_CALL, DoDamageTest, &s_SkullExplode[2]}, {SKULL_EXPLODE + 0, SKULL_EXPLODE_RATE, NullAnimator, &s_SkullExplode[3]}, {SKULL_EXPLODE + 1, SKULL_EXPLODE_RATE, NullAnimator, &s_SkullExplode[4]}, {SKULL_EXPLODE + 2, SF_QUICK_CALL, DoSkullSpawnShrap, &s_SkullExplode[5]}, {SKULL_EXPLODE + 2, SKULL_EXPLODE_RATE, NullAnimator, &s_SkullExplode[6]}, {SKULL_EXPLODE + 3, SKULL_EXPLODE_RATE, NullAnimator, &s_SkullExplode[7]}, {SKULL_EXPLODE + 4, SKULL_EXPLODE_RATE, NullAnimator, &s_SkullExplode[8]}, {SKULL_EXPLODE + 5, SKULL_EXPLODE_RATE, NullAnimator, &s_SkullExplode[9]}, {SKULL_EXPLODE + 6, SKULL_EXPLODE_RATE, NullAnimator, &s_SkullExplode[10]}, {SKULL_EXPLODE + 7, SKULL_EXPLODE_RATE, NullAnimator, &s_SkullExplode[11]}, {SKULL_EXPLODE + 8, SKULL_EXPLODE_RATE, NullAnimator, &s_SkullExplode[12]}, {SKULL_EXPLODE + 9, SKULL_EXPLODE_RATE, NullAnimator, &s_SkullExplode[13]}, {SKULL_EXPLODE +10, SKULL_EXPLODE_RATE, NullAnimator, &s_SkullExplode[14]}, {SKULL_EXPLODE +11, SKULL_EXPLODE_RATE, NullAnimator, &s_SkullExplode[15]}, {SKULL_EXPLODE +12, SKULL_EXPLODE_RATE, NullAnimator, &s_SkullExplode[16]}, {SKULL_EXPLODE +13, SKULL_EXPLODE_RATE, NullAnimator, &s_SkullExplode[17]}, {SKULL_EXPLODE +13, SKULL_EXPLODE_RATE, DoSuicide, &s_SkullExplode[17]} }; STATEp sg_SkullExplode[] = { s_SkullExplode, }; int SetupSkull(short SpriteNum) { auto actor = &swActors[SpriteNum]; SPRITEp sp = &sprite[SpriteNum]; USERp u; ANIMATOR DoActorDecide; if (TEST(sp->cstat, CSTAT_SPRITE_RESTORE)) { u = User[SpriteNum].Data(); ASSERT(u); } else { u = SpawnUser(SpriteNum,SKULL_R0,s_SkullWait[0]); u->Health = HEALTH_SKULL; } ChangeState(actor, s_SkullWait[0]); u->Attrib = &SkullAttrib; DoActorSetSpeed(actor, NORM_SPEED); u->StateEnd = s_SkullExplode; u->Rot = sg_SkullWait; u->ID = SKULL_R0; EnemyDefaults(SpriteNum, nullptr, nullptr); sp->clipdist = (128+64) >> 2; SET(u->Flags, SPR_XFLIP_TOGGLE); SET(sp->cstat, CSTAT_SPRITE_YCENTER); u->Radius = 400; if (SPRITEp_BOS(sp) > u->loz - Z(16)) { sp->z = u->loz + Z(tileTopOffset(sp->picnum)); u->loz = sp->z; // leave 8 pixels above the ground sp->z += SPRITEp_SIZE_TOS(sp) - Z(3);; } else { u->Counter = RANDOM_P2(2048); u->sz = sp->z; } return 0; } int DoSkullMove(DSWActor* actor) { USER* u = actor->u(); int SpriteNum = u->SpriteNum; SPRITEp sp = &sprite[SpriteNum]; int32_t dax, day, daz; dax = MOVEx(sp->xvel, sp->ang); day = MOVEy(sp->xvel, sp->ang); daz = sp->zvel; u->ret = move_missile(SpriteNum, dax, day, daz, Z(16), Z(16), CLIPMASK_MISSILE, ACTORMOVETICS); DoFindGroundPoint(actor); return 0; } int DoSkullBeginDeath(DSWActor* actor) { USER* u = actor->u(); int SpriteNum = u->SpriteNum; SPRITEp sp = &sprite[SpriteNum]; int16_t i,num_ord=0; //extern short *DamageRadiusSkull; // Decrease for Serp God if (sp->owner >= 0) User[sp->owner]->Counter--; // starts the explosion that does the actual damage switch (sp->hitag) { case 1: if (sp->lotag) num_ord = sp->lotag; else num_ord = 2; if (num_ord > 3) num_ord = 3; for (i=0; iang = NORM_ANGLE(sp->ang+(i*1024)); InitSpriteChemBomb(SpriteNum); } break; case 2: if (sp->lotag) num_ord = sp->lotag; else num_ord = 5; if (num_ord > 10) num_ord = 10; for (i=0; iang = NORM_ANGLE(RandomRange(2048)); InitCaltrops(SpriteNum); } break; case 3: UpdateSinglePlayKills(actor); InitFlashBomb(actor); break; case 4: if (sp->lotag) num_ord = sp->lotag; else num_ord = 5; if (num_ord > 10) num_ord = 10; for (i=0; iang = NORM_ANGLE(sp->ang+(i*(2048/num_ord))); InitSpriteGrenade(actor); } break; default: SpawnMineExp(SpriteNum); for (i=0; i<3; i++) { sp->ang = NORM_ANGLE(RandomRange(2048)); InitPhosphorus(SpriteNum); } break; } RESET(sp->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN); u->RotNum = 0; u->Tics = 0; // ChangeState(actor, u->StateEnd); //u->State = u->StateStart = u->StateEnd; //u->State = u->StateEnd; u->ID = SKULL_R0; u->Radius = DamageData[DMG_SKULL_EXP].radius; //*DamageRadiusSkull; u->OverlapZ = Z(64); change_actor_stat(actor, STAT_DEAD_ACTOR); sp->shade = -40; SpawnLittleExp(SpriteNum); SetSuicide(actor); //u->spal = sp->pal = PALETTE_RED_LIGHTING; return 0; } int DoSkullJump(DSWActor* actor) { USER* u = actor->u(); int SpriteNum = u->SpriteNum; SPRITEp sp = &sprite[SpriteNum]; if (sp->xvel) DoSkullMove(actor); else sp->ang = NORM_ANGLE(sp->ang + (64 * ACTORMOVETICS)); if (TEST(u->Flags,SPR_JUMPING)) { DoJump(actor); } else if (TEST(u->Flags,SPR_FALLING)) { DoFall(actor); // jump/fall type if (sp->xvel) { int dist,a,b,c; DISTANCE(sp->x, sp->y, u->targetActor->s().x, u->targetActor->s().y, dist, a, b, c); if (dist < 1000 && SpriteOverlapZ(SpriteNum, u->targetActor->GetSpriteIndex(), Z(32))) { UpdateSinglePlayKills(actor); DoSkullBeginDeath(actor); return 0; } if ((sp->z > u->loz - Z(36))) { sp->z = u->loz - Z(36); UpdateSinglePlayKills(actor); DoSkullBeginDeath(actor); return 0; } } // non jumping type else { if (u->jump_speed > 200) { UpdateSinglePlayKills(actor); DoSkullBeginDeath(actor); } } } else { UpdateSinglePlayKills(actor); DoSkullBeginDeath(actor); } return 0; } int DoSkullBob(DSWActor* actor) { USER* u = actor->u(); int SpriteNum = u->SpriteNum; SPRITEp sp = &sprite[SpriteNum]; // actor does a sine wave about u->sz - this is the z mid point #define SKULL_BOB_AMT (Z(16)) u->Counter = (u->Counter + (ACTORMOVETICS << 3) + (ACTORMOVETICS << 1)) & 2047; sp->z = u->sz + MulScale(SKULL_BOB_AMT, bsin(u->Counter), 14) + MulScale(DIV2(SKULL_BOB_AMT), bsin(u->Counter), 14); return 0; } int DoSkullSpawnShrap(DSWActor* actor) { SpawnShrap(actor, nullptr); //PlaySpriteSound(SpriteNum,attr_extra1,v3df_none); return 0; } int DoSkullWait(DSWActor* actor) { USER* u = actor->u(); int SpriteNum = u->SpriteNum; SPRITEp sp = &sprite[SpriteNum]; int a,b,c,dist; DISTANCE(sp->x, sp->y, u->targetActor->s().x, u->targetActor->s().y, dist, a, b, c); DoActorPickClosePlayer(actor); //if (dist < u->active_range) // return(0); if ((u->WaitTics -= ACTORMOVETICS) <= 0) { PlaySound(DIGI_AHSCREAM, sp, v3df_none); u->WaitTics = SEC(3) + RandomRange(360); } // below the floor type if (sp->z > u->loz) { // look for closest player every once in a while if (dist < 3500) { sp->xvel = 0; u->jump_speed = -600; NewStateGroup(actor, sg_SkullJump); DoBeginJump(actor); } } else // above the floor type { sp->ang = NORM_ANGLE(sp->ang + (48 * ACTORMOVETICS)); DoSkullBob(actor); if (dist < 8000) { sp->ang = getangle(u->targetActor->s().x - sp->x, u->targetActor->s().y - sp->y); sp->xvel = 128 + (RANDOM_P2(256<<8)>>8); u->jump_speed = -700; NewStateGroup(actor, sg_SkullJump); DoBeginJump(actor); } } return 0; } ////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////// // // BETTY Wait // ////////////////////// ANIMATOR DoBettyMove,DoActorDebris; #define BETTY_RATE 10 ANIMATOR DoBettyWait; STATE s_BettyWait[5][3] = { { {BETTY_R0 + 0, BETTY_RATE, DoBettyWait, &s_BettyWait[0][1]}, {BETTY_R0 + 1, BETTY_RATE, DoBettyWait, &s_BettyWait[0][2]}, {BETTY_R0 + 2, BETTY_RATE, DoBettyWait, &s_BettyWait[0][0]}, }, { {BETTY_R1 + 0, BETTY_RATE, DoBettyWait, &s_BettyWait[1][1]}, {BETTY_R1 + 1, BETTY_RATE, DoBettyWait, &s_BettyWait[1][2]}, {BETTY_R1 + 2, BETTY_RATE, DoBettyWait, &s_BettyWait[1][0]}, }, { {BETTY_R2 + 0, BETTY_RATE, DoBettyWait, &s_BettyWait[2][1]}, {BETTY_R2 + 1, BETTY_RATE, DoBettyWait, &s_BettyWait[2][2]}, {BETTY_R2 + 2, BETTY_RATE, DoBettyWait, &s_BettyWait[2][0]}, }, { {BETTY_R3 + 0, BETTY_RATE, DoBettyWait, &s_BettyWait[3][1]}, {BETTY_R3 + 1, BETTY_RATE, DoBettyWait, &s_BettyWait[3][2]}, {BETTY_R3 + 2, BETTY_RATE, DoBettyWait, &s_BettyWait[3][0]}, }, { {BETTY_R4 + 0, BETTY_RATE, DoBettyWait, &s_BettyWait[4][1]}, {BETTY_R4 + 1, BETTY_RATE, DoBettyWait, &s_BettyWait[4][2]}, {BETTY_R4 + 2, BETTY_RATE, DoBettyWait, &s_BettyWait[4][0]}, } }; STATEp sg_BettyWait[] = { &s_BettyWait[0][0], &s_BettyWait[1][0], &s_BettyWait[2][0], &s_BettyWait[3][0], &s_BettyWait[4][0] }; ATTRIBUTE BettyAttrib = { {60, 80, 100, 130}, // Speeds {3, 0, -2, -3}, // Tic Adjusts 3, // MaxWeapons; {0, 0, 0, 0, 0, 0,0,0,0,0} }; ////////////////////// // // BETTY Jump // ////////////////////// ANIMATOR DoBettyJump; STATE s_BettyJump[5][1] = { { {BETTY_R0 + 0, BETTY_RATE, DoBettyJump, &s_BettyJump[0][0]}, }, { {BETTY_R1 + 0, BETTY_RATE, DoBettyJump, &s_BettyJump[1][0]}, }, { {BETTY_R2 + 0, BETTY_RATE, DoBettyJump, &s_BettyJump[2][0]}, }, { {BETTY_R3 + 0, BETTY_RATE, DoBettyJump, &s_BettyJump[3][0]}, }, { {BETTY_R4 + 0, BETTY_RATE, DoBettyJump, &s_BettyJump[4][0]}, } }; STATEp sg_BettyJump[] = { &s_BettyJump[0][0], &s_BettyJump[1][0], &s_BettyJump[2][0], &s_BettyJump[3][0], &s_BettyJump[4][0] }; ////////////////////// // // BETTY Explode // ////////////////////// #define BETTY_EXPLODE_RATE 11 #define BETTY_EXPLODE BETTY_R0 ANIMATOR DoSuicide; ANIMATOR DoBettySpawnShrap; STATE s_BettyExplode[] = { {BETTY_EXPLODE + 0, SF_QUICK_CALL, DoDamageTest, &s_BettyExplode[1]}, {BETTY_EXPLODE + 0, BETTY_EXPLODE_RATE, DoSuicide, &s_BettyExplode[0]} }; STATEp sg_BettyExplode[] = { s_BettyExplode, }; int SetupBetty(short SpriteNum) { auto actor = &swActors[SpriteNum]; SPRITEp sp = &sprite[SpriteNum]; USERp u; ANIMATOR DoActorDecide; if (TEST(sp->cstat, CSTAT_SPRITE_RESTORE)) { u = User[SpriteNum].Data(); ASSERT(u); } else { u = SpawnUser(SpriteNum,BETTY_R0,s_BettyWait[0]); u->Health = HEALTH_SKULL; } ChangeState(actor, s_BettyWait[0]); u->Attrib = &BettyAttrib; DoActorSetSpeed(actor, NORM_SPEED); u->StateEnd = s_BettyExplode; u->Rot = sg_BettyWait; u->ID = BETTY_R0; EnemyDefaults(SpriteNum, nullptr, nullptr); sp->clipdist = (128+64) >> 2; SET(u->Flags, SPR_XFLIP_TOGGLE); SET(sp->cstat, CSTAT_SPRITE_YCENTER); u->Radius = 400; if (SPRITEp_BOS(sp) > u->loz - Z(16)) { sp->z = u->loz + Z(tileTopOffset(sp->picnum)); u->loz = sp->z; // leave 8 pixels above the ground sp->z += SPRITEp_SIZE_TOS(sp) - Z(3);; } else { u->Counter = RANDOM_P2(2048); u->sz = sp->z; } return 0; } int DoBettyMove(DSWActor* actor) { USER* u = actor->u(); int SpriteNum = u->SpriteNum; SPRITEp sp = &sprite[SpriteNum]; int32_t dax, day, daz; dax = MOVEx(sp->xvel, sp->ang); day = MOVEy(sp->xvel, sp->ang); daz = sp->zvel; u->ret = move_missile(SpriteNum, dax, day, daz, Z(16), Z(16), CLIPMASK_MISSILE, ACTORMOVETICS); DoFindGroundPoint(actor); return 0; } int DoBettyBeginDeath(DSWActor* actor) { USER* u = actor->u(); int SpriteNum = u->SpriteNum; SPRITEp sp = &sprite[SpriteNum]; int16_t i,num_ord=0; //extern short *DamageRadiusBetty; // starts the explosion that does the actual damage switch (sp->hitag) { case 1: if (sp->lotag) num_ord = sp->lotag; else num_ord = 2; if (num_ord > 3) num_ord = 3; for (i=0; iang = NORM_ANGLE(sp->ang+(i*1024)); InitSpriteChemBomb(SpriteNum); } break; case 2: if (sp->lotag) num_ord = sp->lotag; else num_ord = 5; if (num_ord > 10) num_ord = 10; for (i=0; iang = NORM_ANGLE(RandomRange(2048)); InitCaltrops(SpriteNum); } break; case 3: InitFlashBomb(actor); break; case 4: if (sp->lotag) num_ord = sp->lotag; else num_ord = 5; if (num_ord > 10) num_ord = 10; for (i=0; iang = NORM_ANGLE(sp->ang + (i*(2048/num_ord))); InitSpriteGrenade(actor); } break; default: for (i=0; i<5; i++) { sp->ang = NORM_ANGLE(RandomRange(2048)); InitPhosphorus(SpriteNum); SpawnMineExp(SpriteNum); } break; } RESET(sp->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN); u->RotNum = 0; u->Tics = 0; // ChangeState(actor, u->StateEnd); //u->State = u->StateStart = u->StateEnd; //u->State = u->StateEnd; u->ID = BETTY_R0; u->Radius = DamageData[DMG_SKULL_EXP].radius; //*DamageRadiusBetty; u->OverlapZ = Z(64); change_actor_stat(actor, STAT_DEAD_ACTOR); sp->shade = -40; SpawnLittleExp(SpriteNum); SetSuicide(actor); //u->spal = sp->pal = PALETTE_RED_LIGHTING; return 0; } int DoBettyJump(DSWActor* actor) { USER* u = actor->u(); int SpriteNum = u->SpriteNum; SPRITEp sp = &sprite[SpriteNum]; if (sp->xvel) DoBettyMove(actor); else sp->ang = NORM_ANGLE(sp->ang + (64 * ACTORMOVETICS)); if (TEST(u->Flags,SPR_JUMPING)) { DoJump(actor); } else if (TEST(u->Flags,SPR_FALLING)) { DoFall(actor); // jump/fall type if (sp->xvel) { int dist,a,b,c; DISTANCE(sp->x, sp->y, u->targetActor->s().x, u->targetActor->s().y, dist, a, b, c); if (dist < 1000 && SpriteOverlapZ(SpriteNum, u->targetActor->GetSpriteIndex(), Z(32))) { UpdateSinglePlayKills(actor); DoBettyBeginDeath(actor); return 0; } if ((sp->z > u->loz - Z(36))) { sp->z = u->loz - Z(36); UpdateSinglePlayKills(actor); DoBettyBeginDeath(actor); return 0; } } // non jumping type else { if (u->jump_speed > 200) { UpdateSinglePlayKills(actor); DoBettyBeginDeath(actor); } } } else { UpdateSinglePlayKills(actor); DoBettyBeginDeath(actor); } return 0; } int DoBettyBob(DSWActor* actor) { USER* u = actor->u(); int SpriteNum = u->SpriteNum; SPRITEp sp = &sprite[SpriteNum]; // actor does a sine wave about u->sz - this is the z mid point #define BETTY_BOB_AMT (Z(16)) u->Counter = (u->Counter + (ACTORMOVETICS << 3) + (ACTORMOVETICS << 1)) & 2047; sp->z = u->sz + MulScale(BETTY_BOB_AMT, bsin(u->Counter), 14) + MulScale(DIV2(BETTY_BOB_AMT), bsin(u->Counter), 14); return 0; } int DoBettySpawnShrap(DSWActor* actor) { SpawnShrap(actor, nullptr); //PlaySpriteSound(SpriteNum,attr_extra1,v3df_none); return 0; } int DoBettyWait(DSWActor* actor) { USER* u = actor->u(); int SpriteNum = u->SpriteNum; SPRITEp sp = &sprite[SpriteNum]; int a,b,c,dist; DISTANCE(sp->x, sp->y, u->targetActor->s().x, u->targetActor->s().y, dist, a, b, c); DoActorPickClosePlayer(actor); //if (dist < u->active_range) // return(0); if ((u->WaitTics -= ACTORMOVETICS) <= 0) { PlaySound(DIGI_MINEBEEP, sp, v3df_none); u->WaitTics = SEC(3); } // below the floor type if (sp->z > u->loz) { // look for closest player every once in a while if (dist < 3500) { sp->xvel = 0; u->jump_speed = -600; NewStateGroup(actor, sg_BettyJump); DoBeginJump(actor); } } else // above the floor type { sp->ang = NORM_ANGLE(sp->ang + (48 * ACTORMOVETICS)); DoBettyBob(actor); if (dist < 8000) { sp->ang = getangle(u->targetActor->s().x - sp->x, u->targetActor->s().y - sp->y); sp->xvel = 128 + (RANDOM_P2(256<<8)>>8); u->jump_speed = -700; NewStateGroup(actor, sg_BettyJump); DoBeginJump(actor); } } return 0; } #include "saveable.h" static saveable_code saveable_skull_code[] = { SAVE_CODE(DoSkullMove), SAVE_CODE(DoSkullBeginDeath), SAVE_CODE(DoSkullJump), SAVE_CODE(DoSkullBob), SAVE_CODE(DoSkullSpawnShrap), SAVE_CODE(DoSkullWait), SAVE_CODE(DoBettyMove), SAVE_CODE(DoBettyBeginDeath), SAVE_CODE(DoBettyJump), SAVE_CODE(DoBettyBob), SAVE_CODE(DoBettySpawnShrap), SAVE_CODE(DoBettyWait), }; static saveable_data saveable_skull_data[] = { SAVE_DATA(s_SkullWait), SAVE_DATA(sg_SkullWait), SAVE_DATA(SkullAttrib), SAVE_DATA(s_SkullRing), SAVE_DATA(sg_SkullRing), SAVE_DATA(s_SkullJump), SAVE_DATA(sg_SkullJump), SAVE_DATA(s_SkullExplode), SAVE_DATA(sg_SkullExplode), SAVE_DATA(s_BettyWait), SAVE_DATA(sg_BettyWait), SAVE_DATA(BettyAttrib), SAVE_DATA(s_BettyJump), SAVE_DATA(sg_BettyJump), SAVE_DATA(s_BettyExplode), SAVE_DATA(sg_BettyExplode), }; saveable_module saveable_skull = { // code saveable_skull_code, SIZ(saveable_skull_code), // data saveable_skull_data, SIZ(saveable_skull_data) }; END_SW_NS