/* ================================================================ 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; } }