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