1 /*
2 Copyright (C) 20?? COR Entertainment, LLC.
3
4 Copyright (C) 1998 by "Attila." Based on the email address provided, Attila is
5 likely the pseudonym of Jens Bohnwagner, but this isn't certain. Original
6 code: http://www.quakewiki.net/archives/qdevels/quake2/1_2_98.html
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16
17 See the GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License along
20 with this program; if not, write to the Free Software Foundation, Inc.,
21 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include "g_local.h"
29
30
31 /*we get silly velocity-effects when we are on ground and try to
32 accelerate, so lift us a little bit if possible*/
Jet_AvoidGround(edict_t * ent)33 qboolean Jet_AvoidGround( edict_t *ent )
34 {
35 vec3_t new_origin;
36 trace_t trace;
37 qboolean success;
38
39 /*Check if there is enough room above us before we change origin[2]*/
40 new_origin[0] = ent->s.origin[0];
41 new_origin[1] = ent->s.origin[1];
42 new_origin[2] = ent->s.origin[2] + 0.5;
43 trace = gi.trace( ent->s.origin, ent->mins, ent->maxs, new_origin, ent, MASK_MONSTERSOLID );
44
45 if ( (success=(trace.plane.normal[2]) == 0 ) ) /*no ceiling?*/
46 ent->s.origin[2] += 0.5; /*then make sure off ground*/
47
48 return success;
49 }
50
51
52 /*This function returns true if the jet is activated
53 (surprise, surprise)*/
Jet_Active(edict_t * ent)54 qboolean Jet_Active( edict_t *ent )
55 {
56 return ( ent->client->Jet_framenum >= level.framenum );
57 }
58
59
60 /*If a player dies with activated jetpack this function will be called
61 and produces a little explosion*/
Jet_Explosion(edict_t * ent)62 void Jet_Explosion( edict_t *ent )
63 {
64
65 gi.WriteByte( svc_temp_entity );
66 gi.WriteByte( TE_EXPLOSION1 ); /*TE_EXPLOSION2 is possible too*/
67 gi.WritePosition( ent->s.origin );
68 gi.multicast( ent->s.origin, MULTICAST_PVS );
69
70 }
71
72
73 /*The lifting effect is done through changing the origin, it
74 gives the best results. Of course its a little dangerous because
75 if we dont take care, we can move into solid*/
Jet_ApplyLifting(edict_t * ent)76 void Jet_ApplyLifting( edict_t *ent )
77 {
78 float delta;
79 vec3_t new_origin;
80 trace_t trace;
81 int time = 24; /*must be >0, time/10 = time in sec for a
82 complete cycle (up/down)*/
83 float amplitude = 2.0;
84
85 /*calculate the z-distance to lift in this step*/
86 delta = sin( (float)((level.framenum%time)*(360/time))/180*M_PI ) * amplitude;
87 delta = (float)((int)(delta*8))/8; /*round to multiples of 0.125*/
88
89 VectorCopy( ent->s.origin, new_origin );
90 new_origin[2] += delta;
91
92 if( VectorLength(ent->velocity) == 0 )
93 {
94 /*i dont know the reason yet, but there is some floating so we
95 have to compensate that here (only if there is no velocity left)*/
96 new_origin[0] -= 0.125;
97 new_origin[1] -= 0.125;
98 new_origin[2] -= 0.125;
99 }
100
101 /*before we change origin, its important to check that we dont go
102 into solid*/
103 trace = gi.trace( ent->s.origin, ent->mins, ent->maxs, new_origin, ent, MASK_MONSTERSOLID );
104 if ( trace.plane.normal[2] == 0 )
105 VectorCopy( new_origin, ent->s.origin );
106 }
107
108 /*if the angle of the velocity vector is different to the viewing
109 angle (flying curves or stepping left/right) we get a dotproduct
110 which is here used for rolling*/
Jet_ApplyRolling(edict_t * ent,vec3_t right)111 void Jet_ApplyRolling( edict_t *ent, vec3_t right )
112 {
113 float roll,
114 value = 0.05,
115 sign = -1; /*set this to +1 if you want to roll contrariwise*/
116
117 roll = DotProduct( ent->velocity, right ) * value * sign;
118 ent->client->kick_angles[ROLL] = roll;
119 }
120
121
122 /*Now for the main movement code. The steering is a lot like in water, that
123 means your viewing direction is your moving direction. You have three
124 direction Boosters: the big Main Booster and the smaller up-down and
125 left-right Boosters.
126 There are only 2 adds to the code of the first tutorial: the Jet_next_think
127 and the rolling.
128 The other modifications results in the use of the built-in quake functions,
129 there is no change in moving behavior (reinventing the wheel is a lot of
130 "fun" and a BIG waste of time ;-))*/
Jet_ApplyJet(edict_t * ent,usercmd_t * ucmd)131 void Jet_ApplyJet( edict_t *ent, usercmd_t *ucmd )
132 {
133 float direction;
134 vec3_t acc;
135 vec3_t forward, right;
136 int i;
137 gitem_t *vehicle;
138
139 vehicle = FindItemByClassname("item_hover");
140
141 /*clear gravity so we dont have to compensate it with the Boosters*/
142 if(!(ent->client->pers.inventory[ITEM_INDEX(vehicle)]))
143 ent->client->ps.pmove.gravity = 0;
144 else //make hovercraft fall quicker
145 ent->client->ps.pmove.gravity = sv_gravity->value * 4;
146
147 /*calculate the direction vectors dependent on viewing direction
148 (length of the vectors forward/right is always 1, the coordinates of
149 the vectors are values of how much youre looking in a specific direction
150 [if youre looking up to the top, the x/y values are nearly 0 the
151 z value is nearly 1])*/
152 AngleVectors( ent->client->v_angle, forward, right, NULL );
153
154 /*Run jet only 10 times a second so movement dont depends on fps
155 because ClientThink is called as often as possible
156 (fps<10 still is a problem ?)*/
157 if ( ent->client->Jet_next_think <= level.framenum )
158 {
159 ent->client->Jet_next_think = level.framenum + 1;
160
161 /*clear acceleration-vector*/
162 VectorClear( acc );
163
164 /*if we are moving forward or backward add MainBooster acceleration
165 (60)*/
166 if ( ucmd->forwardmove )
167 {
168 /*are we accelerating backward or forward?*/
169 direction = (ucmd->forwardmove<0) ? -1.0 : 1.0;
170
171 /*add the acceleration for each direction*/
172 if(!(ent->client->pers.inventory[ITEM_INDEX(vehicle)])) {
173 acc[0] += direction * forward[0] * 60;
174 acc[1] += direction * forward[1] * 60;
175 acc[2] += direction * forward[2] * 60;
176 }
177 else {
178 acc[0] += direction * forward[0] * 120;
179 acc[1] += direction * forward[1] * 120;
180 }
181 }
182
183 /*if we sidestep add Left-Right-Booster acceleration (40)*/
184 if ( ucmd->sidemove )
185 {
186 /*are we accelerating left or right*/
187 direction = (ucmd->sidemove<0) ? -1.0 : 1.0;
188
189 /*add only to x and y acceleration*/
190 acc[0] += right[0] * direction * 40;
191 acc[1] += right[1] * direction * 40;
192 }
193
194 /*if we crouch or jump add Up-Down-Booster acceleration (30)*/
195 /*if we are in a hovercraft, we won't do this */
196 // if(!(ent->client->pers.inventory[ITEM_INDEX(vehicle)])) {
197 if ( ucmd->upmove )
198 acc[2] += ucmd->upmove > 0 ? 30 : -30;
199 // }
200
201 /*now apply some friction dependent on velocity (higher velocity results
202 in higher friction), without acceleration this will reduce the velocity
203 to 0 in a few steps*/
204 ent->velocity[0] += -(ent->velocity[0]/6.0);
205 ent->velocity[1] += -(ent->velocity[1]/6.0);
206 ent->velocity[2] += -(ent->velocity[2]/7.0);
207
208 /*then accelerate with the calculated values. If the new acceleration for
209 a direction is smaller than an earlier, the friction will reduce the speed
210 in that direction to the new value in a few steps, so if youre flying
211 curves or around corners youre floating a little bit in the old direction*/
212 VectorAdd( ent->velocity, acc, ent->velocity );
213
214 /*round velocitys (is this necessary?)*/
215 ent->velocity[0] = (float)((int)(ent->velocity[0]*8))/8;
216 ent->velocity[1] = (float)((int)(ent->velocity[1]*8))/8;
217 ent->velocity[2] = (float)((int)(ent->velocity[2]*8))/8;
218
219 /*Bound velocitys so that friction and acceleration dont need to be
220 synced on maxvelocitys*/
221 for ( i=0 ; i<2 ; i++) /*allow z-velocity to be greater*/
222 {
223 if(!(ent->client->pers.inventory[ITEM_INDEX(vehicle)])) {
224 if (ent->velocity[i] > 300)
225 ent->velocity[i] = 300;
226 else if (ent->velocity[i] < -300)
227 ent->velocity[i] = -300;
228 }
229 else {
230 if (ent->velocity[i] > 600)
231 ent->velocity[i] = 600;
232 else if (ent->velocity[i] < -600)
233 ent->velocity[i] = -600;
234 }
235 }
236
237 /*add some gentle up and down when idle (not accelerating)*/
238 if( VectorLength(acc) == 0 )
239 Jet_ApplyLifting( ent );
240
241 }//if ( ent->client->Jet_next_think...
242
243 /*add rolling when we fly curves or boost left/right*/
244 Jet_ApplyRolling( ent, right );
245
246 }
247
Use_Jet(edict_t * ent)248 void Use_Jet ( edict_t *ent)
249 {
250
251 /*jetpack in inventory but no fuel time? must be one of the
252 give all/give jetpack cheats, so put fuel in*/
253 if ( ent->client->Jet_remaining == 0 )
254 ent->client->Jet_remaining = 700;
255
256 if ( Jet_Active(ent) )
257 ent->client->Jet_framenum = 0;
258 else
259 ent->client->Jet_framenum = level.framenum + ent->client->Jet_remaining;
260
261 gi.sound( ent, CHAN_ITEM, gi.soundindex("vehicles/got_in.wav"), 0.8, ATTN_NORM, 0 );
262 }
VehicleThink(edict_t * ent)263 static void VehicleThink(edict_t *ent)
264 {
265 ent->nextthink = level.time + FRAMETIME;
266 }
VehicleSetup(edict_t * ent)267 void VehicleSetup (edict_t *ent)
268 {
269 trace_t tr;
270 vec3_t dest;
271 float *v;
272
273 v = tv(-64,-64,-24);
274 VectorCopy (v, ent->mins);
275 v = tv(64,64,64);
276 VectorCopy (v, ent->maxs);
277
278 if (ent->model)
279 gi.setmodel (ent, ent->model);
280 else
281 gi.setmodel (ent, ent->item->world_model);
282
283 if(!strcmp(ent->classname, "item_bomber"))
284 ent->s.modelindex3 = gi.modelindex("vehicles/bomber/helmet.md2");
285
286 ent->solid = SOLID_TRIGGER;
287 ent->movetype = MOVETYPE_TOSS;
288 ent->touch = Touch_Item;
289
290 v = tv(0,0,-128);
291 VectorAdd (ent->s.origin, v, dest);
292
293 tr = gi.trace (ent->s.origin, ent->mins, ent->maxs, dest, ent, MASK_SOLID);
294 if (tr.startsolid)
295 {
296 gi.dprintf ("VehicleSetup: %s startsolid at %s\n", ent->classname, vtos(ent->s.origin));
297 G_FreeEdict (ent);
298 return;
299 }
300
301 VectorCopy (tr.endpos, ent->s.origin);
302
303 gi.linkentity (ent);
304
305 ent->nextthink = level.time + FRAMETIME;
306 ent->think = VehicleThink;
307 }
308
VehicleDropTouch(edict_t * ent,edict_t * other,cplane_t * plane,csurface_t * surf)309 static void VehicleDropTouch(edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
310 {
311 //owner (who dropped us) can't touch for two secs
312 if (other == ent->owner &&
313 ent->nextthink - level.time > 2)
314 return;
315
316 Touch_Item (ent, other, plane, surf);
317 }
318
VehicleDropThink(edict_t * ent)319 static void VehicleDropThink(edict_t *ent)
320 {
321 //let it hang around for awhile
322 ent->nextthink = level.time + 20;
323 ent->think = G_FreeEdict;
324 }
325
VehicleDeadDrop(edict_t * self)326 void VehicleDeadDrop(edict_t *self)
327 {
328 edict_t *dropped;
329 gitem_t *vehicle;
330
331 dropped = NULL;
332
333 vehicle = FindItemByClassname("item_bomber");
334
335 if (self->client->pers.inventory[ITEM_INDEX(vehicle)]) {
336 dropped = Drop_Item(self, vehicle);
337 self->client->pers.inventory[ITEM_INDEX(vehicle)] = 0;
338 safe_bprintf(PRINT_HIGH, "Bomber is abandoned!\n");
339 }
340
341 if (dropped) {
342 dropped->think = VehicleDropThink;
343 dropped->nextthink = level.time + 5;
344 dropped->touch = VehicleDropTouch;
345 dropped->s.frame = 0;
346 return;
347 }
348 //didn't find a bomber, try a strafer
349 vehicle = FindItemByClassname("item_strafer");
350
351 if (self->client->pers.inventory[ITEM_INDEX(vehicle)]) {
352 dropped = Drop_Item(self, vehicle);
353 self->client->pers.inventory[ITEM_INDEX(vehicle)] = 0;
354 safe_bprintf(PRINT_HIGH, "Strafer is abandoned!\n");
355 }
356
357 if (dropped) {
358 dropped->think = VehicleDropThink;
359 dropped->nextthink = level.time + 5;
360 dropped->touch = VehicleDropTouch;
361 dropped->s.frame = 0;
362 return;
363 }
364 //didn't find a strafer, try a hovercraft
365 vehicle = FindItemByClassname("item_hover");
366
367 if (self->client->pers.inventory[ITEM_INDEX(vehicle)]) {
368 dropped = Drop_Item(self, vehicle);
369 self->client->pers.inventory[ITEM_INDEX(vehicle)] = 0;
370 safe_bprintf(PRINT_HIGH, "Hovercraft is abandoned!\n");
371 }
372
373 if (dropped) {
374 dropped->think = VehicleDropThink;
375 dropped->nextthink = level.time + 5;
376 dropped->touch = VehicleDropTouch;
377 dropped->s.frame = 0;
378 return;
379 }
380
381 }
Reset_player(edict_t * ent)382 void Reset_player(edict_t *ent)
383 {
384 char userinfo[MAX_INFO_STRING];
385 int i, done;
386 char playermodel[MAX_OSPATH] = " ";
387 char modelpath[MAX_OSPATH] = " ";
388 FILE *file;
389 char *s;
390
391 //set everything back
392 if ( instagib->integer || insta_rockets->integer )
393 {
394 ent->client->newweapon = FindItem("Alien Disruptor");
395 }
396 else if( rocket_arena->integer )
397 {
398 ent->client->newweapon = FindItem("Rocket Launcher");
399 }
400 else
401 {
402 ent->client->newweapon = FindItem("blaster");
403 }
404 assert( ent->client->newweapon != NULL );
405
406 memcpy (userinfo, ent->client->pers.userinfo, sizeof(userinfo));
407 s = Info_ValueForKey (userinfo, "skin");
408 ent->client->Jet_framenum = 0;
409
410 i = 0;
411 done = false;
412 strcpy(playermodel, " ");
413 while(!done)
414 {
415 if((s[i] == '/') || (s[i] == '\\'))
416 done = true;
417 playermodel[i] = s[i];
418 if(i > 62)
419 done = true;
420 i++;
421 }
422 playermodel[i-1] = 0;
423
424 ent->s.modelindex = 255;
425
426 sprintf(modelpath, "players/%s/helmet.md2", playermodel);
427 Q2_FindFile (modelpath, &file); //does a helmet exist?
428 if(file) {
429 sprintf(modelpath, "players/%s/helmet.md2", playermodel);
430 ent->s.modelindex3 = gi.modelindex(modelpath);
431 fclose(file);
432 }
433 else
434 ent->s.modelindex3 = 0;
435
436 ent->s.modelindex4 = 0;
437
438 ent->in_vehicle = false;
439 }
Leave_vehicle(edict_t * ent,gitem_t * item)440 void Leave_vehicle(edict_t *ent, gitem_t *item)
441 {
442 Reset_player(ent);
443 ent->client->pers.inventory[ITEM_INDEX(item)] = 0;
444
445 gi.sound( ent, CHAN_ITEM, gi.soundindex("vehicles/got_in.wav"), 0.8, ATTN_NORM, 0 );
446
447 Drop_Item (ent, item);
448
449 safe_bprintf(PRINT_HIGH, "Vehicle has been dropped!\n");
450 }
451
Get_in_vehicle(edict_t * ent,edict_t * other)452 qboolean Get_in_vehicle (edict_t *ent, edict_t *other)
453 {
454
455 gitem_t *vehicle;
456
457 float *v;
458
459 vehicle = NULL;
460
461 if(other->in_vehicle) //already in a vehicle
462 return false;
463
464 vehicle = FindItemByClassname(ent->classname);
465
466 //put him in the vehicle
467 if(!strcmp(ent->classname, "item_bomber")) {
468 other->s.modelindex = gi.modelindex("vehicles/bomber/tris.md2");
469 other->s.modelindex2 = 0;
470 other->s.modelindex3 = gi.modelindex("vehicles/bomber/helmet.md2");
471 other->s.modelindex4 = 0;
472 }
473 else if(!strcmp(ent->classname, "item_hover")) {
474 other->s.modelindex = gi.modelindex("vehicles/hover/tris.md2");
475 other->s.modelindex2 = 0;
476 other->s.modelindex3 = 0;
477 other->s.modelindex4 = 0;
478 }
479 else {
480 other->s.modelindex = gi.modelindex("vehicles/strafer/tris.md2");
481 other->s.modelindex2 = 0;
482 other->s.modelindex3 = 0;
483 other->s.modelindex4 = 0;
484 }
485 other->in_vehicle = true;
486 other->client->Jet_remaining = 500;
487
488 v = tv(-64,-64,-24);
489 VectorCopy (v,other->mins);
490 v = tv(64,64,64);
491 VectorCopy (v, other->maxs);
492 other->s.origin[2] +=24;
493
494 other->client->pers.inventory[ITEM_INDEX(vehicle)] = 1;
495 other->client->newweapon = ent->item;
496
497 if (!(ent->spawnflags & DROPPED_ITEM)) {
498 SetRespawn (ent, 60);
499 }
500
501 Use_Jet(other);
502
503 ent->owner = other;
504
505 return true;
506 }
507