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