raze/source/games/sw/src/hornet.cpp
Christoph Oelckers 8f19dc12d8 - SW: code/data pointer saving cleanup.
* a large number of code pointer records were removed because none of these functions ever gets assigned to a pointer
* instead of looking up entries by index, do it by name. This is far less fragile and will survive deeper refactoring. The old storage by table index will break as soon as a single entry gets removed.

Since the old savegames got broken due to this problem recently it was a good time to change the setup.
2021-11-11 23:28:28 +01:00

683 lines
15 KiB
C++

//-------------------------------------------------------------------------
/*
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(short SpriteNum);
int
SetupHornet(short 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,HORNET_RUN_R0,s_HornetRun[0]);
u->Health = HEALTH_HORNET;
}
ChangeState(SpriteNum, s_HornetRun[0]);
u->Attrib = &HornetAttrib;
DoActorSetSpeed(SpriteNum, NORM_SPEED);
u->StateEnd = s_HornetDie;
u->Rot = sg_HornetRun;
EnemyDefaults(SpriteNum, &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);
Set3DSoundOwner(SpriteNum);
return 0;
}
int NullHornet(DSWActor* actor)
{
USER* u = actor->u();
int SpriteNum = u->SpriteNum;
if (TEST(u->Flags,SPR_SLIDING))
DoActorSlide(actor);
DoHornetMatchPlayerZ(SpriteNum);
DoActorSectorDamage(actor);
return 0;
}
int DoHornetMatchPlayerZ(short SpriteNum)
{
SPRITEp sp = &sprite[SpriteNum];
USERp u = User[SpriteNum].Data();
SPRITEp tsp = User[SpriteNum]->tgt_sp;
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;
}
#define HORNET_BOB_AMT (Z(16))
// 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->lo_sp)
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->hi_sp)
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();
int SpriteNum = u->SpriteNum;
SPRITEp sp = &sprite[SpriteNum];
u->ActorActionFunc = DoHornetCircle;
NewStateGroup(SpriteNum, u->ActorActionSet->Run);
// set it close
DoActorSetSpeed(SpriteNum, 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();
int SpriteNum = u->SpriteNum;
SPRITEp sp = &sprite[SpriteNum];
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(SpriteNum, nx, ny, 0L))
{
//ActorMoveHitReact(SpriteNum);
// 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(SpriteNum, 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();
int SpriteNum = u->SpriteNum;
SPRITEp sp = &sprite[SpriteNum];
int nx, ny;
if (TEST(u->Flags, SPR_FALLING))
{
u->loz = u->zclip;
DoFall(SpriteNum);
}
else
{
RESET(sp->cstat, CSTAT_SPRITE_YCENTER);
u->jump_speed = 0;
u->floor_dist = 0;
DoBeginFall(SpriteNum);
DoFindGroundPoint(SpriteNum);
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->ret = move_sprite(SpriteNum, 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(SpriteNum, u->ActorActionSet->Dead);
DeleteNoSoundOwner(SpriteNum);
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 SpriteNum = u->SpriteNum;
int i;
SPRITEp sp = &sprite[SpriteNum], tsp;
USERp tu;
int dist, pdist, a,b,c;
PLAYERp pp;
if (!MoveSkip8) return 0; // Don't over check
if (!u->tgt_sp) return 0;
// Who's the closest meat!?
DoActorPickClosePlayer(SpriteNum);
if (User[u->tgt_sp - sprite]->PlayerP)
{
pp = User[u->tgt_sp - sprite]->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)
{
tsp = &sprite[i];
tu = User[i].Data();
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->tgt_sp = tsp; // Set target to swarm center
}
}
return true;
}
int DoHornetMove(DSWActor* actor)
{
USER* u = actor->u();
int SpriteNum = u->SpriteNum;
SPRITEp sp = &sprite[SpriteNum];
// 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(SpriteNum, ACTORMOVETICS);
else
(*u->ActorActionFunc)(actor);
DoHornetMatchPlayerZ(SpriteNum);
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