1 /*
2 ===========================================================================
3 Copyright (C) 2000 - 2013, Raven Software, Inc.
4 Copyright (C) 2001 - 2013, Activision, Inc.
5 Copyright (C) 2013 - 2015, OpenJK contributors
6 
7 This file is part of the OpenJK source code.
8 
9 OpenJK is free software; you can redistribute it and/or modify it
10 under the terms of the GNU General Public License version 2 as
11 published by the Free Software Foundation.
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.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, see <http://www.gnu.org/licenses/>.
20 ===========================================================================
21 */
22 
23 #include "g_local.h"
24 #include "g_functions.h"
25 #include "b_local.h"
26 
27 extern void G_MoverTouchPushTriggers( gentity_t *ent, vec3_t oldOrg );
28 void G_StopObjectMoving( gentity_t *object );
29 
30 /*
31 ================
32 G_BounceMissile
33 
34 ================
35 */
G_BounceObject(gentity_t * ent,trace_t * trace)36 void G_BounceObject( gentity_t *ent, trace_t *trace )
37 {
38 	vec3_t	velocity;
39 	float	dot;
40 	int		hitTime;
41 
42 	// reflect the velocity on the trace plane
43 	hitTime = level.previousTime + ( level.time - level.previousTime ) * trace->fraction;
44 	EvaluateTrajectoryDelta( &ent->s.pos, hitTime, velocity );
45 	dot = DotProduct( velocity, trace->plane.normal );
46 	float bounceFactor = 60/ent->mass;
47 	if ( bounceFactor > 1.0f )
48 	{
49 		bounceFactor = 1.0f;
50 	}
51 	VectorMA( velocity, -2*dot*bounceFactor, trace->plane.normal, ent->s.pos.trDelta );
52 
53 	//FIXME: customized or material-based impact/bounce sounds
54 	if ( ent->s.eFlags & EF_BOUNCE_HALF )
55 	{
56 		VectorScale( ent->s.pos.trDelta, 0.5, ent->s.pos.trDelta );
57 
58 		// check for stop
59 		if ( ((trace->plane.normal[2] > 0.7&&g_gravity->value>0) || (trace->plane.normal[2]<-0.7&&g_gravity->value<0)) && ((ent->s.pos.trDelta[2]<40&&g_gravity->value>0)||(ent->s.pos.trDelta[2]>-40&&g_gravity->value<0)) ) //this can happen even on very slightly sloped walls, so changed it from > 0 to > 0.7
60 		{
61 			//G_SetOrigin( ent, trace->endpos );
62 			//ent->nextthink = level.time + 500;
63 			ent->s.apos.trType = TR_STATIONARY;
64 			VectorCopy( ent->currentAngles, ent->s.apos.trBase );
65 			VectorCopy( trace->endpos, ent->currentOrigin );
66 			VectorCopy( trace->endpos, ent->s.pos.trBase );
67 			ent->s.pos.trTime = level.time;
68 			return;
69 		}
70 	}
71 
72 	// NEW--It would seem that we want to set our trBase to the trace endpos
73 	//	and set the trTime to the actual time of impact....
74 	//	FIXME: Should we still consider adding the normal though??
75 	VectorCopy( trace->endpos, ent->currentOrigin );
76 	ent->s.pos.trTime = hitTime;
77 
78 	VectorCopy( ent->currentOrigin, ent->s.pos.trBase );
79 	VectorCopy( trace->plane.normal, ent->pos1 );//???
80 }
81 
82 /*
83 ================
84 G_RunObject
85 
86   TODO:  When transition to 0 grav, push away from surface you were resting on
87   TODO:  When free-floating in air, apply some friction to your trDelta (based on mass?)
88 ================
89 */
90 extern void DoImpact( gentity_t *self, gentity_t *other, qboolean damageSelf, trace_t *trace );
G_RunObject(gentity_t * ent)91 void G_RunObject( gentity_t *ent )
92 {
93 	vec3_t		origin, oldOrg;
94 	trace_t		tr;
95 	gentity_t	*traceEnt = NULL;
96 
97 	//FIXME: floaters need to stop floating up after a while, even if gravity stays negative?
98 	if ( ent->s.pos.trType == TR_STATIONARY )//g_gravity->value <= 0 &&
99 	{
100 		ent->s.pos.trType = TR_GRAVITY;
101 		VectorCopy( ent->currentOrigin, ent->s.pos.trBase );
102 		ent->s.pos.trTime = level.previousTime;//?necc?
103 		if ( !g_gravity->value )
104 		{
105 			ent->s.pos.trDelta[2] += 100;
106 		}
107 	}
108 
109 	ent->nextthink = level.time + FRAMETIME;
110 
111 	VectorCopy( ent->currentOrigin, oldOrg );
112 	// get current position
113 	EvaluateTrajectory( &ent->s.pos, level.time, origin );
114 	//Get current angles?
115 	EvaluateTrajectory( &ent->s.apos, level.time, ent->currentAngles );
116 
117 	if ( VectorCompare( ent->currentOrigin, origin ) )
118 	{//error - didn't move at all!
119 		return;
120 	}
121 	// trace a line from the previous position to the current position,
122 	// ignoring interactions with the missile owner
123 	gi.trace( &tr, ent->currentOrigin, ent->mins, ent->maxs, origin,
124 		ent->owner ? ent->owner->s.number : ent->s.number, ent->clipmask, (EG2_Collision)0, 0 );
125 
126 	if ( !tr.startsolid && !tr.allsolid && tr.fraction )
127 	{
128 		VectorCopy( tr.endpos, ent->currentOrigin );
129 		gi.linkentity( ent );
130 	}
131 	else
132 	//if ( tr.startsolid )
133 	{
134 		tr.fraction = 0;
135 	}
136 
137 	G_MoverTouchPushTriggers( ent, oldOrg );
138 	/*
139 	if ( !(ent->s.eFlags & EF_TELEPORT_BIT) && !(ent->svFlags & SVF_NO_TELEPORT) )
140 	{
141 		G_MoverTouchTeleportTriggers( ent, oldOrg );
142 		if ( ent->s.eFlags & EF_TELEPORT_BIT )
143 		{//was teleported
144 			return;
145 		}
146 	}
147 	else
148 	{
149 		ent->s.eFlags &= ~EF_TELEPORT_BIT;
150 	}
151 	*/
152 
153 	if ( tr.fraction == 1 )
154 	{
155 		if ( g_gravity->value <= 0 )
156 		{
157 			if ( ent->s.apos.trType == TR_STATIONARY )
158 			{
159 				VectorCopy( ent->currentAngles, ent->s.apos.trBase );
160 				ent->s.apos.trType = TR_LINEAR;
161 				ent->s.apos.trDelta[1] = Q_flrand( -300, 300 );
162 				ent->s.apos.trDelta[0] = Q_flrand( -10, 10 );
163 				ent->s.apos.trDelta[2] = Q_flrand( -10, 10 );
164 				ent->s.apos.trTime = level.time;
165 			}
166 		}
167 		//friction in zero-G
168 		if ( !g_gravity->value )
169 		{
170 			float friction = 0.975f;
171 			/*friction -= ent->mass/1000.0f;
172 			if ( friction < 0.1 )
173 			{
174 				friction = 0.1f;
175 			}
176 			*/
177 			VectorScale( ent->s.pos.trDelta, friction, ent->s.pos.trDelta );
178 			VectorCopy( ent->currentOrigin, ent->s.pos.trBase );
179 			ent->s.pos.trTime = level.time;
180 		}
181 		return;
182 	}
183 
184 	//hit something
185 
186 	//Do impact damage
187 	traceEnt = &g_entities[tr.entityNum];
188 	if ( tr.fraction || (traceEnt && traceEnt->takedamage) )
189 	{
190 		if ( !VectorCompare( ent->currentOrigin, oldOrg ) )
191 		{//moved and impacted
192 			if ( (traceEnt && traceEnt->takedamage) )
193 			{//hurt someone
194 				vec3_t fxDir;
195 				VectorNormalize2( ent->s.pos.trDelta, fxDir );
196 				VectorScale( fxDir, -1, fxDir );
197 				G_PlayEffect( G_EffectIndex( "melee/kick_impact" ), tr.endpos, fxDir );
198 				//G_Sound( ent, G_SoundIndex( va( "sound/weapons/melee/punch%d", Q_irand( 1, 4 ) ) ) );
199 			}
200 			else
201 			{
202 				G_PlayEffect( G_EffectIndex( "melee/kick_impact_silent" ), tr.endpos, tr.plane.normal );
203 			}
204 			if ( ent->mass > 100 )
205 			{
206 				G_Sound( ent, G_SoundIndex( "sound/movers/objects/objectHitHeavy.wav" ) );
207 			}
208 			else
209 			{
210 				G_Sound( ent, G_SoundIndex( "sound/movers/objects/objectHit.wav" ) );
211 			}
212 		}
213 		DoImpact( ent, traceEnt, (qboolean)!(tr.surfaceFlags&SURF_NODAMAGE), &tr );
214 	}
215 
216 	if ( !ent || (ent->takedamage&&ent->health <= 0) )
217 	{//been destroyed by impact
218 		//chunks?
219 		G_Sound( ent, G_SoundIndex( "sound/movers/objects/objectBreak.wav" ) );
220 		return;
221 	}
222 
223 	//do impact physics
224 	if ( ent->s.pos.trType == TR_GRAVITY )//tr.fraction < 1.0 &&
225 	{//FIXME: only do this if no trDelta
226 		if ( g_gravity->value <= 0 || tr.plane.normal[2] < 0.7 )
227 		{
228 			if ( ent->s.eFlags&(EF_BOUNCE|EF_BOUNCE_HALF) )
229 			{
230 				if ( tr.fraction <= 0.0f )
231 				{
232 					VectorCopy( tr.endpos, ent->currentOrigin );
233 					VectorCopy( tr.endpos, ent->s.pos.trBase );
234 					VectorClear( ent->s.pos.trDelta );
235 					ent->s.pos.trTime = level.time;
236 				}
237 				else
238 				{
239 					G_BounceObject( ent, &tr );
240 				}
241 			}
242 			else
243 			{//slide down?
244 				//FIXME: slide off the slope
245 			}
246 		}
247 		else
248 		{
249 			ent->s.apos.trType = TR_STATIONARY;
250 			pitch_roll_for_slope( ent, tr.plane.normal );
251 			//ent->currentAngles[0] = 0;//FIXME: match to slope
252 			//ent->currentAngles[2] = 0;//FIXME: match to slope
253 			VectorCopy( ent->currentAngles, ent->s.apos.trBase );
254 			//okay, we hit the floor, might as well stop or prediction will
255 			//make us go through the floor!
256 			//FIXME: this means we can't fall if something is pulled out from under us...
257 			G_StopObjectMoving( ent );
258 		}
259 	}
260 	else
261 	{
262 		ent->s.apos.trType = TR_STATIONARY;
263 		pitch_roll_for_slope( ent, tr.plane.normal );
264 		//ent->currentAngles[0] = 0;//FIXME: match to slope
265 		//ent->currentAngles[2] = 0;//FIXME: match to slope
266 		VectorCopy( ent->currentAngles, ent->s.apos.trBase );
267 	}
268 
269 	//call touch func
270 	GEntity_TouchFunc( ent, &g_entities[tr.entityNum], &tr );
271 }
272 
G_StopObjectMoving(gentity_t * object)273 void G_StopObjectMoving( gentity_t *object )
274 {
275 	object->s.pos.trType = TR_STATIONARY;
276 	VectorCopy( object->currentOrigin, object->s.origin );
277 	VectorCopy( object->currentOrigin, object->s.pos.trBase );
278 	VectorClear( object->s.pos.trDelta );
279 
280 	/*
281 	//Stop spinning
282 	VectorClear( self->s.apos.trDelta );
283 	vectoangles(trace->plane.normal, self->s.angles);
284 	VectorCopy(self->s.angles, self->currentAngles );
285 	VectorCopy(self->s.angles, self->s.apos.trBase);
286 	*/
287 }
288 
G_StartObjectMoving(gentity_t * object,vec3_t dir,float speed,trType_t trType)289 void G_StartObjectMoving( gentity_t *object, vec3_t dir, float speed, trType_t trType )
290 {
291 	VectorNormalize (dir);
292 
293 	//object->s.eType = ET_GENERAL;
294 	object->s.pos.trType = trType;
295 	VectorCopy( object->currentOrigin, object->s.pos.trBase );
296 	VectorScale(dir, speed, object->s.pos.trDelta );
297 	object->s.pos.trTime = level.time;
298 
299 	/*
300 	//FIXME: incorporate spin?
301 	vectoangles(dir, object->s.angles);
302 	VectorCopy(object->s.angles, object->s.apos.trBase);
303 	VectorSet(object->s.apos.trDelta, 300, 0, 0 );
304 	object->s.apos.trTime = level.time;
305 	*/
306 
307 	//FIXME: make these objects go through G_RunObject automatically, like missiles do
308 	if ( object->e_ThinkFunc == thinkF_NULL )
309 	{
310 		object->nextthink = level.time + FRAMETIME;
311 		object->e_ThinkFunc = thinkF_G_RunObject;
312 	}
313 	else
314 	{//You're responsible for calling RunObject
315 	}
316 }
317 
G_CreateObject(gentity_t * owner,vec3_t origin,vec3_t angles,int modelIndex,int frame,trType_t trType,int effectID=0)318 gentity_t *G_CreateObject ( gentity_t *owner, vec3_t origin, vec3_t angles, int modelIndex, int frame, trType_t trType, int effectID = 0  )
319 {
320 	gentity_t	*object;
321 
322 	object = G_Spawn();
323 
324 	if ( object == NULL )
325 	{
326 		return NULL;
327 	}
328 
329 	object->classname = "object";//?
330 	object->nextthink = level.time + FRAMETIME;
331 	object->e_ThinkFunc = thinkF_G_RunObject;
332 	object->s.eType = ET_GENERAL;
333 	object->s.eFlags |= EF_AUTO_SIZE;//CG_Ents will create the mins & max itself based on model bounds
334 	object->s.modelindex = modelIndex;
335 	//FIXME: allow to set a targetname/script_targetname and animation info?
336 	object->s.frame = object->startFrame = object->endFrame = frame;
337 	object->owner = owner;
338 	//object->damage = 100;
339 	//object->splashDamage = 200;
340 	//object->splashRadius = 200;
341 	//object->methodOfDeath = MOD_EXPLOSIVE;
342 	//object->splashMethodOfDeath = MOD_EXPLOSIVE_SPLASH;
343 	object->clipmask = MASK_SOLID;//?
344 	//object->e_TouchFunc = touchF_charge_stick;
345 
346 	// The effect to play.
347 	object->count = effectID;
348 
349 	//Give it SOME size for now
350 	VectorSet( object->mins, -4, -4, -4 );
351 	VectorSet( object->maxs, 4, 4, 4 );
352 
353 	//Origin
354 	G_SetOrigin( object, origin );
355 	object->s.pos.trType = trType;
356 	VectorCopy( origin, object->s.pos.trBase );
357 	//Velocity
358 	VectorClear( object->s.pos.trDelta );
359 	object->s.pos.trTime = level.time;
360 	//VectorScale( dir, 300, object->s.pos.trDelta );
361 	//object->s.pos.trTime = level.time;
362 
363 	//Angles
364 	VectorCopy( angles, object->s.angles );
365 	VectorCopy( object->s.angles, object->s.apos.trBase );
366 	//Angular Velocity
367 	VectorClear( object->s.apos.trDelta );
368 	object->s.apos.trTime = level.time;
369 	//VectorSet( object->s.apos.trDelta, 300, 0, 0 );
370 	//object->s.apos.trTime = level.time;
371 
372 	gi.linkentity( object );
373 
374 	return object;
375 }
376