sin-2015/hoverbike.cpp

2910 lines
68 KiB
C++
Raw Permalink Normal View History

1999-04-22 00:00:00 +00:00
/*
================================================================
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;
}
}