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