raze-gles/source/sw/src/hornet.cpp
Christoph Oelckers 75c76ccf66 - changed SW's PlaySound interface to take pointers to objects instead of pointers to coordinates.
With pointers to coordinates the sound engine cannot work, so this had to be changed globally.
2019-12-18 11:09:01 +01:00

680 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 "keys.h"
#include "names2.h"
#include "game.h"
#include "tags.h"
#include "ai.h"
#include "actor.h"
#include "weapon.h"
#include "track.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, NULL },
};
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,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL, //climb
NULL, //pain
sg_HornetDie,
NULL,
sg_HornetDead,
NULL,
NULL,
{NULL},
{0},
{NULL},
{0},
{NULL},
NULL,
NULL
};
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];
ASSERT(u);
}
else
{
User[SpriteNum] = 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(short SpriteNum)
{
USERp u = User[SpriteNum];
SPRITEp sp = User[SpriteNum]->SpriteP;
if (TEST(u->Flags,SPR_SLIDING))
DoActorSlide(SpriteNum);
DoHornetMatchPlayerZ(SpriteNum);
DoActorSectorDamage(SpriteNum);
return 0;
}
int DoHornetMatchPlayerZ(short SpriteNum)
{
SPRITEp sp = &sprite[SpriteNum];
USERp u = User[SpriteNum];
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 + RANDOM_RANGE(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] && SectUser[u->lo_sectp - sector]->depth)
loz -= Z(SectUser[u->lo_sectp - sector]->depth) - 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 + ((HORNET_BOB_AMT * (int)sintable[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(short SpriteNum)
{
SPRITEp sp = &sprite[SpriteNum];
USERp u = User[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 = (RANDOM_RANGE(3)+1) * 60;
(*u->ActorActionFunc)(SpriteNum);
return 0;
}
int DoHornetCircle(short SpriteNum)
{
SPRITEp sp = &sprite[SpriteNum];
USERp u = User[SpriteNum];
SPRITEp tsp = User[SpriteNum]->tgt_sp;
int nx,ny,bound;
sp->ang = NORM_ANGLE(sp->ang + u->Counter2);
nx = sp->xvel * (int) sintable[NORM_ANGLE(sp->ang + 512)] >> 14;
ny = sp->xvel * (int) sintable[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 = sp->xvel * (int) sintable[NORM_ANGLE(sp->ang + 512)] >> 14;
ny = sp->xvel * (int) sintable[sp->ang] >> 14;
if (!move_actor(SpriteNum, nx, ny, 0L))
{
InitActorReposition(SpriteNum);
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(SpriteNum);
return 0;
}
// time out
if ((u->WaitTics -= ACTORMOVETICS) < 0)
{
InitActorReposition(SpriteNum);
u->WaitTics = 0;
return 0;
}
return 0;
}
int
DoHornetDeath(short SpriteNum)
{
SPRITEp sp = &sprite[SpriteNum];
USERp u = User[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(SpriteNum);
// slide while falling
nx = sp->xvel * (int) sintable[NORM_ANGLE(sp->ang + 512)] >> 14;
ny = sp->xvel * (int) sintable[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(short SpriteNum)
{
short i,nexti;
SPRITEp sp = &sprite[SpriteNum], tsp;
USERp u = User[SpriteNum], 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
TRAVERSE_SPRITE_STAT(headspritestat[STAT_ENEMY], i, nexti)
{
tsp = &sprite[i];
tu = User[i];
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(short SpriteNum)
{
SPRITEp sp = &sprite[SpriteNum];
USERp u = User[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(SpriteNum);
if (TEST(u->Flags,SPR_SLIDING))
DoActorSlide(SpriteNum);
if (u->track >= 0)
ActorFollowTrack(SpriteNum, ACTORMOVETICS);
else
(*u->ActorActionFunc)(SpriteNum);
DoHornetMatchPlayerZ(SpriteNum);
DoActorSectorDamage(SpriteNum);
return 0;
}
#include "saveable.h"
static saveable_code saveable_hornet_code[] =
{
SAVE_CODE(SetupHornet),
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