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