2910 lines
No EOL
68 KiB
C++
2910 lines
No EOL
68 KiB
C++
/*
|
|
================================================================
|
|
HOVERBIKE
|
|
================================================================
|
|
|
|
Copyright (C) 1998 by 2015, Inc.
|
|
All rights reserved.
|
|
|
|
This source is may not be distributed and/or modified without
|
|
expressly written permission by 2015, Inc.
|
|
|
|
AI NOTE:
|
|
Although I didn't have time to impliment it, the hoverbike should be
|
|
(for the most part) structured properly to allow for AI control of one.
|
|
I was planning on having the AI communicate to the hoverbike through
|
|
fake usercmd_t info that the AI would put together and send to bike to
|
|
tell it were to go. This note's here in hopes that you're an industrious
|
|
soul and want to make a Sin bot that can use hoverbikes. ;)
|
|
*/
|
|
|
|
#include "hoverbike.h"
|
|
#include "player.h"
|
|
|
|
//==============================================================
|
|
|
|
// hovering stuff
|
|
#define HOVER_HEIGHT 48
|
|
#define HOVER_FORCE 60
|
|
#define HOVER_GROUNDGRAVITY 275
|
|
// forward movement stuff
|
|
#define HOVER_MAX_SPEED 800
|
|
#define HOVER_ACCEL_CEILING 1200
|
|
#define HOVER_ACCEL 300
|
|
//braking stuff
|
|
#define HOVER_MIN_SPEED -100
|
|
#define HOVER_BRAKES 600
|
|
// side movement stuff
|
|
#define HOVER_STRAFE 700
|
|
#define HOVER_MAX_SIDE 250
|
|
#define HOVER_STRAFE_ROLL 20
|
|
#define HOVER_ROLL 50
|
|
// vertical booster
|
|
#define HOVER_BOOSTER 300
|
|
#define HOVER_BOOSTER_USERATE 5
|
|
// non-movement drift %
|
|
#define HOVER_DRIFT -0.65
|
|
#define HOVER_SIDE_DRIFT -0.575
|
|
#define HOVER_MIN_DRIFT 2
|
|
#define HOVER_MAX_DRIFT 12
|
|
// for pitch adjustments made mid-air
|
|
#define HOVER_FLY_PITCH -30
|
|
#define HOVER_PITCH_SPEED 35
|
|
// for turbo booster stuff
|
|
#define HOVER_TURBO_REFILL 0.3
|
|
#define HOVER_TURBO_USERATE 3
|
|
#define HOVER_TURBO_BOOST 400
|
|
#define HOVER_TURBO_MAXSPEED 1200
|
|
// hoverbike health
|
|
#define HOVER_MAX_HEALTH 250
|
|
// timming delays for some stuff
|
|
#define HOVER_EFFECTS_TIME 0.2
|
|
|
|
//==============================================================
|
|
// hoverbike movement flags used when
|
|
// deciding what hovering sound to play
|
|
|
|
#define HBSND_NONE 0
|
|
#define HBSND_HOVERING 1 // set when the bike is hovering off the ground
|
|
#define HBSND_CLOSE 2 // set when hoverbike is hover close to the ground
|
|
#define HBSND_ACCEL 4 // set when hoverbike is accelerating
|
|
#define HBSND_BRAKE 8 // set when hoverbike is braking
|
|
|
|
//==============================================================
|
|
// various physics functions
|
|
void G_AddGravity (Entity *ent);
|
|
void G_CheckVelocity (Entity *ent);
|
|
int G_ClipVelocity (Vector& in, Vector& normal, Vector& out, float overbounce, int gravaxis);
|
|
void G_Impact (Entity *e1, trace_t *trace);
|
|
|
|
//==============================================================
|
|
// check for world effects on hoverbike (like lava)
|
|
|
|
void Hoverbike_WorldEffects(Hoverbike *bike)
|
|
{
|
|
bike->watertype = gi.pointcontents(bike->worldorigin.vec3());
|
|
|
|
if(bike->watertype & CONTENTS_LAVA)
|
|
{
|
|
bike->Damage(world, world, level.lavadamage, bike->worldorigin, vec_zero, vec_zero, 0, DAMAGE_NO_ARMOR, MOD_LAVA, -1, -1, 1.0f);
|
|
}
|
|
}
|
|
|
|
//==============================================================
|
|
/* Hoverbike movement physics
|
|
First goes through each of the bike's seperate bounding boxes
|
|
to see how far the whole bike can actually move. After that, it
|
|
then actually moves the bike and the player.
|
|
*/
|
|
|
|
#define MAX_CLIP_PLANES 5
|
|
#define STOP_EPSILON 0.1
|
|
#define sv_friction 6
|
|
#define sv_stopspeed 100
|
|
|
|
int G_Physics_Hoverbike(Hoverbike *bike)
|
|
{
|
|
Entity *hit, *passent;
|
|
HoverbikeBox *front, *back;
|
|
trace_t trace, trace2;
|
|
int blocked, mask;
|
|
int bumpcount, numbumps;
|
|
Vector original_velocity;
|
|
Vector dir, org, end, forward;
|
|
float time_left, nearest_hit, speed;
|
|
int hit_pos;
|
|
float friction, newspeed, control;
|
|
float bounceback;
|
|
|
|
end = Vector(0, bike->angles[YAW], 0);
|
|
end.AngleVectors(&forward, NULL, NULL);
|
|
|
|
assert(bike->frontbox);
|
|
front = bike->frontbox;
|
|
|
|
assert(bike->backbox);
|
|
back = bike->backbox;
|
|
|
|
if(bike->rider)
|
|
{
|
|
passent = bike->rider;
|
|
}
|
|
else
|
|
{
|
|
// refill turbo booster if needed
|
|
bike->turbo += HOVER_TURBO_REFILL;
|
|
if(bike->turbo > 100)
|
|
bike->turbo = 100;
|
|
|
|
// add gravity
|
|
if(!bike->groundentity)
|
|
G_AddGravity(bike);
|
|
|
|
if(bike->groundentity)
|
|
{
|
|
if(bike->velocity.z > 0)
|
|
{
|
|
bike->groundentity = NULL;
|
|
}
|
|
else
|
|
{
|
|
bike->velocity = vec_zero;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
passent = NULL;
|
|
// need to set the seperate parts as not solid
|
|
// to prevent collision problems between them
|
|
bike->setSolidType(SOLID_NOT);
|
|
bike->frontbox->setSolidType(SOLID_NOT);
|
|
bike->backbox->setSolidType(SOLID_NOT);
|
|
}
|
|
|
|
numbumps = 4;
|
|
|
|
G_CheckVelocity(bike);
|
|
|
|
blocked = 0;
|
|
original_velocity = bike->velocity;
|
|
|
|
time_left = FRAMETIME;
|
|
mask = MASK_PLAYERSOLID;
|
|
|
|
for(bumpcount = 0; bumpcount < numbumps; bumpcount++)
|
|
{
|
|
// we need to do distance checks for each box to see what's the first thing we hit
|
|
nearest_hit = 1;
|
|
friction = 0;
|
|
bounceback = 1.25;
|
|
|
|
// first check front
|
|
org = bike->worldorigin + forward*front->offset;
|
|
end = org + time_left*bike->velocity;
|
|
trace2 = G_Trace(org, front->mins, front->maxs, end, passent, mask, "G_Physics_Hoverbike");
|
|
// check for running something over
|
|
speed = bike->velocity.length();
|
|
if((passent) && (trace2.ent) && speed > 200)
|
|
{
|
|
hit = trace2.ent->entity;
|
|
if(hit->takedamage)
|
|
{
|
|
int damg;
|
|
|
|
if (
|
|
(hit->isSubclassOf(Sentient) && ((Sentient *)hit)->IsOnBike()) ||
|
|
hit->isSubclassOf(Hoverbike) ||
|
|
hit->isSubclassOf(HoverbikeBox)
|
|
)
|
|
damg = 0;
|
|
else if(hit->isSubclassOf(Sentient))
|
|
damg = (speed - 150)*0.5;
|
|
else
|
|
damg = (speed - 150)*0.2;
|
|
|
|
if(damg > 0)
|
|
{
|
|
if(bike->rider)
|
|
hit->Damage(bike, bike->rider, damg, bike->origin, bike->velocity, vec_zero, (damg*0.5), 0, MOD_HOVERBIKE, -1, -1, 1.0f);
|
|
else
|
|
hit->Damage(bike, bike, damg, bike->origin, bike->velocity, vec_zero, (damg*0.5), 0, MOD_HOVERBIKE, -1, -1, 1.0f);
|
|
}
|
|
}
|
|
}
|
|
// basically ignore the trace if the extra bounding box is in something solid
|
|
if(trace2.startsolid || trace2.allsolid)
|
|
{
|
|
trace2.fraction = 1;
|
|
}
|
|
else if(trace2.fraction < nearest_hit)
|
|
{
|
|
// set collision stuff
|
|
nearest_hit = trace2.fraction;
|
|
hit_pos = 1; // indicates that it was the front that bumped
|
|
trace = trace2;
|
|
}
|
|
|
|
// now check back
|
|
org = bike->worldorigin + forward*back->offset;
|
|
end = org + time_left*bike->velocity;
|
|
trace2 = G_Trace(org, back->mins, back->maxs, end, passent, mask, "G_Physics_Hoverbike");
|
|
// basically ignore the trace if the extra bounding box is in something solid
|
|
if(trace2.startsolid || trace2.allsolid)
|
|
{
|
|
trace2.fraction = 1;
|
|
}
|
|
else if(trace2.fraction < nearest_hit)
|
|
{
|
|
// set collision stuff
|
|
nearest_hit = trace2.fraction;
|
|
hit_pos = 2; // indicates that it was the back that bumped
|
|
trace = trace2;
|
|
}
|
|
|
|
// now check main bike box
|
|
end = bike->worldorigin + time_left*bike->velocity;
|
|
trace2 = G_Trace(bike->worldorigin, bike->mins, bike->maxs, end, passent, mask, "G_Physics_Hoverbike");
|
|
if(trace2.allsolid)
|
|
{
|
|
// bike is trapped in another solid
|
|
if(!bike->rider)
|
|
{
|
|
bike->setSolidType(SOLID_BBOX);
|
|
bike->frontbox->setSolidType(SOLID_BBOX);
|
|
bike->backbox->setSolidType(SOLID_BBOX);
|
|
}
|
|
return 3;
|
|
}
|
|
// use this trace if main trace structure hasn't been set yet
|
|
if(trace2.fraction < nearest_hit || nearest_hit == 1)
|
|
{
|
|
// set collision stuff
|
|
nearest_hit = trace2.fraction;
|
|
hit_pos = 0; // indicates that it was the middle that bumped
|
|
trace = trace2;
|
|
}
|
|
else // do a trace for the middle according to the shortest distance moveable
|
|
{
|
|
nearest_hit -= 0.01;
|
|
if(nearest_hit < 0)
|
|
nearest_hit = 0;
|
|
end = bike->worldorigin + bike->velocity*time_left*nearest_hit;
|
|
trace2 = G_Trace(bike->worldorigin, bike->mins, bike->maxs, end, passent, mask, "G_Physics_Hoverbike");
|
|
trace.endpos[0] = trace2.endpos[0];
|
|
trace.endpos[1] = trace2.endpos[1];
|
|
trace.endpos[2] = trace2.endpos[2];
|
|
}
|
|
|
|
if(trace.fraction > 0)
|
|
{
|
|
// actually covered some distance
|
|
bike->setOrigin(trace.endpos);
|
|
org = bike->worldorigin + forward*front->offset;
|
|
front->setOrigin(org);
|
|
org = bike->worldorigin + forward*back->offset;
|
|
back->setOrigin(org);
|
|
}
|
|
|
|
if(trace.fraction == 1) // moved the entire distance
|
|
{
|
|
break;
|
|
}
|
|
|
|
hit = trace.ent->entity;
|
|
|
|
if((trace.plane.normal[gravity_axis[bike->gravaxis].z]*gravity_axis[bike->gravaxis].sign) > 0.7)
|
|
{
|
|
// floor
|
|
blocked |= 1;
|
|
|
|
// apply friction to non-hovering bikes
|
|
if(!bike->rider)
|
|
{
|
|
if(hit->getSolidType() == SOLID_BSP)
|
|
{
|
|
bike->groundentity = hit->edict;
|
|
bike->groundentity_linkcount = hit->edict->linkcount;
|
|
bike->groundplane = trace.plane;
|
|
bike->groundsurface = trace.surface;
|
|
bike->groundcontents = trace.contents;
|
|
}
|
|
|
|
friction = sv_friction;
|
|
}
|
|
else
|
|
{
|
|
bounceback = 1;
|
|
}
|
|
}
|
|
else if((trace.plane.normal[gravity_axis[bike->gravaxis].z]*gravity_axis[bike->gravaxis].sign) < -0.7)
|
|
{
|
|
if(bike->velocity[gravity_axis[bike->gravaxis].z] > 400)
|
|
{
|
|
friction = 2;
|
|
}
|
|
else if(bike->velocity[gravity_axis[bike->gravaxis].z] > 50)
|
|
{
|
|
friction = 1;
|
|
}
|
|
|
|
bounceback = 1.1;
|
|
}
|
|
else // slow down a bit from hitting a wall
|
|
{
|
|
speed = sqrt(bike->velocity[gravity_axis[bike->gravaxis].x]*bike->velocity[gravity_axis[bike->gravaxis].x]
|
|
+ bike->velocity[gravity_axis[bike->gravaxis].y]*bike->velocity[gravity_axis[bike->gravaxis].y]);
|
|
if(speed > HOVER_MAX_SPEED*0.6)
|
|
{
|
|
friction = 1;
|
|
}
|
|
else if(speed > 75)
|
|
{
|
|
friction = 0.5;
|
|
}
|
|
}
|
|
|
|
// apply friction if needed
|
|
if(friction)
|
|
{
|
|
speed = sqrt(bike->velocity[gravity_axis[bike->gravaxis].x]*bike->velocity[gravity_axis[bike->gravaxis].x]
|
|
+ bike->velocity[gravity_axis[bike->gravaxis].y]*bike->velocity[gravity_axis[bike->gravaxis].y]);
|
|
|
|
control = speed < sv_stopspeed ? sv_stopspeed : speed;
|
|
newspeed = speed - FRAMETIME*control*friction;
|
|
|
|
if(newspeed < 0)
|
|
{
|
|
newspeed = 0;
|
|
}
|
|
|
|
if(speed <= 0)
|
|
newspeed = 1;
|
|
else
|
|
newspeed /= speed;
|
|
|
|
// apply the friction in the way needed
|
|
if(trace.plane.normal[gravity_axis[bike->gravaxis].z] > 0.7)
|
|
{
|
|
bike->velocity.x *= newspeed;
|
|
bike->velocity.y *= newspeed;
|
|
if(bike->velocity.z < 0)
|
|
bike->velocity.z = 0;
|
|
}
|
|
else if(trace.plane.normal[gravity_axis[bike->gravaxis].z] < -0.7)
|
|
{
|
|
bike->velocity.x *= newspeed;
|
|
bike->velocity.y *= newspeed;
|
|
if(bike->velocity.z > 0)
|
|
bike->velocity.z = 0;
|
|
}
|
|
else
|
|
{
|
|
bike->velocity *= newspeed;
|
|
}
|
|
}
|
|
|
|
G_ClipVelocity(bike->velocity, Vector(trace.plane.normal), bike->velocity,
|
|
bounceback, bike->gravaxis);
|
|
|
|
// run the impact function
|
|
// run impact function for rider if touched an item
|
|
if(hit_pos == 1)
|
|
G_Impact(front, &trace);
|
|
else if(hit_pos == 2)
|
|
G_Impact(back, &trace);
|
|
else
|
|
G_Impact(bike, &trace);
|
|
if(!bike->edict->inuse)
|
|
{
|
|
break; // removed by the impact function
|
|
}
|
|
|
|
time_left -= time_left * trace.fraction;
|
|
}
|
|
|
|
// make appropriate collision sound if needed
|
|
bike->CollisionSound(original_velocity, bike->velocity);
|
|
|
|
// check for damage from world effects at final location
|
|
org = bike->worldorigin + Vector(0, 0, bike->mins.z);
|
|
if(gi.pointcontents(org.vec3()) & CONTENTS_LAVA)
|
|
{
|
|
// check a bit higher up too
|
|
org = bike->worldorigin;
|
|
if(gi.pointcontents(org.vec3()) & CONTENTS_LAVA)
|
|
{
|
|
bike->Damage(world, world, level.lavadamage * 3, bike->worldorigin, vec_zero, vec_zero, 0, DAMAGE_NO_ARMOR, MOD_LAVA, -1, -1, 1.0f );
|
|
}
|
|
else
|
|
{
|
|
bike->Damage(world, world, level.lavadamage * 2, bike->worldorigin, vec_zero, vec_zero, 0, DAMAGE_NO_ARMOR, MOD_LAVA, -1, -1, 1.0f );
|
|
}
|
|
}
|
|
|
|
if(!bike->edict->inuse)
|
|
{
|
|
return blocked; // destroyed by lava
|
|
}
|
|
|
|
|
|
if(!bike->rider)
|
|
{
|
|
bike->setSolidType(SOLID_BBOX);
|
|
bike->frontbox->setSolidType(SOLID_BBOX);
|
|
bike->backbox->setSolidType(SOLID_BBOX);
|
|
}
|
|
|
|
return blocked;
|
|
}
|
|
|
|
//==============================================================
|
|
|
|
// this variation of G_TouchTriggers allows players to touch items with their hoverbike
|
|
void Hoverbike_TouchTriggers(Entity *ent, Entity *rider)
|
|
{
|
|
int i, num;
|
|
edict_t *touch[MAX_EDICTS];
|
|
edict_t *hit;
|
|
Event *ev;
|
|
Vector absmin, absmax, end;
|
|
|
|
// dead things don't activate triggers!
|
|
if(rider->health <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
absmin = ent->absmin;
|
|
absmax = ent->absmax;
|
|
// extend touch area down to the ground for picking up items
|
|
if(ent->isSubclassOf(Hoverbike))
|
|
{
|
|
if(ent->groundentity)
|
|
{
|
|
absmin[gravity_axis[rider->gravaxis].z] -= 32*gravity_axis[rider->gravaxis].sign;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(((HoverbikeBox *)ent)->bike->groundentity)
|
|
{
|
|
absmin[gravity_axis[rider->gravaxis].z] -= 32*gravity_axis[rider->gravaxis].sign;
|
|
}
|
|
}
|
|
|
|
num = gi.BoxEdicts(absmin.vec3(), absmax.vec3(), touch, MAX_EDICTS, AREA_TRIGGERS);
|
|
|
|
// be careful, it is possible to have an entity in this
|
|
// list removed before we get to it (killtriggered)
|
|
for(i = 0; i < num; i++)
|
|
{
|
|
hit = touch[i];
|
|
if(!hit->inuse || (hit->entity == ent))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
assert(hit->entity);
|
|
|
|
// FIXME
|
|
// post the events on the list with zero time
|
|
ev = new Event(EV_Touch);
|
|
ev->AddEntity(rider);
|
|
hit->entity->ProcessEvent(ev);
|
|
}
|
|
}
|
|
|
|
//==============================================================
|
|
// HoverbikeBox entity
|
|
// serves as the extra bounding boxes for a hoverbike
|
|
//==============================================================
|
|
|
|
CLASS_DECLARATION(Entity, HoverbikeBox, NULL);
|
|
|
|
ResponseDef HoverbikeBox::Responses[] =
|
|
{
|
|
{&EV_Use, (Response)HoverbikeBox::BikeUse},
|
|
{&EV_Damage, (Response)HoverbikeBox::BikeDamage},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
HoverbikeBox::HoverbikeBox()
|
|
{
|
|
setSolidType(SOLID_BBOX);
|
|
setMoveType(MOVETYPE_NONE);
|
|
hideModel();
|
|
|
|
takedamage = DAMAGE_YES;
|
|
health = 32000;
|
|
|
|
// don't need to send hoverbike bounding boxes to clients
|
|
edict->svflags |= SVF_NOCLIENT;
|
|
}
|
|
|
|
// a hoverbike's bounding box was used, so pass
|
|
// the use event on to the main bike entity
|
|
void HoverbikeBox::BikeUse(Event *ev)
|
|
{
|
|
// just to be safe
|
|
if(!bike)
|
|
{
|
|
PostEvent(EV_Remove, 0);
|
|
return;
|
|
}
|
|
|
|
((Hoverbike *)bike.Ptr())->BikeUse(ev);
|
|
}
|
|
|
|
void HoverbikeBox::BikeDamage(Event *ev)
|
|
{
|
|
Event *event;
|
|
|
|
// make sure we have a bike
|
|
if(!bike)
|
|
return;
|
|
|
|
// don't bother if bike is dead
|
|
if(bike->deadflag)
|
|
return;
|
|
|
|
// pass damage along to the main bike entity
|
|
event = new Event(ev);
|
|
bike->ProcessEvent(event);
|
|
}
|
|
|
|
//==============================================================
|
|
// Hoverbike
|
|
// main entity for a hoverbike
|
|
//==============================================================
|
|
|
|
CLASS_DECLARATION(Entity, Hoverbike, "vehicles_hoverbike");
|
|
|
|
Event EV_Hoverbike_Respawn("respawn");
|
|
|
|
// script commands
|
|
Event EV_Hoverbike_Controlled("controlled");
|
|
Event EV_Hoverbike_Uncontrolled("uncontrolled");
|
|
Event EV_Hoverbike_GetRider("getrider");
|
|
Event EV_Hoverbike_ReleaseRider("releaserider");
|
|
Event EV_Hoverbike_Controls("controls");
|
|
Event EV_Hoverbike_TurnSpeed("yawspeed");
|
|
Event EV_Hoverbike_TargetYaw("targetyaw");
|
|
|
|
ResponseDef Hoverbike::Responses[] =
|
|
{
|
|
{&EV_Use, (Response)Hoverbike::BikeUse},
|
|
{&EV_Pain, (Response)Hoverbike::BikePain},
|
|
{&EV_Killed, (Response)Hoverbike::BikeKilled},
|
|
{&EV_Hoverbike_Respawn, (Response)Hoverbike::BikeRespawn},
|
|
|
|
// script command responces
|
|
{&EV_Hoverbike_Controlled, (Response)Hoverbike::ControlledEvent},
|
|
{&EV_Hoverbike_Uncontrolled, (Response)Hoverbike::UncontrolledEvent},
|
|
{&EV_Hoverbike_GetRider, (Response)Hoverbike::BikeUse},
|
|
{&EV_Hoverbike_ReleaseRider, (Response)Hoverbike::ReleaseRider},
|
|
{&EV_Hoverbike_Controls, (Response)Hoverbike::ControlsEvent},
|
|
{&EV_Hoverbike_TurnSpeed, (Response)Hoverbike::YawSpeedEvent},
|
|
{&EV_Hoverbike_TargetYaw, (Response)Hoverbike::TargetYawEvent},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
Hoverbike::Hoverbike()
|
|
{
|
|
Vector tmpvec;
|
|
|
|
// hoverbikes don't work with MFD
|
|
if(deathmatch->value == DEATHMATCH_MFD)
|
|
{
|
|
gi.printf("Marked for Death does not work with hoverbikes... bike being removed\n");
|
|
PostEvent(EV_Remove, 0);
|
|
return;
|
|
}
|
|
|
|
// script commands vars
|
|
controlled = false;
|
|
scriptturboing = false;
|
|
scripttargetyaw = 0;
|
|
scriptturnspeed = 0;
|
|
|
|
effectstimmer = 0;
|
|
soundtimmer = 0;
|
|
|
|
takedamage = DAMAGE_YES;
|
|
health = HOVER_MAX_HEALTH;
|
|
max_health = HOVER_MAX_HEALTH;
|
|
deadflag = 0;
|
|
mass = 800;
|
|
rider = NULL;
|
|
player = false;
|
|
respawntimer = 0;
|
|
strafingroll = 0;
|
|
bobsin = 0;
|
|
bobfrac = 0;
|
|
weaponmode = 0;
|
|
if(deathmatch->value)
|
|
{
|
|
rockets = 40;
|
|
bullets = 60;
|
|
mines = 5;
|
|
}
|
|
else
|
|
{
|
|
rockets = 100;
|
|
bullets = 250;
|
|
mines = 10;
|
|
}
|
|
sndflags = HBSND_NONE;
|
|
turbo = 100;
|
|
forwardmove = 0;
|
|
sidemove = 0;
|
|
upmove = 0;
|
|
flags |= FL_POSTTHINK;
|
|
// just marks this as a hoverbike to the game since
|
|
// it's not attached to a parent
|
|
edict->s.effects |= EF_HOVER; //***
|
|
|
|
setOrigin(origin);
|
|
setSolidType(SOLID_BBOX);
|
|
setMoveType(MOVETYPE_HOVERBIKE);
|
|
|
|
angles = Vector( 0, G_GetFloatArg("angle", 0), 0);
|
|
scripttargetyaw = angles[YAW];
|
|
setAngles(angles);
|
|
|
|
setModel("bike_prototype.def");
|
|
showModel();
|
|
|
|
// precache some other models
|
|
modelIndex("view_hoverbike.def");
|
|
modelIndex("hb_ammonum.def");
|
|
modelIndex("hb_health.def");
|
|
modelIndex("hb_speedbar.def");
|
|
modelIndex("hb_speednum.def");
|
|
modelIndex("hb_turbo.def");
|
|
modelIndex("hb_health.def");
|
|
modelIndex("hb_ammonum.def");
|
|
modelIndex("hb_weap.def");
|
|
modelIndex("stinger.def");
|
|
modelIndex("hovermine.def");
|
|
modelIndex("beam_bike.def");
|
|
|
|
// make the front and back bounding boxes now
|
|
frontbox = new HoverbikeBox;
|
|
backbox = new HoverbikeBox;
|
|
|
|
// set gravityaxis and bounding box sizes
|
|
gravaxis = G_GetIntArg("gravityaxis", 0);
|
|
if(gravaxis < 0)
|
|
gravaxis = 0;
|
|
else if(gravaxis > 5)
|
|
gravaxis = 5;
|
|
SetGravityAxis(gravaxis);
|
|
|
|
// make front bounding box
|
|
frontbox->offset = 40;
|
|
//tmpvec = origin + Vector(orientation[0])*box->offset;
|
|
tmpvec[gravity_axis[gravaxis].x] = orientation[0][0]*frontbox->offset;
|
|
tmpvec[gravity_axis[gravaxis].y] = orientation[0][1]*frontbox->offset*gravity_axis[gravaxis].sign;
|
|
tmpvec[gravity_axis[gravaxis].z] = orientation[0][2]*frontbox->offset*gravity_axis[gravaxis].sign;
|
|
tmpvec += origin;
|
|
frontbox->setOrigin(tmpvec);
|
|
frontbox->bike = this;
|
|
|
|
// make back bounding box
|
|
backbox->offset = -40;
|
|
// tmpvec = origin + Vector(orientation[0])*box->offset;
|
|
tmpvec[gravity_axis[gravaxis].x] = orientation[0][0]*backbox->offset;
|
|
tmpvec[gravity_axis[gravaxis].y] = orientation[0][1]*backbox->offset*gravity_axis[gravaxis].sign;
|
|
tmpvec[gravity_axis[gravaxis].z] = orientation[0][2]*backbox->offset*gravity_axis[gravaxis].sign;
|
|
tmpvec += origin;
|
|
backbox->setOrigin(tmpvec);
|
|
backbox->bike = this;
|
|
|
|
move_angles = angles;
|
|
ApplyMoveAngles();
|
|
// setup values for respawning
|
|
spawnspot = origin;
|
|
spawnangles = angles;
|
|
}
|
|
|
|
void Hoverbike::RespawnSetup(Vector spot, Vector ang, float respawndelay, int grav)
|
|
{
|
|
setSolidType(SOLID_NOT);
|
|
setMoveType(MOVETYPE_NONE);
|
|
hideModel();
|
|
edict->s.effects &= ~EF_HOVER;
|
|
|
|
spawnspot = spot;
|
|
spawnangles = ang;
|
|
gravaxis = grav;
|
|
|
|
PostEvent(EV_Hoverbike_Respawn, respawndelay);
|
|
}
|
|
|
|
void Hoverbike::BikeRespawn(Event *ev)
|
|
{
|
|
trace_t trace;
|
|
Vector pos, forward;
|
|
Vector tmins, tmaxs;
|
|
|
|
Vector tmpvec;
|
|
|
|
spawnangles.AngleVectors(&forward, NULL, NULL);
|
|
|
|
frontbox = new HoverbikeBox;
|
|
backbox = new HoverbikeBox;
|
|
|
|
// check that area's clear for respawn
|
|
tmins = mins;
|
|
tmaxs = maxs;
|
|
pos = spawnspot;
|
|
trace = G_Trace(pos, tmins, tmaxs, pos, NULL, MASK_PLAYERSOLID, "Hoverbike::BikeRespawn");
|
|
if(trace.allsolid) // can't respawn yet
|
|
{
|
|
// try again later
|
|
PostEvent(EV_Hoverbike_Respawn, 1);
|
|
frontbox->ProcessEvent(EV_Remove);
|
|
backbox->ProcessEvent(EV_Remove);
|
|
return;
|
|
}
|
|
|
|
tmins = frontbox->mins;
|
|
tmaxs = frontbox->maxs;
|
|
pos = spawnspot + forward*40;
|
|
trace = G_Trace(pos, tmins, tmaxs, pos, NULL, MASK_PLAYERSOLID, "Hoverbike::BikeRespawn");
|
|
if(trace.allsolid) // can't respawn yet
|
|
{
|
|
// try again later
|
|
PostEvent(EV_Hoverbike_Respawn, 1);
|
|
frontbox->ProcessEvent(EV_Remove);
|
|
backbox->ProcessEvent(EV_Remove);
|
|
return;
|
|
}
|
|
|
|
tmins = backbox->mins;
|
|
tmaxs = backbox->maxs;
|
|
pos = spawnspot - forward*40;
|
|
trace = G_Trace(pos, tmins, tmaxs, pos, NULL, MASK_PLAYERSOLID, "Hoverbike::BikeRespawn");
|
|
if(trace.allsolid) // can't respawn yet
|
|
{
|
|
// try again later
|
|
PostEvent(EV_Hoverbike_Respawn, 1);
|
|
frontbox->ProcessEvent(EV_Remove);
|
|
backbox->ProcessEvent(EV_Remove);
|
|
return;
|
|
}
|
|
|
|
// call inti function to set everything up
|
|
takedamage = DAMAGE_YES;
|
|
health = HOVER_MAX_HEALTH;
|
|
max_health = HOVER_MAX_HEALTH;
|
|
deadflag = 0;
|
|
|
|
rider = NULL;
|
|
player = false;
|
|
respawntimer = 0;
|
|
strafingroll = 0;
|
|
bobsin = 0;
|
|
bobfrac = 0;
|
|
weaponmode = 0;
|
|
rockets = 40;
|
|
bullets = 60;
|
|
mines = 5;
|
|
sndflags = HBSND_NONE;
|
|
turbo = 100;
|
|
forwardmove = 0;
|
|
sidemove = 0;
|
|
upmove = 0;
|
|
flags |= FL_POSTTHINK;
|
|
edict->s.effects |= EF_HOVER;
|
|
|
|
setOrigin(spawnspot);
|
|
setSolidType(SOLID_BBOX);
|
|
setMoveType(MOVETYPE_HOVERBIKE);
|
|
velocity = vec_zero;
|
|
|
|
angles = spawnangles;
|
|
setAngles(angles);
|
|
|
|
edict->svflags &= ~SVF_NOCLIENT;
|
|
setModel("bike_prototype.def");
|
|
showModel();
|
|
|
|
SetGravityAxis(gravaxis);
|
|
|
|
// make front bounding box
|
|
frontbox->offset = 40;
|
|
// tmpvec = origin + Vector(orientation[0])*box->offset;
|
|
tmpvec[gravity_axis[gravaxis].x] = orientation[0][0]*frontbox->offset;
|
|
tmpvec[gravity_axis[gravaxis].y] = orientation[0][1]*frontbox->offset*gravity_axis[gravaxis].sign;
|
|
tmpvec[gravity_axis[gravaxis].z] = orientation[0][2]*frontbox->offset*gravity_axis[gravaxis].sign;
|
|
tmpvec += origin;
|
|
frontbox->setOrigin(tmpvec);
|
|
frontbox->bike = this;
|
|
|
|
// make back bounding box
|
|
backbox->offset = -40;
|
|
// tmpvec = origin + Vector(orientation[0])*box->offset;
|
|
tmpvec[gravity_axis[gravaxis].x] = orientation[0][0]*backbox->offset;
|
|
tmpvec[gravity_axis[gravaxis].y] = orientation[0][1]*backbox->offset*gravity_axis[gravaxis].sign;
|
|
tmpvec[gravity_axis[gravaxis].z] = orientation[0][2]*backbox->offset*gravity_axis[gravaxis].sign;
|
|
tmpvec += origin;
|
|
backbox->setOrigin(tmpvec);
|
|
backbox->bike = this;
|
|
|
|
// make visual effects
|
|
SpawnTeleportEffect(origin, 124);
|
|
SpawnTeleportEffect(frontbox->origin, 124);
|
|
SpawnTeleportEffect(backbox->origin, 124);
|
|
}
|
|
|
|
//==============================================================
|
|
|
|
void Hoverbike::SetRiderAngles (Vector angles)
|
|
{
|
|
int i;
|
|
|
|
if (!rider)
|
|
return;
|
|
|
|
if(!rider->client)
|
|
return;
|
|
|
|
for(i = 0; i < 2; i++)
|
|
{
|
|
rider->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(angles[i] - rider->client->resp.cmd_angles[i]);
|
|
}
|
|
}
|
|
|
|
void Hoverbike::SetRiderYaw (float yawangle)
|
|
{
|
|
if (!rider)
|
|
return;
|
|
|
|
if(!rider->client)
|
|
return;
|
|
|
|
rider->client->ps.pmove.delta_angles[YAW] = ANGLE2SHORT(yawangle - rider->client->resp.cmd_angles[YAW]);
|
|
}
|
|
|
|
void Hoverbike::BikeUse(Event *ev)
|
|
{
|
|
Entity *other;
|
|
Weapon *weapon;
|
|
|
|
// already in use
|
|
if(rider)
|
|
return;
|
|
|
|
other = ev->GetEntity(1);
|
|
|
|
if(!other)
|
|
return;
|
|
|
|
// mutants can't use hoverbikes
|
|
if(other->flags & (FL_MUTANT|FL_SP_MUTANT))
|
|
return;
|
|
|
|
// never got around to adding AI support for hoverbikes
|
|
if(!other->isSubclassOf(Player))
|
|
return;
|
|
|
|
// can't get on a bike while using a guided missile
|
|
if(((Player *)other)->ViewMode() == MISSILE_VIEW)
|
|
return;
|
|
|
|
// player's already using a bike
|
|
if(((Player *)other)->GetHoverbike())
|
|
return;
|
|
|
|
rider = (Sentient *)other;
|
|
player = true;
|
|
|
|
// set stuff for the bike
|
|
setMoveType(MOVETYPE_NONE);
|
|
SetGravityAxis(gravaxis);
|
|
hideModel();
|
|
edict->s.effects &= ~EF_HOVER;
|
|
oldweapon = rider->currentWeapon;
|
|
// debounce the use (turbo) key
|
|
getontimer = level.time + 0.5;
|
|
// don't need to send hoverbike since it's just a bounding box now
|
|
edict->svflags |= SVF_NOCLIENT;
|
|
|
|
// setup a bike respawn if we need to
|
|
if(deathmatch->value && !respawntimer)
|
|
{
|
|
Hoverbike *newbike;
|
|
newbike = new Hoverbike;
|
|
newbike->RespawnSetup(spawnspot, spawnangles, 20, gravaxis);
|
|
respawntimer = 1;
|
|
}
|
|
|
|
// set stuff for the rider
|
|
rider->setOrigin(origin);
|
|
rider->velocity = velocity;
|
|
SetRiderAngles(angles);
|
|
weapon = rider->giveWeapon("HoverWeap");
|
|
assert(weapon);
|
|
rider->ForceChangeWeapon(weapon);
|
|
rider->SetGravityAxis(gravaxis);
|
|
|
|
rider->currentWeapon->edict->s.effects |= EF_HOVER;
|
|
effectstimmer = level.time;
|
|
soundtimmer = level.time + HOVER_EFFECTS_TIME*0.5;
|
|
|
|
MakeGuages();
|
|
|
|
if(player)
|
|
{
|
|
Player *pent;
|
|
str model;
|
|
|
|
pent = (Player *)rider.Ptr();
|
|
pent->hoverbike = this;
|
|
pent->vehicleanim = "ride";
|
|
|
|
if(pent->ViewMode() == THIRD_PERSON)
|
|
pent->SetViewMode(THIRD_PERSON);
|
|
|
|
// set bike's model & skin to the player's choice
|
|
edict->s.renderfx |= RF_CUSTOMSKIN;
|
|
edict->s.skinnum = pent->edict->s.number - 1;
|
|
|
|
// Make sure that the model is allowed
|
|
model = pent->client->pers.bikemodel;
|
|
if(!game.ValidBikeModels.ObjectInList(model))
|
|
{
|
|
model = "bike_prototype.def";
|
|
}
|
|
|
|
setModel(model.c_str());
|
|
|
|
// also set skin for visible hoverbike representation
|
|
rider->currentWeapon->edict->s.renderfx |= RF_CUSTOMSKIN;
|
|
rider->currentWeapon->edict->s.skinnum = pent->edict->s.number - 1;
|
|
rider->currentWeapon->SetModels(model.c_str(), "view_hoverbike.def");
|
|
}
|
|
}
|
|
|
|
void Hoverbike::BikeGetOff(void)
|
|
{
|
|
trace_t trace;
|
|
Vector pos, tmpmins, tmpmaxs, right;
|
|
Vector t[ 3 ];
|
|
int mask, rideroff;
|
|
Weapon *weapon;
|
|
|
|
if(!rider)
|
|
return;
|
|
|
|
// if dead, then we're getting the player off elsewere
|
|
if(deadflag)
|
|
return;
|
|
|
|
// don't effect the side vector by roll or pitch
|
|
pos = Vector(0, angles[YAW], 0);
|
|
pos.AngleVectors( &t[0], &t[1], &t[2] );
|
|
right[ gravity_axis[gravaxis].x ] = t[ 1 ][ 0 ];
|
|
right[ gravity_axis[gravaxis].y ] = t[ 1 ][ 1 ] * gravity_axis[gravaxis].sign;
|
|
right[ gravity_axis[gravaxis].z ] = t[ 1 ][ 2 ] * gravity_axis[gravaxis].sign;
|
|
right *= -1;
|
|
|
|
// move the player off of the bike
|
|
rideroff = 0;
|
|
if(player)
|
|
mask = MASK_PLAYERSOLID;
|
|
else
|
|
mask = MASK_MONSTERSOLID;
|
|
tmpmins = Vector(-16, -16, -16);
|
|
tmpmaxs = Vector(16, 16, 16);
|
|
if(rider->health <= 0)
|
|
{
|
|
if(gravity_axis[gravaxis].sign < 0)
|
|
{
|
|
tmpmins[gravity_axis[gravaxis].z] = -CROUCH_HEIGHT;
|
|
tmpmaxs[gravity_axis[gravaxis].z] = 0;
|
|
}
|
|
else
|
|
{
|
|
tmpmins[gravity_axis[gravaxis].z] = 0;
|
|
tmpmaxs[gravity_axis[gravaxis].z] = CROUCH_HEIGHT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(gravity_axis[gravaxis].sign < 0)
|
|
{
|
|
tmpmins[gravity_axis[gravaxis].z] = -STAND_HEIGHT;
|
|
tmpmaxs[gravity_axis[gravaxis].z] = 0;
|
|
}
|
|
else
|
|
{
|
|
tmpmins[gravity_axis[gravaxis].z] = 0;
|
|
tmpmaxs[gravity_axis[gravaxis].z] = STAND_HEIGHT;
|
|
}
|
|
}
|
|
|
|
if(velocity.length() > 400)
|
|
{
|
|
pos = origin;
|
|
pos[gravity_axis[gravaxis].z] += 40*gravity_axis[gravaxis].sign;
|
|
trace = gi.trace(pos.vec3(), tmpmins.vec3(), tmpmaxs.vec3(), pos.vec3(), NULL, mask);
|
|
if(!trace.allsolid)
|
|
{
|
|
rider->setOrigin(trace.endpos);
|
|
rideroff = 1;
|
|
velocity *= 0.75;
|
|
rider->velocity[gravity_axis[gravaxis].z] += 150*gravity_axis[gravaxis].sign;
|
|
}
|
|
}
|
|
|
|
if(!rideroff)
|
|
{
|
|
pos = origin + right*48;
|
|
trace = gi.trace(pos.vec3(), tmpmins.vec3(), tmpmaxs.vec3(), pos.vec3(), NULL, mask);
|
|
if(!trace.allsolid)
|
|
{
|
|
rider->setOrigin(trace.endpos);
|
|
rideroff = 1;
|
|
}
|
|
|
|
if(!rideroff)
|
|
{
|
|
pos = origin - right*48;
|
|
trace = gi.trace(pos.vec3(), tmpmins.vec3(), tmpmaxs.vec3(), pos.vec3(), NULL, mask);
|
|
if(!trace.allsolid)
|
|
{
|
|
rider->setOrigin(trace.endpos);
|
|
rideroff = 1;
|
|
}
|
|
|
|
if(!rideroff)
|
|
{
|
|
pos = origin;
|
|
pos[gravity_axis[gravaxis].z] += 40*gravity_axis[gravaxis].sign;
|
|
trace = gi.trace(pos.vec3(), tmpmins.vec3(), tmpmaxs.vec3(), pos.vec3(), NULL, mask);
|
|
if(!trace.allsolid)
|
|
{
|
|
rider->setOrigin(trace.endpos);
|
|
rideroff = 1;
|
|
velocity *= 0.75;
|
|
rider->velocity[gravity_axis[gravaxis].z] += 150*gravity_axis[gravaxis].sign;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// player couldn't get off
|
|
if(!rideroff)
|
|
{
|
|
// force a dead player to get off
|
|
if(rider->health <= 0)
|
|
{
|
|
pos = origin;
|
|
pos[gravity_axis[gravaxis].z] += 16*gravity_axis[gravaxis].sign;
|
|
rider->setOrigin(pos);
|
|
rideroff = 1;
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
//set stuff for the rider
|
|
// only worry about weapon stuff if player is not dead
|
|
if(rider->health > 0)
|
|
{
|
|
// switch player back to previous weapon
|
|
if(oldweapon)
|
|
{
|
|
weapon = oldweapon;
|
|
}
|
|
else
|
|
{
|
|
// switch player to fists
|
|
weapon = (Weapon *)rider->FindItem("fists");
|
|
}
|
|
|
|
if(weapon)
|
|
rider->ForceChangeWeapon(weapon);
|
|
}
|
|
rider->takeWeapon("HoverWeap");
|
|
|
|
KillGuages();
|
|
|
|
if(player)
|
|
{
|
|
Player *pent;
|
|
|
|
pent = (Player *)rider.Ptr();
|
|
pent->hoverbike = NULL;
|
|
|
|
if(pent->ViewMode() == THIRD_PERSON)
|
|
pent->SetViewMode(THIRD_PERSON);
|
|
}
|
|
// turn off the hovering sounds
|
|
sndflags = HBSND_NONE;
|
|
soundtimmer = 0;
|
|
rider->edict->s.sound = 0;
|
|
|
|
// set stuff for the bike
|
|
rider = NULL;
|
|
player = false;
|
|
setMoveType(MOVETYPE_HOVERBIKE);
|
|
//set the hoverbike's angles
|
|
move_angles[PITCH] = move_angles[ROLL] = 0;
|
|
ApplyMoveAngles();
|
|
SetGravityAxis(gravaxis);
|
|
showModel();
|
|
edict->s.effects |= EF_HOVER;
|
|
// send hoverbike to client again
|
|
edict->svflags &= ~SVF_NOCLIENT;
|
|
// remove after a minute of not being used if in DM
|
|
if(deathmatch->value)
|
|
{
|
|
// remove after only 5 sec in a bikes only map
|
|
if(level.bikesonly)
|
|
respawntimer = level.time + 5;
|
|
else
|
|
{
|
|
respawntimer = health * 0.2;
|
|
if(respawntimer < 3)
|
|
respawntimer = 3;
|
|
respawntimer += level.time;
|
|
}
|
|
}
|
|
}
|
|
|
|
//==============================================================
|
|
|
|
void Hoverbike::BikePain(Event *ev)
|
|
{
|
|
Vector pos, dir;
|
|
int count;
|
|
|
|
// make a visual pain cue
|
|
pos = ev->GetVector(3);
|
|
if(pos != vec_zero)
|
|
{
|
|
count = (int)ev->GetFloat(1);
|
|
dir = pos - origin;
|
|
dir.normalize();
|
|
SpawnSparks(pos, dir, count);
|
|
}
|
|
|
|
if(damagetimer < level.time)
|
|
{
|
|
RandomSound("hb_collide", 1, CHAN_VOICE, ATTN_NORM);
|
|
damagetimer = level.time + 1;
|
|
}
|
|
}
|
|
|
|
void Hoverbike::BikeKilled(Event *ev)
|
|
{
|
|
// bike is already dead
|
|
if(deadflag)
|
|
return;
|
|
|
|
// set the bike as being dead
|
|
deadflag = 1;
|
|
|
|
// if in a bike only map, also kill the rider
|
|
if(level.bikesonly && rider)
|
|
{
|
|
Event *event;
|
|
|
|
event = new Event(EV_Killed);
|
|
event->AddEntity(ev->GetEntity(1)); // attacker
|
|
event->AddInteger(1000); // damage (doesn't matter)
|
|
event->AddEntity(ev->GetEntity(3)); // inflictor
|
|
event->AddString("all"); // just say they were hit in the all
|
|
event->AddInteger(ev->GetInteger(5)); // MOD
|
|
rider->ProcessEvent(event);
|
|
}
|
|
|
|
// if the bike had a rider, give the killer an extra frag
|
|
if(rider && player && !level.bikesonly)
|
|
{
|
|
Entity *attacker;
|
|
const char *message1 = NULL;
|
|
const char *message2 = "";
|
|
int num;
|
|
|
|
|
|
attacker = ev->GetEntity(1);
|
|
if(attacker && attacker->isClient())
|
|
{
|
|
if(attacker == rider)
|
|
{
|
|
num = G_Random(2);
|
|
switch (num)
|
|
{
|
|
case 0:
|
|
message1 = "shot his hoverbike in the foot";
|
|
break;
|
|
default:
|
|
message1 = "felt it would be better to go on foot";
|
|
break;
|
|
}
|
|
gi.bprintf(PRINT_MEDIUM, "%s %s\n", rider->client->pers.netname, message1);
|
|
attacker->client->resp.score--;
|
|
}
|
|
else
|
|
{
|
|
num = G_Random(3);
|
|
switch (num)
|
|
{
|
|
case 0:
|
|
message1 = "'s hoverbike was destroyed by";
|
|
break;
|
|
case 1:
|
|
message1 = " lost his hoverbike to";
|
|
break;
|
|
default:
|
|
message1 = " gave up his hoverbike to give";
|
|
message2 = " a chance";
|
|
break;
|
|
}
|
|
|
|
gi.bprintf(PRINT_MEDIUM, "%s%s %s%s\n", rider->client->pers.netname,
|
|
message1,
|
|
attacker->client->pers.netname,
|
|
message2);
|
|
attacker->client->resp.score++;
|
|
}
|
|
}
|
|
|
|
// make sure that we toss our rider
|
|
BikeGetOff();
|
|
}
|
|
|
|
// he still here? just toss 'em
|
|
if(rider)
|
|
{
|
|
Weapon *weapon;
|
|
|
|
// only worry about weapon stuff if player is not dead
|
|
if(rider->health > 0)
|
|
{
|
|
// switch player back to previous weapon
|
|
if(oldweapon)
|
|
{
|
|
weapon = oldweapon;
|
|
}
|
|
else
|
|
{
|
|
// switch player to fists
|
|
weapon = (Weapon *)rider->FindItem("fists");
|
|
}
|
|
|
|
if(weapon)
|
|
rider->ForceChangeWeapon(weapon);
|
|
}
|
|
rider->takeWeapon("HoverWeap");
|
|
|
|
KillGuages();
|
|
|
|
if(player)
|
|
{
|
|
Player *pent;
|
|
|
|
pent = (Player *)rider.Ptr();
|
|
pent->hoverbike = NULL;
|
|
if(pent->ViewMode() == THIRD_PERSON)
|
|
pent->SetViewMode(THIRD_PERSON);
|
|
}
|
|
// turn off the hovering sounds
|
|
sndflags = HBSND_NONE;
|
|
SetHoverSound();
|
|
|
|
// set stuff for the bike
|
|
rider = NULL;
|
|
player = false;
|
|
angles[PITCH] = 0;
|
|
if(rand()%2)
|
|
angles[ROLL] = 5;
|
|
else
|
|
angles[ROLL] = -5;
|
|
move_angles[PITCH] = move_angles[ROLL] = 0;
|
|
setAngles(angles);
|
|
}
|
|
|
|
SpawnRocketExplosion(frontbox->origin);
|
|
SpawnRocketExplosion(backbox->origin);
|
|
|
|
frontbox->setSolidType(SOLID_NOT);
|
|
frontbox->PostEvent(EV_Remove, 0.1);
|
|
|
|
backbox->setSolidType(SOLID_NOT);
|
|
backbox->PostEvent(EV_Remove, 0.1);
|
|
|
|
setSolidType(SOLID_NOT);
|
|
setMoveType(MOVETYPE_NONE);
|
|
hideModel();
|
|
edict->s.effects &= ~EF_HOVER;
|
|
|
|
// if in deathmatch, respawn after 15 seconds
|
|
if(deathmatch->value && !respawntimer)
|
|
{
|
|
Hoverbike *newbike;
|
|
newbike = new Hoverbike;
|
|
newbike->RespawnSetup(spawnspot, spawnangles, 15, gravaxis);
|
|
respawntimer = 1;
|
|
}
|
|
|
|
PostEvent(EV_Remove, 0.1);
|
|
}
|
|
|
|
void Hoverbike::GiveExtraFrag(Entity *attacker)
|
|
{
|
|
const char *message1 = NULL;
|
|
int num;
|
|
|
|
if(attacker && attacker->isClient())
|
|
{
|
|
if(attacker != rider)
|
|
{
|
|
num = G_Random(3);
|
|
switch (num)
|
|
{
|
|
case 0:
|
|
message1 = " was shot off his bike by";
|
|
break;
|
|
case 1:
|
|
message1 = " was pushed off his bike by";
|
|
break;
|
|
default:
|
|
message1 = " was convinced to not ride a bike by";
|
|
break;
|
|
}
|
|
|
|
gi.bprintf(PRINT_MEDIUM, "%s%s %s\n", rider->client->pers.netname,
|
|
message1, attacker->client->pers.netname);
|
|
attacker->client->resp.score++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//==============================================================
|
|
|
|
qboolean Hoverbike::Ride(usercmd_t *ucmd)
|
|
{
|
|
if(!rider)
|
|
return false;
|
|
|
|
// store movement commands in the bike
|
|
if(!controlled)
|
|
{
|
|
forwardmove = ucmd->forwardmove;
|
|
sidemove = ucmd->sidemove;
|
|
upmove = ucmd->upmove;
|
|
}
|
|
|
|
if(player)
|
|
{
|
|
// set player position and velocity
|
|
if(((Player *)rider.Ptr())->watchCamera != rider)
|
|
{
|
|
short temporigin[3];
|
|
|
|
//
|
|
// we save off the origin so that camera's origins are not messed up
|
|
//
|
|
temporigin[0] = rider->client->ps.pmove.origin[0];
|
|
temporigin[1] = rider->client->ps.pmove.origin[1];
|
|
temporigin[2] = rider->client->ps.pmove.origin[2];
|
|
|
|
RiderMove(ucmd);
|
|
|
|
rider->client->ps.pmove.origin[0] = temporigin[0];
|
|
rider->client->ps.pmove.origin[1] = temporigin[1];
|
|
rider->client->ps.pmove.origin[2] = temporigin[2];
|
|
}
|
|
else
|
|
{
|
|
RiderMove(ucmd);
|
|
}
|
|
}
|
|
|
|
rider->setAngles(angles);
|
|
|
|
return true;
|
|
}
|
|
|
|
void Hoverbike::RiderMove(usercmd_t *ucmd)
|
|
{
|
|
pmove_t pm;
|
|
Player *pent;
|
|
|
|
pent = (Player *)rider.Ptr();
|
|
|
|
// decide wether or not to do client side prediction
|
|
if(pent->ViewMode() == THIRD_PERSON)
|
|
{
|
|
pent->client->ps.pmove.pm_flags |= PMF_NO_PREDICTION;
|
|
}
|
|
else
|
|
{
|
|
pent->client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
|
|
}
|
|
|
|
pent->client->ps.pmove.pm_type = PM_ONBIKE;
|
|
pent->client->ps.pmove.gravity = 0;
|
|
pent->SetMoveInfo(&pm, ucmd);
|
|
pm.snapinitial = false;
|
|
|
|
// perform a pmove
|
|
gi.Pmove(&pm);
|
|
|
|
pent->GetMoveInfo(&pm);
|
|
}
|
|
|
|
void Hoverbike::Postthink(void)
|
|
{
|
|
Vector tmpvec;
|
|
|
|
// stuff to do if we don't have a rider
|
|
if(!rider)
|
|
{
|
|
// check for respawning if in deathmatch
|
|
if((deathmatch->value) && (respawntimer > 0) && (respawntimer < level.time))
|
|
{
|
|
// cause a respawn by killing the bike
|
|
ProcessEvent(EV_Killed);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if(!rider->IsOnBike())
|
|
{
|
|
// shouldn't happen, but just incase
|
|
return;
|
|
}
|
|
|
|
// check for the player getting off
|
|
// player can get off by ducking
|
|
// can't get off in a bikes only map
|
|
if(!level.bikesonly && (upmove < -50) && (getontimer < level.time))
|
|
{
|
|
BikeGetOff();
|
|
return;
|
|
}
|
|
|
|
// temporarily make the bike not solid
|
|
setSolidType(SOLID_NOT);
|
|
frontbox->setSolidType(SOLID_NOT);
|
|
backbox->setSolidType(SOLID_NOT);
|
|
|
|
if(controlled)
|
|
{
|
|
if(scriptturnspeed)
|
|
{
|
|
float yawofs;
|
|
|
|
yawofs = scripttargetyaw - move_angles[YAW];
|
|
if(yawofs < -180)
|
|
yawofs += 360;
|
|
if(yawofs > 180)
|
|
yawofs -= 360;
|
|
|
|
if(fabs(yawofs) <= scriptturnspeed)
|
|
{
|
|
move_angles[YAW] = scripttargetyaw;
|
|
}
|
|
else if(yawofs < 0)
|
|
{
|
|
move_angles[YAW] -= scriptturnspeed * FRAMETIME;
|
|
}
|
|
else if(yawofs > 0)
|
|
{
|
|
move_angles[YAW] += scriptturnspeed * FRAMETIME;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
move_angles[YAW] = scripttargetyaw;
|
|
}
|
|
}
|
|
else if(player)
|
|
{
|
|
move_angles[YAW] = ((Player *)rider.Ptr())->v_angle[YAW];
|
|
}
|
|
|
|
//calc the hover force & pitch angle
|
|
Hover();
|
|
//apply client's controls to the bike
|
|
ApplyControls();
|
|
//apply bike angle changes
|
|
ApplyMoveAngles();
|
|
//apply hoverbike movement physics
|
|
G_Physics_Hoverbike(this);
|
|
// check if bike is still alive
|
|
if(health <= 0)
|
|
return;
|
|
// check to see if the rider is no longer on the bike
|
|
if(!rider)
|
|
return;
|
|
|
|
//calculate the hoverbike's horizontal speed
|
|
speed = sqrt(velocity[0]*velocity[0] + velocity[1]*velocity[1]);
|
|
forward_speed = speed;
|
|
|
|
// set hoverbike's hovering sound
|
|
SetHoverSound();
|
|
|
|
// set origin for the weapon model
|
|
rider->currentWeapon->setOrigin(origin);
|
|
|
|
//update info guages
|
|
UpdateGuages();
|
|
|
|
// set rider's origin and velocity to the bike's
|
|
rider->setOrigin(origin);
|
|
rider->velocity = velocity;
|
|
// incriment view bobbing
|
|
bobsin += 0.2;
|
|
if(bobsin > M_PI*2)
|
|
bobsin -= M_PI*2;
|
|
bobfrac = sin(bobsin);
|
|
if(bobfrac < 0)
|
|
bobfrac *= -1;
|
|
|
|
// make the bike solid again
|
|
setSolidType(SOLID_BBOX);
|
|
frontbox->setSolidType(SOLID_BBOX);
|
|
backbox->setSolidType(SOLID_BBOX);
|
|
|
|
// touch any triggers we may be in
|
|
Hoverbike_TouchTriggers(this, rider);
|
|
Hoverbike_TouchTriggers(frontbox, rider);
|
|
Hoverbike_TouchTriggers(backbox, rider);
|
|
}
|
|
|
|
void Hoverbike::Hover(void)
|
|
{
|
|
trace_t f_trace, m_trace, b_trace;
|
|
Vector down, start, end;
|
|
Vector tmins, tmaxs;
|
|
float hoverpoints, hoverdist, tmpflt;
|
|
int hoverflags, mask, content;
|
|
const gravityaxis_t &grav = gravity_axis[gravaxis];
|
|
|
|
// clip mask to use for hovering
|
|
mask = MASK_PLAYERSOLID | MASK_WATER;
|
|
|
|
// this is used to keep track of various different
|
|
// things from the hovering traces
|
|
hoverflags = 0;
|
|
hoverpoints = 1;
|
|
|
|
down[grav.z] -= grav.sign;
|
|
tmins = Vector(-18, -18, -18);
|
|
tmaxs = Vector(18, 18, 18);
|
|
if(grav.sign > 0)
|
|
{
|
|
tmins[grav.z] = -12;
|
|
tmaxs[grav.z] = 0;
|
|
}
|
|
else
|
|
{
|
|
tmins[grav.z] = 0;
|
|
tmaxs[grav.z] = 12;
|
|
}
|
|
|
|
//get the trace for the front
|
|
start = frontbox->origin;
|
|
end = start;
|
|
end[grav.z] = origin[grav.z] - HOVER_HEIGHT*grav.sign;
|
|
f_trace = G_Trace(start, tmins, tmaxs, end, rider, mask, "Hoverbike::Hover");
|
|
// check if front is in something solid
|
|
if(f_trace.allsolid)
|
|
{
|
|
f_trace.fraction = 1;
|
|
hoverflags |= 8;
|
|
}
|
|
else
|
|
hoverpoints++;
|
|
// check if front is in water, if so, make sure there's some thrust
|
|
end = start + down*12;
|
|
content = gi.pointcontents(end.vec3());
|
|
if(content & MASK_WATER)
|
|
{
|
|
if(f_trace.fraction > 0.1)
|
|
f_trace.fraction = 0.1;
|
|
hoverflags |= 1;
|
|
}
|
|
|
|
//get the trace for the middle
|
|
start = origin;
|
|
end = start;
|
|
end[grav.z] = origin[grav.z] - HOVER_HEIGHT*grav.sign;
|
|
m_trace = G_Trace(start, tmins, tmaxs, end, rider, mask, "Hoverbike::Hover");
|
|
// check if middle is in water, if so, make sure there's some thrust
|
|
end = start + down*12;
|
|
content = gi.pointcontents(end.vec3());
|
|
if(content & MASK_WATER)
|
|
{
|
|
if(m_trace.fraction > 0.1)
|
|
m_trace.fraction = 0.1;
|
|
hoverflags |= 2;
|
|
}
|
|
|
|
//get the trace for the back
|
|
start = backbox->origin;
|
|
end = start;
|
|
end[grav.z] = origin[grav.z] - HOVER_HEIGHT*grav.sign;
|
|
b_trace = G_Trace(start, tmins, tmaxs, end, rider, mask, "Hoverbike::Hover");
|
|
// check if front is in something solid
|
|
if(b_trace.allsolid)
|
|
{
|
|
b_trace.fraction = 1;
|
|
hoverflags |= 16;
|
|
}
|
|
else
|
|
hoverpoints++;
|
|
// check if back is in water, if so, make sure there's some thrust
|
|
end = start + down*12;
|
|
content = gi.pointcontents(end.vec3());
|
|
if(content & MASK_WATER)
|
|
{
|
|
if(b_trace.fraction > 0.1)
|
|
b_trace.fraction = 0.1;
|
|
hoverflags |= 4;
|
|
}
|
|
|
|
//check if not even near the ground & add the correct
|
|
//amount of gravity while we're at it
|
|
if(f_trace.fraction == 1 && m_trace.fraction == 1 && b_trace.fraction == 1)
|
|
{
|
|
// add gravity (hacked to smooth out arial movement and bouncing)
|
|
if((velocity[grav.z]*grav.sign) > 0)
|
|
{
|
|
tmpflt = gravity*sv_gravity->value*FRAMETIME*0.9;
|
|
velocity += down*tmpflt;
|
|
}
|
|
else
|
|
{
|
|
tmpflt = gravity*sv_gravity->value*FRAMETIME*0.7;
|
|
velocity += down*tmpflt;
|
|
}
|
|
|
|
if((velocity[grav.z]*grav.sign) < 0
|
|
&& airpitch_timmer < level.time)
|
|
{
|
|
// tilt hoverbike's pitch in air for a nice, simple effect
|
|
if(move_angles[PITCH] > HOVER_FLY_PITCH)
|
|
{
|
|
move_angles[PITCH] -= HOVER_PITCH_SPEED*FRAMETIME;
|
|
if(move_angles[PITCH] < HOVER_FLY_PITCH)
|
|
move_angles[PITCH] = HOVER_FLY_PITCH;
|
|
}
|
|
else if(move_angles[PITCH] < HOVER_FLY_PITCH)
|
|
{
|
|
move_angles[PITCH] += HOVER_PITCH_SPEED*FRAMETIME;
|
|
if(move_angles[PITCH] > HOVER_FLY_PITCH)
|
|
move_angles[PITCH] = HOVER_FLY_PITCH;
|
|
}
|
|
}
|
|
// stop playing hovering sound
|
|
sndflags &= ~HBSND_HOVERING;
|
|
sndflags &= ~HBSND_CLOSE;
|
|
|
|
groundentity = NULL;
|
|
|
|
return;
|
|
}
|
|
else
|
|
airpitch_timmer = level.time + 0.5;
|
|
|
|
//add downward force
|
|
velocity += down*HOVER_GROUNDGRAVITY*FRAMETIME;
|
|
|
|
// set bike's ground entity
|
|
if(f_trace.fraction < m_trace.fraction)
|
|
{
|
|
if(f_trace.fraction < b_trace.fraction)
|
|
{
|
|
groundentity = f_trace.ent;
|
|
groundentity_linkcount = f_trace.ent->linkcount;
|
|
if(groundentity->entity->getSolidType() == SOLID_BSP)
|
|
{
|
|
groundplane = f_trace.plane;
|
|
groundsurface = f_trace.surface;
|
|
groundcontents = f_trace.contents;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
groundentity = b_trace.ent;
|
|
groundentity_linkcount = f_trace.ent->linkcount;
|
|
if(groundentity->entity->getSolidType() == SOLID_BSP)
|
|
{
|
|
groundplane = b_trace.plane;
|
|
groundsurface = b_trace.surface;
|
|
groundcontents = b_trace.contents;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(m_trace.fraction < b_trace.fraction)
|
|
{
|
|
groundentity = m_trace.ent;
|
|
groundentity_linkcount = f_trace.ent->linkcount;
|
|
if(groundentity->entity->getSolidType() == SOLID_BSP)
|
|
{
|
|
groundplane = m_trace.plane;
|
|
groundsurface = m_trace.surface;
|
|
groundcontents = m_trace.contents;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
groundentity = b_trace.ent;
|
|
groundentity_linkcount = f_trace.ent->linkcount;
|
|
if(groundentity->entity->getSolidType() == SOLID_BSP)
|
|
{
|
|
groundplane = b_trace.plane;
|
|
groundsurface = b_trace.surface;
|
|
groundcontents = b_trace.contents;
|
|
}
|
|
}
|
|
}
|
|
|
|
// calc hover distance
|
|
hoverdist = m_trace.fraction;
|
|
if(!(hoverflags & 8)) // front not stuck in something solid
|
|
hoverdist += f_trace.fraction;
|
|
if(!(hoverflags & 16)) // back not stuck in something solid
|
|
hoverdist += b_trace.fraction;
|
|
if(hoverdist)
|
|
hoverdist /= hoverpoints;
|
|
|
|
// calc hover direction (for when on extremly sloped surfaces)
|
|
down *= -0.8;
|
|
if(f_trace.fraction < 1 && !(hoverflags & 1))
|
|
down += Vector(f_trace.plane.normal);
|
|
if(m_trace.fraction < 1 && !(hoverflags & 2))
|
|
down += Vector(m_trace.plane.normal);
|
|
if(b_trace.fraction < 1 && !(hoverflags & 4))
|
|
down += Vector(b_trace.plane.normal);
|
|
down.normalize();
|
|
|
|
//calc hovering force
|
|
tmpflt = 1 - hoverdist;
|
|
tmpflt *= HOVER_FORCE;
|
|
tmpflt *= tmpflt;
|
|
tmpflt *= 0.5;
|
|
tmpflt = tmpflt*FRAMETIME;
|
|
|
|
velocity += down*tmpflt;
|
|
|
|
// an extra lift if still moving towards ground
|
|
if((velocity[grav.z]*grav.sign) < 0)
|
|
{
|
|
velocity += down*tmpflt;
|
|
|
|
if((velocity[grav.z]*grav.sign) > 0)
|
|
velocity[grav.z] = 0;
|
|
}
|
|
|
|
// set down back to normal
|
|
down = vec_zero;
|
|
down[grav.z] -= grav.sign;
|
|
|
|
// dampen upward velocity if not vertical boosting
|
|
if((velocity[grav.z]*grav.sign) < 0
|
|
|| boosttimmer < level.time)
|
|
{
|
|
velocity[gravity_axis[gravaxis].z] *= 0.65;
|
|
}
|
|
|
|
// play correct hovering sound
|
|
sndflags |= HBSND_HOVERING;
|
|
if(hoverdist < 0.5)
|
|
{
|
|
sndflags |= HBSND_CLOSE;
|
|
}
|
|
else
|
|
{
|
|
sndflags &= ~HBSND_CLOSE;
|
|
}
|
|
|
|
// calc pitch angle of the bike
|
|
if(hoverflags & 8) // front is inside a solid
|
|
{
|
|
if(hoverflags & 16) // back is also inside a solid
|
|
{
|
|
start = Vector(1, 0, 0); // will result in 0 pitch
|
|
}
|
|
else
|
|
{
|
|
// start = Vector(m_trace.endpos) - Vector(b_trace.endpos);
|
|
tmins[0] = m_trace.endpos[grav.x];
|
|
tmins[1] = m_trace.endpos[grav.y]*grav.sign;
|
|
tmins[2] = m_trace.endpos[grav.z]*grav.sign;
|
|
|
|
tmaxs[0] = b_trace.endpos[grav.x];
|
|
tmaxs[1] = b_trace.endpos[grav.y]*grav.sign;
|
|
tmaxs[2] = b_trace.endpos[grav.z]*grav.sign;
|
|
|
|
start = tmins - tmaxs;
|
|
}
|
|
}
|
|
else if(hoverflags & 16) // just the back is inside a solid
|
|
{
|
|
// start = Vector(f_trace.endpos) - Vector(m_trace.endpos);
|
|
tmins[0] = f_trace.endpos[grav.x];
|
|
tmins[1] = f_trace.endpos[grav.y]*grav.sign;
|
|
tmins[2] = f_trace.endpos[grav.z]*grav.sign;
|
|
|
|
tmaxs[0] = m_trace.endpos[grav.x];
|
|
tmaxs[1] = m_trace.endpos[grav.y]*grav.sign;
|
|
tmaxs[2] = m_trace.endpos[grav.z]*grav.sign;
|
|
|
|
start = tmins - tmaxs;
|
|
}
|
|
else // both front and back are ok
|
|
{
|
|
// start = Vector(f_trace.endpos) - Vector(b_trace.endpos);
|
|
tmins[0] = f_trace.endpos[grav.x];
|
|
tmins[1] = f_trace.endpos[grav.y]*grav.sign;
|
|
tmins[2] = f_trace.endpos[grav.z]*grav.sign;
|
|
|
|
tmaxs[0] = b_trace.endpos[grav.x];
|
|
tmaxs[1] = b_trace.endpos[grav.y]*grav.sign;
|
|
tmaxs[2] = b_trace.endpos[grav.z]*grav.sign;
|
|
|
|
start = tmins - tmaxs;
|
|
}
|
|
end = start.toAngles();
|
|
move_angles[PITCH] = -end[PITCH];
|
|
if(move_angles[PITCH] < -180)
|
|
move_angles[PITCH] += 360;
|
|
}
|
|
|
|
void Hoverbike::ApplyControls(void)
|
|
{
|
|
Vector forward, right, tmpvec;
|
|
float f_speed, s_speed, tmpflt;
|
|
Vector t[ 3 ];
|
|
const gravityaxis_t &grav = gravity_axis[gravaxis];
|
|
qboolean isturboing, seteffects;
|
|
|
|
// determine if we need to set the hoverbike's effects
|
|
if(effectstimmer < level.time)
|
|
{
|
|
seteffects = true;
|
|
|
|
effectstimmer = level.time + HOVER_EFFECTS_TIME;
|
|
}
|
|
else
|
|
{
|
|
seteffects = false;
|
|
}
|
|
|
|
// don't want to include roll in direction
|
|
// calcs, because some weird shit will happen
|
|
tmpvec[YAW] = move_angles[YAW];
|
|
if(!groundentity) // don't take pitch into account if in the air
|
|
{
|
|
tmpvec[PITCH] = 0;
|
|
}
|
|
else if(angles[PITCH] < 0)
|
|
tmpvec[PITCH] = move_angles[PITCH]*0.5;
|
|
else
|
|
tmpvec[PITCH] = move_angles[PITCH]*0.3;
|
|
|
|
// orient velocity directions to bike's gravityaxis
|
|
|
|
tmpvec.AngleVectors(&t[0], &t[1], &t[2]);
|
|
forward[grav.x] = t[0][0];
|
|
forward[grav.y] = t[0][1] * grav.sign;
|
|
forward[grav.z] = t[0][2] * grav.sign;
|
|
right[grav.x] = t[1][0];
|
|
right[grav.y] = t[1][1] * grav.sign;
|
|
right[grav.z] = t[1][2] * grav.sign;
|
|
|
|
//get forward & side speeds
|
|
f_speed = DotProduct(velocity.vec3(), forward.vec3());
|
|
s_speed = DotProduct(velocity.vec3(), right.vec3());
|
|
|
|
// set turboing value
|
|
isturboing = false;
|
|
if(scriptturboing)
|
|
{
|
|
isturboing = true;
|
|
}
|
|
else if(player)
|
|
{
|
|
if(((Player *)rider.Ptr())->Buttons() & BUTTON_USE)
|
|
isturboing = true;
|
|
}
|
|
|
|
//forward movement
|
|
|
|
//check for acceleration
|
|
if(forwardmove > 0 && f_speed < HOVER_MAX_SPEED)
|
|
{
|
|
if(f_speed < 0)
|
|
tmpflt = 1.5;
|
|
else
|
|
{
|
|
tmpflt = 1- (f_speed/HOVER_ACCEL_CEILING);
|
|
tmpflt *= tmpflt;
|
|
if(f_speed < (HOVER_MAX_SPEED*0.5))
|
|
tmpflt *= 1.5;
|
|
}
|
|
|
|
f_speed += HOVER_ACCEL*FRAMETIME*tmpflt;
|
|
|
|
// limit speed to max speed to prevent jerky movement
|
|
if(f_speed > HOVER_MAX_SPEED)
|
|
f_speed = HOVER_MAX_SPEED;
|
|
|
|
//change sound to accelerating sound
|
|
sndflags |= HBSND_ACCEL;
|
|
sndflags &= ~HBSND_BRAKE;
|
|
|
|
// set thruster effect
|
|
if(seteffects)
|
|
rider->currentWeapon->edict->s.effects |= EF_HOVERTHRUST;
|
|
}
|
|
//check for applying brakes
|
|
else if(forwardmove < 0 && f_speed > HOVER_MIN_SPEED)
|
|
{
|
|
f_speed -= (HOVER_BRAKES*FRAMETIME);
|
|
|
|
// limit speed to max speed to prevent jerky movement
|
|
if(f_speed < HOVER_MIN_SPEED)
|
|
f_speed = HOVER_MIN_SPEED;
|
|
|
|
// play braking sound
|
|
sndflags |= HBSND_BRAKE;
|
|
sndflags &= ~HBSND_ACCEL;
|
|
|
|
// set thruster effect off
|
|
if(seteffects)
|
|
rider->currentWeapon->edict->s.effects &= ~EF_HOVERTHRUST;
|
|
}
|
|
else
|
|
{
|
|
// set acceleration and brake sound flags
|
|
if(forwardmove > 0)
|
|
{
|
|
//change sound to accelerating sound
|
|
sndflags |= HBSND_ACCEL;
|
|
sndflags &= ~HBSND_BRAKE;
|
|
|
|
// set thruster effect
|
|
if(seteffects)
|
|
rider->currentWeapon->edict->s.effects |= EF_HOVERTHRUST;
|
|
}
|
|
else if(forwardmove < 0)
|
|
{
|
|
// play braking sound
|
|
sndflags |= HBSND_BRAKE;
|
|
sndflags &= ~HBSND_ACCEL;
|
|
|
|
// set thruster effect off
|
|
if(seteffects)
|
|
rider->currentWeapon->edict->s.effects &= ~EF_HOVERTHRUST;
|
|
}
|
|
else
|
|
{
|
|
// don't play either
|
|
sndflags &= ~HBSND_ACCEL;
|
|
sndflags &= ~HBSND_BRAKE;
|
|
|
|
// set thruster effect off
|
|
if(seteffects)
|
|
rider->currentWeapon->edict->s.effects &= ~EF_HOVERTHRUST;
|
|
}
|
|
|
|
// drift diff. over one second
|
|
tmpflt = f_speed*HOVER_DRIFT;
|
|
// scale it to a frame
|
|
tmpflt *= FRAMETIME;
|
|
// minimum amount of drift
|
|
if(tmpflt < HOVER_MIN_DRIFT && tmpflt > -HOVER_MIN_DRIFT)
|
|
{
|
|
if(tmpflt < 0)
|
|
tmpflt = -HOVER_MIN_DRIFT;
|
|
else if(tmpflt > 0)
|
|
tmpflt = HOVER_MIN_DRIFT;
|
|
}
|
|
// max amount of drift
|
|
else if(tmpflt > HOVER_MAX_DRIFT || tmpflt < -HOVER_MAX_DRIFT)
|
|
{
|
|
if(tmpflt > 0)
|
|
tmpflt = HOVER_MAX_DRIFT;
|
|
else
|
|
tmpflt = -HOVER_MAX_DRIFT;
|
|
}
|
|
// almost no drift when in the air
|
|
if(!groundentity)
|
|
tmpflt *= 0.01;
|
|
// take the drift out of the backward movement
|
|
if(f_speed < 0)
|
|
{
|
|
// don't drift below min speed if braking
|
|
if(forwardmove < 0 && f_speed < HOVER_MIN_SPEED)
|
|
{
|
|
f_speed += tmpflt;
|
|
if(f_speed > HOVER_MIN_SPEED)
|
|
f_speed = HOVER_MIN_SPEED;
|
|
}
|
|
else
|
|
{
|
|
f_speed += tmpflt;
|
|
if(f_speed > 0)
|
|
f_speed = 0;
|
|
}
|
|
}
|
|
else if(f_speed > 0) // take the drift out of the forward movement
|
|
{
|
|
// don't drift below max turbo speed if turboing
|
|
if(isturboing && turbo >= HOVER_TURBO_USERATE)
|
|
{
|
|
if(f_speed > HOVER_TURBO_MAXSPEED)
|
|
{
|
|
f_speed += tmpflt;
|
|
if(f_speed < HOVER_TURBO_MAXSPEED)
|
|
f_speed = HOVER_TURBO_MAXSPEED;
|
|
}
|
|
}
|
|
// don't drift below max speed if accelerating
|
|
else if(forwardmove > 0 && f_speed > HOVER_MAX_SPEED)
|
|
{
|
|
f_speed += tmpflt;
|
|
if(f_speed < HOVER_MAX_SPEED)
|
|
f_speed = HOVER_MAX_SPEED;
|
|
}
|
|
else
|
|
{
|
|
f_speed += tmpflt;
|
|
if(f_speed < 0)
|
|
f_speed = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// side movement
|
|
|
|
//moving to the left
|
|
if(sidemove < 0 && s_speed > (-HOVER_MAX_SIDE))
|
|
{
|
|
// less straifing control in the air
|
|
if(groundentity)
|
|
s_speed -= (HOVER_STRAFE*FRAMETIME);
|
|
else
|
|
s_speed -= (HOVER_STRAFE*FRAMETIME*0.75);
|
|
|
|
// limit speed to max speed to prevent jerky movement
|
|
if(s_speed < (-HOVER_MAX_SIDE))
|
|
s_speed = -HOVER_MAX_SIDE;
|
|
|
|
// roll bike to the left
|
|
if(strafingroll > (-HOVER_STRAFE_ROLL))
|
|
strafingroll -= HOVER_STRAFE_ROLL*0.4;
|
|
else
|
|
strafingroll = -HOVER_STRAFE_ROLL;
|
|
}
|
|
//moving to the right
|
|
else if(sidemove > 0 && s_speed < HOVER_MAX_SIDE)
|
|
{
|
|
// less straifing control in the air
|
|
if(groundentity)
|
|
s_speed += (HOVER_STRAFE*FRAMETIME);
|
|
else
|
|
s_speed += (HOVER_STRAFE*FRAMETIME*0.75);
|
|
|
|
// limit speed to max speed to prevent jerky movement
|
|
if(s_speed > HOVER_MAX_SIDE)
|
|
s_speed = HOVER_MAX_SIDE;
|
|
|
|
// roll bike to the right
|
|
if(strafingroll < HOVER_STRAFE_ROLL)
|
|
strafingroll += HOVER_STRAFE_ROLL*0.4;
|
|
else
|
|
strafingroll = HOVER_STRAFE_ROLL;
|
|
}
|
|
// drift down the sideways movement
|
|
else
|
|
{
|
|
// drift diff. over one second
|
|
tmpflt = s_speed*HOVER_SIDE_DRIFT;
|
|
// scale it to a frame
|
|
tmpflt *= FRAMETIME;
|
|
// minimum amount of drift
|
|
if(tmpflt > 0 && tmpflt < 1)
|
|
tmpflt = 1;
|
|
else if(tmpflt < 0 && tmpflt > -1)
|
|
tmpflt = -1;
|
|
// almost no drift when in the air
|
|
if(!groundentity)
|
|
tmpflt *= 0.05;
|
|
// take the drift out of the side movement
|
|
if(s_speed < 0)
|
|
{
|
|
// don't drift past max side speed if strafing left
|
|
if(sidemove < 0 && s_speed < (-HOVER_MAX_SIDE))
|
|
{
|
|
s_speed += tmpflt;
|
|
if(s_speed > (-HOVER_MAX_SIDE))
|
|
s_speed = (-HOVER_MAX_SIDE);
|
|
}
|
|
else
|
|
{
|
|
s_speed += tmpflt;
|
|
if(s_speed > 0)
|
|
s_speed = 0;
|
|
}
|
|
}
|
|
else if(s_speed > 0)
|
|
{
|
|
// don't drift past max side speed if strafing left
|
|
if(sidemove > 0 && s_speed > HOVER_MAX_SIDE)
|
|
{
|
|
s_speed += tmpflt;
|
|
if(s_speed < HOVER_MAX_SIDE)
|
|
s_speed = HOVER_MAX_SIDE;
|
|
}
|
|
else
|
|
{
|
|
s_speed += tmpflt;
|
|
if(s_speed < 0)
|
|
s_speed = 0;
|
|
}
|
|
}
|
|
// take care of strafe roll stuff
|
|
if(sidemove < 0) // still strafing left
|
|
{
|
|
// roll bike to the left
|
|
if(strafingroll > (-HOVER_STRAFE_ROLL))
|
|
strafingroll -= HOVER_STRAFE_ROLL*0.3;
|
|
else
|
|
strafingroll = -HOVER_STRAFE_ROLL;
|
|
}
|
|
else if(sidemove > 0) // still strafing right
|
|
{
|
|
// roll bike to the right
|
|
if(strafingroll < HOVER_STRAFE_ROLL)
|
|
strafingroll += HOVER_STRAFE_ROLL*0.3;
|
|
else
|
|
strafingroll = HOVER_STRAFE_ROLL;
|
|
}
|
|
else // not strafing
|
|
{
|
|
// reduce strafe roll if not 0
|
|
if(strafingroll < 0)
|
|
{
|
|
strafingroll += HOVER_STRAFE_ROLL*0.1;
|
|
if(strafingroll > 0)
|
|
strafingroll = 0;
|
|
}
|
|
else if(strafingroll > 0)
|
|
{
|
|
strafingroll -= HOVER_STRAFE_ROLL*0.1;
|
|
if(strafingroll < 0)
|
|
strafingroll = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// vertical booster movement
|
|
|
|
// check for vertical booster
|
|
if(upmove > 0)
|
|
{
|
|
if(boosttimmer < level.time)
|
|
{
|
|
if(groundentity)
|
|
{
|
|
if((velocity[grav.z]*grav.sign) < HOVER_BOOSTER)
|
|
velocity[grav.z] = HOVER_BOOSTER*grav.sign;
|
|
|
|
// play sound
|
|
RandomSound("hb_booster", 1, CHAN_BODY, ATTN_NORM);
|
|
|
|
// make particle effect for booster
|
|
SpawnHoverBoost(origin, angles[YAW]);
|
|
|
|
// set button release timmer
|
|
boosttimmer = level.time + 0.2;
|
|
}
|
|
}
|
|
else // player must release button to use again
|
|
boosttimmer = level.time + 0.2;
|
|
}
|
|
else
|
|
boosttimmer = 0;
|
|
|
|
// check for turbo booster
|
|
if(isturboing && (turbo >= HOVER_TURBO_USERATE || controlled)&& (getontimer < level.time))
|
|
{
|
|
turbo -= HOVER_TURBO_USERATE;
|
|
if(turbo < 0)
|
|
turbo = 0;
|
|
|
|
if(f_speed < HOVER_TURBO_MAXSPEED)
|
|
{
|
|
if(f_speed < 0)
|
|
tmpflt = 3;
|
|
else
|
|
{
|
|
tmpflt = 1- (f_speed/HOVER_ACCEL_CEILING);
|
|
tmpflt *= tmpflt;
|
|
if(f_speed < (HOVER_MAX_SPEED*0.5))
|
|
tmpflt *= 3;
|
|
else
|
|
tmpflt *= 2;
|
|
}
|
|
|
|
tmpflt = HOVER_ACCEL*FRAMETIME*tmpflt;
|
|
// make sure we have a minimal amount of thrust
|
|
if(tmpflt < HOVER_TURBO_BOOST*FRAMETIME)
|
|
tmpflt = HOVER_TURBO_BOOST*FRAMETIME;
|
|
|
|
f_speed += tmpflt;
|
|
|
|
if(f_speed > HOVER_TURBO_MAXSPEED)
|
|
f_speed = HOVER_TURBO_MAXSPEED;
|
|
}
|
|
|
|
// set turbo effect on
|
|
if(seteffects)
|
|
rider->currentWeapon->edict->s.effects |= EF_HOVERTURBO;
|
|
}
|
|
else
|
|
{
|
|
if(!isturboing) // only refill if not trying to boost
|
|
{
|
|
turbo += HOVER_TURBO_REFILL;
|
|
if(turbo > 100)
|
|
turbo = 100;
|
|
}
|
|
|
|
// set turbo effect off
|
|
if(seteffects)
|
|
rider->currentWeapon->edict->s.effects &= ~EF_HOVERTURBO;
|
|
}
|
|
|
|
// keep turbo filled if in god mode
|
|
if(rider->flags & FL_GODMODE)
|
|
turbo = 100;
|
|
|
|
//calc the roll angle for the sideways movement
|
|
if(s_speed == 0)
|
|
tmpflt = 0;
|
|
else
|
|
{
|
|
tmpflt = s_speed/HOVER_MAX_SPEED;
|
|
tmpflt *= -HOVER_ROLL;
|
|
if(tmpflt < (-HOVER_ROLL))
|
|
tmpflt = -HOVER_ROLL;
|
|
else if(tmpflt > HOVER_ROLL)
|
|
tmpflt = HOVER_ROLL;
|
|
}
|
|
move_angles[ROLL] = tmpflt + strafingroll;
|
|
|
|
//calc the new velocity
|
|
velocity[grav.x] = 0;
|
|
velocity[grav.y] = 0;
|
|
velocity += f_speed*forward;
|
|
velocity += s_speed*right;
|
|
}
|
|
|
|
void Hoverbike::ApplyMoveAngles(void)
|
|
{
|
|
Vector t[ 3 ];
|
|
Vector forward;
|
|
Vector right;
|
|
Vector up;
|
|
const gravityaxis_t &grav = gravity_axis[gravaxis];
|
|
|
|
// orient the bike's angles to its gravityaxis and apply them
|
|
|
|
move_angles.AngleVectors(&t[0], &t[1], &t[2]);
|
|
forward[grav.x] = t[0][0];
|
|
forward[grav.y] = t[0][1] * grav.sign;
|
|
forward[grav.z] = t[0][2] * grav.sign;
|
|
right[grav.x] = t[1][0];
|
|
right[grav.y] = t[1][1] * grav.sign;
|
|
right[grav.z] = t[1][2] * grav.sign;
|
|
up[grav.x] = t[2][0];
|
|
up[grav.y] = t[2][1] * grav.sign;
|
|
up[grav.z] = t[2][2] * grav.sign;
|
|
VectorsToEulerAngles(forward.vec3(), right.vec3(), up.vec3(), angles.vec3());
|
|
|
|
setAngles(angles);
|
|
}
|
|
|
|
void Hoverbike::SetGravityAxis(int axis)
|
|
{
|
|
Vector newmins, newmaxs;
|
|
|
|
edict->s.effects &= ~( EF_GRAVITY_AXIS_0 | EF_GRAVITY_AXIS_1 | EF_GRAVITY_AXIS_2 );
|
|
edict->s.effects |= GRAVITYAXIS_TO_EFFECTS( axis );
|
|
gravaxis = EFFECTS_TO_GRAVITYAXIS( edict->s.effects );
|
|
groundentity = NULL;
|
|
|
|
const gravityaxis_t &grav = gravity_axis[gravaxis];
|
|
|
|
// orient bounding boxes to new gravityaxis
|
|
newmins[grav.x] = -20;
|
|
newmins[grav.y] = -20;
|
|
if(rider)
|
|
{
|
|
if(grav.sign < 0)
|
|
newmins[grav.z] = -32;
|
|
else
|
|
newmins[grav.z] = -12;
|
|
}
|
|
else
|
|
{
|
|
if(grav.sign < 0)
|
|
newmins[grav.z] = -16;
|
|
else
|
|
newmins[grav.z] = -12;
|
|
}
|
|
|
|
newmaxs[grav.x] = 20;
|
|
newmaxs[grav.y] = 20;
|
|
if(rider)
|
|
{
|
|
if(grav.sign < 0)
|
|
newmaxs[grav.z] = 12;
|
|
else
|
|
newmaxs[grav.z] = 32;
|
|
}
|
|
else
|
|
{
|
|
if(grav.sign < 0)
|
|
newmaxs[grav.z] = 12;
|
|
else
|
|
newmaxs[grav.z] = 16;
|
|
}
|
|
|
|
setSize(newmins, newmaxs);
|
|
|
|
if(gravity_axis[gravaxis].sign < 0)
|
|
{
|
|
newmins[grav.z] = -32;
|
|
newmaxs[grav.z] = 12;
|
|
}
|
|
else
|
|
{
|
|
newmins[grav.z] = -12;
|
|
newmaxs[grav.z] = 32;
|
|
}
|
|
|
|
frontbox->setSize(newmins, newmaxs);
|
|
|
|
if(gravity_axis[gravaxis].sign < 0)
|
|
{
|
|
newmins[grav.z] = -24;
|
|
newmaxs[grav.z] = 12;
|
|
}
|
|
else
|
|
{
|
|
newmins[grav.z] = -12;
|
|
newmaxs[grav.z] = 24;
|
|
}
|
|
|
|
backbox->setSize(newmins, newmaxs);
|
|
}
|
|
|
|
//==============================================================
|
|
// set the hovering sound to play for the hoverbike
|
|
|
|
void Hoverbike::SetHoverSound(void)
|
|
{
|
|
str newsound;
|
|
|
|
// only set the bike's sound every other frame
|
|
if(soundtimmer < level.time)
|
|
{
|
|
soundtimmer = level.time + HOVER_EFFECTS_TIME;
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
|
|
// turbo sound takes presidence over the other sounds
|
|
if(rider->currentWeapon->edict->s.effects & EF_HOVERTURBO)
|
|
{
|
|
if(sndflags & HBSND_HOVERING)
|
|
{
|
|
if(sndflags & HBSND_ACCEL)
|
|
newsound = "hb_turboaccel";
|
|
else
|
|
newsound = "hb_turbohover";
|
|
}
|
|
else
|
|
newsound = "hb_turbo";
|
|
}
|
|
else if(sndflags & HBSND_HOVERING)
|
|
{
|
|
if(sndflags & HBSND_CLOSE)
|
|
{
|
|
if(sndflags & HBSND_ACCEL)
|
|
{
|
|
newsound = "hb_closeaccel";
|
|
}
|
|
else if(sndflags & HBSND_BRAKE)
|
|
{
|
|
newsound = "hb_closebrake";
|
|
}
|
|
else
|
|
{
|
|
newsound = "hb_closehover";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(sndflags & HBSND_ACCEL)
|
|
{
|
|
newsound = "hb_accel";
|
|
}
|
|
else if(sndflags & HBSND_BRAKE)
|
|
{
|
|
newsound = "hb_brake";
|
|
}
|
|
else
|
|
{
|
|
newsound = "hb_hover";
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(sndflags & HBSND_ACCEL)
|
|
{
|
|
newsound = "hb_accel";
|
|
}
|
|
else if(sndflags & HBSND_BRAKE)
|
|
{
|
|
newsound = "hb_brake";
|
|
}
|
|
else
|
|
{
|
|
newsound = "";
|
|
}
|
|
}
|
|
|
|
// hover sound is set each time to prevent special case problems
|
|
if(newsound == "")
|
|
{
|
|
rider->edict->s.sound = 0;
|
|
}
|
|
else
|
|
{
|
|
const char *soundname;
|
|
|
|
soundname = gi.Alias_FindRandom(edict->s.modelindex, newsound.c_str());
|
|
rider->edict->s.sound = gi.soundindex(soundname);
|
|
rider->edict->s.sound |= ATTN_NORM<<14;
|
|
}
|
|
|
|
currsound = newsound;
|
|
}
|
|
|
|
//==============================================================
|
|
|
|
void Hoverbike::WeaponChangeSound(void)
|
|
{
|
|
RandomSound("hb_weapswitch", 0.75, CHAN_BODY, ATTN_NORM);
|
|
}
|
|
|
|
void Hoverbike::WeaponNoAmmoSound(void)
|
|
{
|
|
RandomSound("hb_noammo", 1, CHAN_BODY, ATTN_NORM);
|
|
}
|
|
|
|
void Hoverbike::CollisionSound(Vector before, Vector after)
|
|
{
|
|
Vector diff;
|
|
float delta;
|
|
|
|
diff = after - before;
|
|
delta = diff.length();
|
|
|
|
// hard collision
|
|
if(delta > 800)
|
|
{
|
|
RandomSound("hb_collide", 1, CHAN_VOICE, ATTN_NORM);
|
|
}
|
|
// medium collision
|
|
else if(delta > 400)
|
|
{
|
|
RandomSound("hb_collide", 0.75, CHAN_VOICE, ATTN_NORM);
|
|
}
|
|
// just a scrape
|
|
else if(delta > 250)
|
|
{
|
|
RandomSound("hb_scrape", 1, CHAN_VOICE, ATTN_NORM);
|
|
}
|
|
// just a light scrape
|
|
else if(delta > 100)
|
|
{
|
|
RandomSound("hb_scrape", 0.5, CHAN_VOICE, ATTN_NORM);
|
|
}
|
|
}
|
|
|
|
//==============================================================
|
|
// weapon changing functions
|
|
|
|
void Hoverbike::NextWeapon(void)
|
|
{
|
|
weaponmode++;
|
|
if(weaponmode >= NUM_HOVERWEAPONS)
|
|
weaponmode = 0;
|
|
|
|
WeaponChangeSound();
|
|
rider->currentWeapon->NextAttack(0.5);
|
|
}
|
|
|
|
void Hoverbike::PreviousWeapon(void)
|
|
{
|
|
weaponmode--;
|
|
if(weaponmode < 0)
|
|
weaponmode = NUM_HOVERWEAPONS - 1;
|
|
WeaponChangeSound();
|
|
rider->currentWeapon->NextAttack(0.5);
|
|
}
|
|
|
|
void Hoverbike::SelectWeapon(int weapon)
|
|
{
|
|
if(weapon < 0)
|
|
weapon = 0;
|
|
else if(weapon >= NUM_HOVERWEAPONS)
|
|
weapon = NUM_HOVERWEAPONS - 1;
|
|
|
|
weaponmode = weapon;
|
|
|
|
WeaponChangeSound();
|
|
rider->currentWeapon->NextAttack(1);
|
|
}
|
|
|
|
//==============================================================
|
|
|
|
void Hoverbike::MakeGuages(void)
|
|
{
|
|
HoverbikeGuage *guage;
|
|
|
|
// only players need the guages
|
|
if(!player)
|
|
return;
|
|
|
|
if(!speed_bar)
|
|
guage = new HoverbikeGuage;
|
|
else
|
|
guage = speed_bar;
|
|
guage->Setup(rider, HBGUAGE_SPEEDBAR);
|
|
speed_bar = guage;
|
|
|
|
if(!speed_number)
|
|
guage = new HoverbikeGuage;
|
|
else
|
|
guage = speed_number;
|
|
guage->Setup(rider, HBGUAGE_SPEEDNUM);
|
|
speed_number = guage;
|
|
|
|
if(!turbo_bar)
|
|
guage = new HoverbikeGuage;
|
|
else
|
|
guage = turbo_bar;
|
|
guage->Setup(rider, HBGUAGE_TURBOBAR);
|
|
turbo_bar = guage;
|
|
|
|
if(!health_bar)
|
|
guage = new HoverbikeGuage;
|
|
else
|
|
guage = health_bar;
|
|
guage->Setup(rider, HBGUAGE_HEALTHBAR);
|
|
health_bar = guage;
|
|
|
|
if(!ammo_number)
|
|
guage = new HoverbikeGuage;
|
|
else
|
|
guage = ammo_number;
|
|
guage->Setup(rider, HBGUAGE_AMMONUM);
|
|
ammo_number = guage;
|
|
|
|
if(!weapon_icon)
|
|
guage = new HoverbikeGuage;
|
|
else
|
|
guage = weapon_icon;
|
|
guage->Setup(rider, HBGUAGE_WEAPONICON);
|
|
weapon_icon = guage;
|
|
}
|
|
|
|
void Hoverbike::KillGuages(void)
|
|
{
|
|
if(speed_bar)
|
|
speed_bar->ProcessEvent(EV_Remove);
|
|
if(speed_number)
|
|
speed_number->ProcessEvent(EV_Remove);
|
|
if(turbo_bar)
|
|
turbo_bar->ProcessEvent(EV_Remove);
|
|
if(health_bar)
|
|
health_bar->ProcessEvent(EV_Remove);
|
|
if(ammo_number)
|
|
ammo_number->ProcessEvent(EV_Remove);
|
|
if(weapon_icon)
|
|
weapon_icon->ProcessEvent(EV_Remove);
|
|
}
|
|
|
|
void Hoverbike::GuagesViewerOn(void)
|
|
{
|
|
if(speed_bar)
|
|
{
|
|
speed_bar->edict->s.renderfx |= RF_VIEWERMODEL;
|
|
}
|
|
|
|
if(speed_number)
|
|
speed_number->edict->s.renderfx |= RF_VIEWERMODEL;
|
|
|
|
if(turbo_bar)
|
|
{
|
|
turbo_bar->edict->s.renderfx |= RF_VIEWERMODEL;
|
|
}
|
|
|
|
if(health_bar)
|
|
{
|
|
health_bar->edict->s.renderfx |= RF_VIEWERMODEL;
|
|
}
|
|
|
|
if(ammo_number)
|
|
ammo_number->edict->s.renderfx |= RF_VIEWERMODEL;
|
|
|
|
if(weapon_icon)
|
|
weapon_icon->edict->s.renderfx |= RF_VIEWERMODEL;
|
|
}
|
|
|
|
void Hoverbike::GuagesViewerOff(void)
|
|
{
|
|
if(speed_bar)
|
|
speed_bar->edict->s.renderfx &= ~RF_VIEWERMODEL;
|
|
if(speed_number)
|
|
speed_number->edict->s.renderfx &= ~RF_VIEWERMODEL;
|
|
if(turbo_bar)
|
|
turbo_bar->edict->s.renderfx &= ~RF_VIEWERMODEL;
|
|
if(health_bar)
|
|
health_bar->edict->s.renderfx &= ~RF_VIEWERMODEL;
|
|
if(ammo_number)
|
|
ammo_number->edict->s.renderfx &= ~RF_VIEWERMODEL;
|
|
if(weapon_icon)
|
|
weapon_icon->edict->s.renderfx &= ~RF_VIEWERMODEL;
|
|
}
|
|
|
|
|
|
void Hoverbike::UpdateGuages(void)
|
|
{
|
|
if(speed_bar)
|
|
speed_bar->SetValue(forward_speed);
|
|
if(speed_number)
|
|
speed_number->SetValue(forward_speed);
|
|
if(turbo_bar)
|
|
turbo_bar->SetValue(turbo);
|
|
if(health_bar)
|
|
health_bar->SetValue(health);
|
|
if(ammo_number)
|
|
{
|
|
if(weaponmode == HWMODE_ROCKETS)
|
|
ammo_number->SetValue(rockets);
|
|
else if(weaponmode == HWMODE_CHAINGUN)
|
|
ammo_number->SetValue(bullets);
|
|
else
|
|
ammo_number->SetValue(mines);
|
|
}
|
|
if(weapon_icon)
|
|
weapon_icon->SetValue(weaponmode);
|
|
}
|
|
|
|
//==============================================================
|
|
// script commands stuff
|
|
|
|
void Hoverbike::ControlledEvent(Event *ev)
|
|
{
|
|
controlled = true;
|
|
}
|
|
|
|
void Hoverbike::UncontrolledEvent(Event *ev)
|
|
{
|
|
controlled = false;
|
|
}
|
|
|
|
void Hoverbike::ReleaseRider(Event *ev)
|
|
{
|
|
if(rider)
|
|
BikeGetOff();
|
|
}
|
|
|
|
void Hoverbike::ControlsEvent(Event *ev)
|
|
{
|
|
str command;
|
|
int i;
|
|
|
|
for(i = 1; i <= ev->NumArgs(); i++)
|
|
{
|
|
command = ev->GetString(i);
|
|
|
|
if(command == "+accel")
|
|
{
|
|
forwardmove = 200;
|
|
}
|
|
else if(command == "-accel")
|
|
{
|
|
forwardmove = 0;
|
|
}
|
|
else if(command == "+turbo")
|
|
{
|
|
scriptturboing = true;
|
|
}
|
|
else if(command == "-turbo")
|
|
{
|
|
scriptturboing = false;
|
|
}
|
|
else if(command == "+brake")
|
|
{
|
|
forwardmove = -200;
|
|
}
|
|
else if(command == "-brake")
|
|
{
|
|
forwardmove = 0;
|
|
}
|
|
else if(command == "+left")
|
|
{
|
|
sidemove = -200;
|
|
}
|
|
else if(command == "-left")
|
|
{
|
|
sidemove = 0;
|
|
}
|
|
else if(command == "+right")
|
|
{
|
|
sidemove = 200;
|
|
}
|
|
else if(command == "-right")
|
|
{
|
|
sidemove = 0;
|
|
}
|
|
else if(command == "+jump")
|
|
{
|
|
upmove = 200;
|
|
}
|
|
else if(command == "-jump")
|
|
{
|
|
upmove = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Hoverbike::YawSpeedEvent(Event *ev)
|
|
{
|
|
scriptturnspeed = ev->GetFloat(1);
|
|
}
|
|
|
|
void Hoverbike::TargetYawEvent(Event *ev)
|
|
{
|
|
scripttargetyaw = ev->GetFloat(1);
|
|
}
|
|
|
|
//==============================================================
|
|
// Hoverbike Viewmodel Guages
|
|
//==============================================================
|
|
|
|
CLASS_DECLARATION(Entity, HoverbikeGuage, NULL);
|
|
|
|
ResponseDef HoverbikeGuage::Responses[] =
|
|
{
|
|
{NULL, NULL}
|
|
};
|
|
|
|
void HoverbikeGuage::Setup(Entity *owner, int type)
|
|
{
|
|
int groupindex;
|
|
int tri_num;
|
|
Vector orient;
|
|
str guagemodel;
|
|
|
|
// set guage type
|
|
guagetype = type;
|
|
|
|
// NOTE: gunmodelindex is used to specify which secondary
|
|
// view model it should be. Can be from 1 to 8
|
|
|
|
switch(guagetype)
|
|
{
|
|
case HBGUAGE_SPEEDBAR:
|
|
guagemodel = "hb_speedbar.def";
|
|
edict->s.gunmodelindex = 1;
|
|
break;
|
|
case HBGUAGE_SPEEDNUM:
|
|
guagemodel = "hb_speednum.def";
|
|
edict->s.gunmodelindex = 2;
|
|
break;
|
|
case HBGUAGE_TURBOBAR:
|
|
guagemodel = "hb_turbo.def";
|
|
edict->s.gunmodelindex = 3;
|
|
break;
|
|
case HBGUAGE_HEALTHBAR:
|
|
guagemodel = "hb_health.def";
|
|
edict->s.gunmodelindex = 4;
|
|
break;
|
|
case HBGUAGE_AMMONUM:
|
|
guagemodel = "hb_ammonum.def";
|
|
edict->s.gunmodelindex = 5;
|
|
break;
|
|
case HBGUAGE_WEAPONICON:
|
|
guagemodel = "hb_weap.def";
|
|
edict->s.gunmodelindex = 6;
|
|
break;
|
|
default:
|
|
gi.dprintf("HoverbikeGuage::Setup : bad guage type\n");
|
|
PostEvent(EV_Remove, 0);
|
|
return;
|
|
break;
|
|
}
|
|
|
|
setSolidType(SOLID_NOT);
|
|
setMoveType(MOVETYPE_NONE);
|
|
|
|
// uses the built in fake bone that works like Quake2's modelindexes did.
|
|
gi.GetBoneInfo(owner->edict->s.modelindex, "origin", &groupindex, &tri_num, orient.vec3());
|
|
attach(owner->entnum, groupindex, tri_num, orient);
|
|
setOrigin(vec_zero);
|
|
setModel(guagemodel);
|
|
showModel();
|
|
|
|
StopAnimating(); // make sure the thing doesn't animate
|
|
|
|
edict->s.effects |= EF_VIEWMODEL; // specify it as a secondary view model
|
|
|
|
// only send this to it's owner
|
|
edict->owner = owner->edict;
|
|
edict->svflags |= SVF_ONLYPARENT;
|
|
}
|
|
|
|
void HoverbikeGuage::SetValue(int value)
|
|
{
|
|
switch(guagetype)
|
|
{
|
|
case HBGUAGE_SPEEDBAR:
|
|
case HBGUAGE_SPEEDNUM:
|
|
value *= 0.2;
|
|
if(value < 0)
|
|
value = 0;
|
|
if(value > 250)
|
|
value = 250;
|
|
if(edict->s.frame != value)
|
|
edict->s.frame = value;
|
|
break;
|
|
case HBGUAGE_TURBOBAR:
|
|
if(edict->s.frame != value)
|
|
edict->s.frame = value;
|
|
break;
|
|
case HBGUAGE_HEALTHBAR:
|
|
if(value < 0)
|
|
value = 0;
|
|
else if(value > HOVER_MAX_HEALTH)
|
|
value = HOVER_MAX_HEALTH;
|
|
value *= (50.0/HOVER_MAX_HEALTH);
|
|
if(edict->s.frame != value)
|
|
edict->s.frame = value;
|
|
break;
|
|
case HBGUAGE_AMMONUM:
|
|
if(edict->s.frame != value)
|
|
edict->s.frame = value;
|
|
break;
|
|
case HBGUAGE_WEAPONICON:
|
|
if(edict->s.frame != value)
|
|
edict->s.frame = value;
|
|
break;
|
|
}
|
|
} |