1 /*
2 * Shotgun Debugger
3 * Copyright 2005 Game Creation Society
4 *
5 * Programmed by Matt Sarnoff
6 * http://www.contrib.andrew.cmu.edu/~msarnoff
7 * http://www.gamecreation.org
8 *
9 * objects.cpp - object (entity, particle, bullet, laser) routines
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 *
25 */
26
27 #include "sdb.h"
28
29 //Level currLevel;
30 ObjectType objType[NUM_OBJ_TYPES];
31 ParticleType partType[NUM_PART_TYPES];
32 BulletType bltType[NUM_BLT_TYPES];
33 LaserType lasType[NUM_LASER_TYPES];
34 WeaponType wpnType[NUM_WPN_TYPES];
35 int oID;
36
37 // Update an animation
update()38 void Animation::update()
39 {
40 if (numFrames > 1 && !done)
41 {
42 if (frame == stopAtFrame)
43 { setDirection(0); stopAtFrame = -1; }
44
45 animTime += timer.dT();
46 if (animTime >= delay)
47 {
48 frame += direction;
49 animTime = 0;
50
51 if (direction > 0)
52 {
53 if (frame >= numFrames)
54 { frame = 0; numLoops++; }
55 }
56 else if (direction < 0)
57 {
58 if (frame < 0)
59 { frame = numFrames-1; numLoops++; }
60 }
61
62 if (loop > INF)
63 if (numLoops >= loop)
64 done = true;
65 }
66 }
67 }
68
update()69 void ModelAnimation::update()
70 {
71 if (model < 0 || models[model].pObject.size() <= 1)
72 return;
73
74 tAnimationInfo *pAnim = &(models[model].pAnimations[
75 (currAnim == ANIM_BASE) ? baseAnim : transientAnim]);
76
77 nextFrame = (currFrame + 1) % pAnim->endFrame;
78
79 // Loop to the start if we're on the base animation. If not,
80 // return to the base animation.
81 if (nextFrame == 0)
82 {
83 if (currAnim == ANIM_BASE)
84 {
85 nextFrame = pAnim->startFrame;
86 //nextFrame = (currFrame+1) % pAnim->endFrame;
87 }
88 else
89 {
90 currAnim = ANIM_BASE;
91 transientAnim = -1;
92 tAnimationInfo *pAnim = &(models[model].pAnimations[baseAnim]);
93 currFrame = pAnim->startFrame;
94 nextFrame = (currFrame + 1) % pAnim->endFrame;
95 }
96 }
97
98 animTime += timer.dT();
99
100 if (animTime >= 1.0/speed)
101 {
102 currFrame = nextFrame;
103 animTime = 0;
104 }
105 }
106
setAnimation(int val,bool anim)107 void ModelAnimation::setAnimation(int val, bool anim)
108 {
109 if (model >= 0 && models[model].pObject.size() > 1)
110 {
111 //printf("%d\n", val);
112 if (anim == ANIM_BASE && baseAnim != val)
113 {
114 baseAnim = val;
115 if (currAnim == ANIM_BASE)
116 {
117 tAnimationInfo *pAnim = &(models[model].pAnimations[baseAnim]);
118 currFrame = pAnim->startFrame;
119
120 nextFrame = (currFrame + 1) % pAnim->endFrame;
121 }
122 }
123 // If we specify a transient ("one-shot") animation, we switch
124 // to it automatically
125 else if (anim == ANIM_TRANSIENT && transientAnim != val)
126 {
127 transientAnim = val;
128 currAnim = ANIM_TRANSIENT;
129 tAnimationInfo *pAnim = &(models[model].pAnimations[transientAnim]);
130 currFrame = pAnim->startFrame;
131 nextFrame = (currFrame + 1) % pAnim->endFrame;
132 }
133 }
134 }
135
draw()136 void ModelAnimation::draw()
137 {
138 if (model >= 0)
139 {
140 glBindTexture(GL_TEXTURE_2D, textures[models[model].texture]);
141 models[model].draw((currAnim == ANIM_BASE) ? baseAnim : transientAnim, currFrame, nextFrame, animTime, speed);
142 }
143 }
144
145 // Set properties of a force.
146 // If mode is FORCE_RECT, a and b are the vector's x and y components
147 // If mode is FORCE_POLAR, a and b are the vector's magnitude and angle
set(int obj,float dur,float a,float b,bool mode)148 void Force::set(int obj, float dur, float a, float b, bool mode)
149 {
150 target = obj;
151 duration = dur;
152 (mode == FORCE_RECT) ? force.set(a,b) : force.set(a*cos(b), a*sin(b));
153 reset();
154 }
155
156 // Applies a force to the target object
apply()157 void Force::apply()
158 {
159 if (active)
160 {
161 forceTime += timer.dT();
162 currLevelObjs[target]->changeVel(force);
163 if (forceTime >= duration) destroyMe = true;
164 }
165 }
166
reset()167 void Shockwave::reset()
168 {
169 shockTime = 0;
170 active = false;
171 destroyMe = false;
172 }
173
set(float x,float y,float ir,float irs,float otr,float ors,float dur,float frc,float hlth,bool ag)174 void Shockwave::set(float x, float y, float ir, float irs, float otr, float ors, float dur, float frc, float hlth, bool ag)
175 {
176 pos.set(x, y);
177 innerRad = ir;
178 innerRadSpeed = irs;
179 outerRad = otr;
180 outerRadSpeed = ors;
181 duration = dur;
182 force = frc;
183 health = hlth;
184 affectGladiators = ag;
185 active = false;
186 destroyMe = false;
187 shockTime = duration;
188 }
189
update()190 void Shockwave::update()
191 {
192 if (active)
193 {
194 innerRad += innerRadSpeed * timer.dT();
195 outerRad += outerRadSpeed * timer.dT();
196
197 shockTime -= timer.dT();
198
199 if (shockTime <= 0.0)
200 { active = false; destroyMe = true; }
201 }
202 }
203
detect(Vector2D p,float * resultForce,float * resultHealth)204 bool Shockwave::detect(Vector2D p, float *resultForce, float *resultHealth)
205 {
206 if (active)
207 {
208 float distSq = distSquared(pos, p);
209 if (distSq >= innerRad*innerRad && distSq <= outerRad*outerRad)
210 {
211 float factor = 1.0-(distSq/(outerRad*outerRad));
212 *resultHealth = (health == SHOCK_ESG) ? SHOCK_ESG : factor*health;
213
214 *resultForce = factor*force;
215 return true;
216 }
217 }
218 return false;
219 }
220
draw()221 void Shockwave::draw()
222 {
223 if (active)
224 {
225 glDisable(GL_TEXTURE_2D);
226 glPushMatrix();
227 glColor3f(1.0, 1.0, 1.0);
228 glTranslatef(pos.c[X], pos.c[Y], 0);
229 glPointSize(4);
230 for (int i = 0; i < 360; i+=15)
231 {
232 glRotatef(15.0, 0.0, 0.0, i);
233 glBegin(GL_POINTS);
234 glVertex3f(innerRad, 0.0,0.5);
235 glVertex3f(outerRad, 0.0,0.5);
236 glEnd();
237 }
238 glPopMatrix();
239 }
240 }
241
242 // Set the properties of an object type
set(float im,float iw,float ih,float ir,float imv,float ijv,float imtv,float ia,float ita,float hlth,int dth,bool cd,bool grav,bool shad)243 void ObjectType::set(float im, float iw, float ih, float ir, float imv, float ijv,
244 float imtv, float ia, float ita, float hlth, int dth, bool cd, bool grav, bool shad)
245 {
246 spriteSize.set(iw, ih);
247 boundRadius = ir;
248 mass = im; maxVel = imv; jumpVel = ijv;
249 maxTurnvel = imtv; accel = ia; turnAccel = ita;
250 maxHealth = hlth;
251 deathType = dth;
252 collide = cd; gravAffect = grav; hasShadow = shad;
253 model1 = model2 = 0;
254 numModels = 0;
255 clearSprites();
256 }
257
258 // Set the properties of a particle type
set(float mrl,float mrs,float mrjs,float rv,float mrrv,float fs,float mrfs,float sf,float ss,float sst,int hb,bool gl,float acc,int anf,float ad,float mrad,int al)259 void ParticleType::set(float mrl, float mrs, float mrjs, float rv, float mrrv, float fs, float mrfs, float sf, float ss, float sst,
260 int hb, bool gl, float acc, int anf, float ad, float mrad, int al)
261 {
262 maxRandAddedToLife = mrl;
263 maxRandAddedToSpeed = mrs;
264 maxRandAddedToJumpSpeed = mrjs;
265 minRotVel = rv;
266 maxRandAddedToRotVel = mrrv;
267 minFadeSpeed = fs;
268 maxRandAddedToFadeSpeed = mrfs;
269 startFade = sf;
270 scaleSpeed = ss;
271 scaleStop = sst;
272 hitBehavior = hb;
273 glow = gl;
274 accel = acc;
275
276 animNumFrames = anf;
277 animDelay = ad;
278 maxRandAddedToAnimDelay = mrad;
279 animLoop = al;
280 }
281
282 // Specify a list of texture IDs that makes up the object's sprite collection
setSprites(int num,...)283 void ObjectType::setSprites(int num, ...)
284 {
285 if (num > 0 && num <= MAX_OBJ_SPRITES)
286 {
287 va_list ap;
288 va_start(ap, num);
289 for (int i = 0; i < num; i++)
290 {
291 int sp = va_arg(ap, int);
292 if (sp >= -1 && sp < NUM_TEXTURES)
293 sprites[i] = sp;
294 }
295 }
296 }
297
resetIDCounter()298 void Object::resetIDCounter()
299 { oID = 0; }
300
setID()301 void Object::setID()
302 { id = oID; oID++; }
303
304 // Initializes an object.
reset()305 void Object::reset()
306 {
307 h = speed = hvel = turnvel = 0;
308
309 pos = origPos;
310 heading = origHeading;
311
312 lookAngle = heading;
313 jumpSpeed = jumpHeading = jumpStrafe = jumpPause = 0;
314 spr1Anim.reset();
315 spr2Anim.reset();
316
317 model[0].set(objType[type].model1);
318 model[1].set((!augmented) ? objType[type].model2 : MDL_PLAYER_TORSO2);
319
320 ev.reset();
321 health = objType[type].maxHealth;
322 active = false;
323 dying = false;
324 destroyMe = false;
325 hitGround = false;
326 collideWithObjects = false;
327 fall = false;
328 fallTimer = stunTimer = dyingTimer = 0;
329 shockCollisions = 0;
330 collided = -1;
331 currSurfaceFriction = 0;
332 currSurfaceHealth = 0;
333 surfaceHealthSound = false;
334 dirAlert = NO_ALERT;
335 dirAlertType = ALERT_NONE;
336 lastDamageAmount = 0.0;
337
338 moveVel.set(0, 0);
339 forceVel.set(0, 0);
340 }
341
changeVel(Vector2D v)342 void Object::changeVel(Vector2D v)
343 { vel += v; }
344
changeForceVel(Vector2D v)345 void Object::changeForceVel(Vector2D v)
346 { forceVel += (v/objType[type].mass); }
347
changeForceVelNoMass(Vector2D v)348 void Object::changeForceVelNoMass(Vector2D v)
349 { forceVel += v; }
350
351
changeHealth(float amt)352 void Object::changeHealth(float amt)
353 {
354 float prevHealth = health;
355 if (objType[type].maxHealth != INF)
356 health = (health+amt < objType[type].maxHealth) ? health+amt : objType[type].maxHealth;
357
358 if (type == ENT_PLAYER)
359 {
360 if (health-prevHealth < 0)
361 addBlur(0.1*fabs(health-prevHealth), 1.0, 0.0, 0.0);
362 else if (health-prevHealth > 0)
363 addBlur(0.05*fabs(health-prevHealth), 0.0, 0.0, 1.0);
364 }
365 }
366
changeHealthAmt(float amt)367 float Object::changeHealthAmt(float amt)
368 {
369 float prevHealth = health;
370 changeHealth(amt);
371 return health - prevHealth;
372 }
373
374 // Widely-used drawing commands
drawStart()375 void Object::drawStart()
376 {
377 glPushMatrix();
378
379 glTranslatef(pos.c[X], pos.c[Y], 0);
380
381 // uncomment to draw bounding circles
382 /*glPushMatrix();
383 glColor3f(1.0, 0.0, 0.0);
384 for (int i = 0; i < 360; i+=15)
385 {
386 glRotatef(15.0, 0.0, 0.0, i);
387 glBegin(GL_POINTS);
388 glVertex2f(objType[type].boundRadius, 0.0);
389 glEnd();
390 }
391 glPopMatrix();*/
392
393 glRotatef(todeg(lookAngle), 0.0, 0.0, 1.0);
394
395 glEnable(GL_TEXTURE_2D);
396 glColor4f(1.0, 1.0, 1.0, 1.0);
397 }
398
drawShadow()399 void Object::drawShadow()
400 {
401 if (active && objType[type].hasShadow && h >= 0)
402 {
403 glPushMatrix();
404 // translate ever so slightly above the ground to avoid being
405 // screwed over by the depth test
406 glTranslatef(pos.c[X], pos.c[Y], 0.01);
407 glRotatef(todeg(lookAngle), 0.0, 0.0, 1.0);
408 glEnable(GL_TEXTURE_2D);
409 glColor4f(1.0, 1.0, 1.0, 1.0);
410 texQuadSize(TEX_SHADOW, objType[type].boundRadius*2, objType[type].boundRadius*2);
411 glDisable(GL_TEXTURE_2D);
412 glPopMatrix();
413 }
414 }
415
drawEnd()416 void Object::drawEnd()
417 {
418 glDisable(GL_TEXTURE_2D);
419 glPopMatrix();
420 }
421
422 // Typical way of drawing an object (shadow, spr1, spr2)
draw()423 void Object::draw()
424 {
425 if (active)
426 {
427 drawStart();
428
429 if (!objType[type].numModels)
430 {
431 glTranslatef(0.0, 0.0, h+0.02);
432 if (spr1 != NO_SPRITE) {texQuad(objType[type].sprites[spr1]);}
433 glTranslatef(0.0, 0.0, 0.01);
434 if (spr2 != NO_SPRITE) {texQuad(objType[type].sprites[spr2]);}
435 }
436 else
437 {
438 glEnable(GL_TEXTURE_2D);
439 glTranslatef(0.0, 0.0, h+0.02);
440
441 for (int i = 0; i < objType[type].numModels; i++)
442 model[i].draw();
443 }
444 drawEnd();
445 }
446 }
447
448 // Returns a pointer to the closest wall intersected, or NULL.
wallCollisionDetect(int cell,Vector2D * inter)449 Wall* Object::wallCollisionDetect(int cell, Vector2D *inter)
450 {
451 // Don't do collision detection on static objects
452 if (fabs(vel.mag()) > 0.01 || type == ENT_PLAYER)
453 {
454 Vector2D intersect;
455 int numWalls = currLevel.grid[cell].wall.size();
456 Wall *currWall;
457
458 Wall *closestHitWall = NULL;
459 Vector2D closestHitIntersect;
460 float closestHitDistance = 1e37; // big numbah!
461
462 for (int i = 0; i < numWalls + 4*currLevel.grid[cell].door.size(); i++)
463 {
464 // A pointer allows us to combine collision detection with
465 // walls and doors into one loop.
466 if (i < numWalls)
467 currWall = &currLevel.wall[currLevel.grid[cell].wall[i]];
468 else
469 currWall = &currLevel.door[currLevel.grid[cell].door[(i-numWalls)/4]].sides[(i-numWalls)%4];
470
471 if ((currWall->CollFlags() & COLLIDE_OBJECT && !IS_BULLET(type)) ||
472 (currWall->CollFlags() & COLLIDE_BULLET && IS_BULLET(type)))
473 {
474 // Open doors
475 if (i >= numWalls)
476 currLevel.door[currLevel.grid[cell].door[(i-numWalls)/4]].detect(pos, (type == ENT_PLAYER) ? keys : -1);
477
478 Vector2D offset = vel;
479 offset.normalize();
480 offset *= objType[type].boundRadius+(vel.mag()*timer.dT());
481
482 Vector2D back = vel;
483 back.normalize();
484 back *= objType[type].boundRadius;
485
486 if (currWall->intersection(pos-back, pos+offset, &intersect))
487 {
488 if ((intersect-pos).mag() < closestHitDistance)
489 {
490 closestHitDistance = (intersect-pos).mag();
491 closestHitWall = currWall;
492 closestHitIntersect = Vector2D(intersect.c[X],intersect.c[Y]);
493 }
494 }
495 else
496 currWall->renewTrigger();
497 }
498 }
499 if (closestHitWall)
500 {
501 if (inter)
502 inter->set(closestHitIntersect.c[X], closestHitIntersect.c[Y]);
503
504 return closestHitWall;
505 }
506 }
507 return NULL;
508 }
509
510 // Shockwave collision detection
shockwaveCollisionDetect()511 void Object::shockwaveCollisionDetect()
512 {
513 float swf, swh, ang;
514 for (int i = 0; !IS_POWERUP(type) && i < liveShockwaves; i++)
515 {
516 if (shockwaves[i].isActive())
517 {
518 if (shockwaves[i].detect(pos, &swf, &swh) && !(shockCollisions & (1 << (i % 64))))
519 {
520 if (swh == SHOCK_ESG)
521 {
522 if (IS_ENEMY(type) && !Stunned())
523 {
524 stun(15);
525 LAUNCH_MULTI_PARTICLES(25, PART_PARTICLE_SPARK,
526 pos.c[X], pos.c[Y],0.5,torad(rand()%360),1.0,0);
527
528 LAUNCH_MULTI_PARTICLES(5, PART_SMOKE,
529 pos.c[X], pos.c[Y],0.5,torad(rand()%360),1.0,0);
530
531 playSound(SND_ROBOT_PARALYZE, 5);
532 }
533 }
534 else
535 {
536 if (type == ENT_GLADIATOR)
537 {
538 if (shockwaves[i].AffectGladiators())
539 damage(swh, DAMAGE_SHOCKWAVE);
540 }
541 else
542 damage(swh, DAMAGE_SHOCKWAVE);
543 }
544
545 ang = atan2(pos.c[Y]-shockwaves[i].PosY(),pos.c[X]-shockwaves[X].PosX());
546 setDirectionAlert(ang+PI, ALERT_SHOCKWAVE);
547
548 if (!fixed)
549 changeForceVel(Vector2D(cos(ang)*swf, sin(ang)*swf));
550
551 // set bit flag
552 shockCollisions |= 1 << (i % 64);
553
554 if (type == ENT_PLAYER && health <= 0)
555 playSound(SND_EXPLODE, 2);
556 }
557 }
558 else
559 {
560 if (shockCollisions & (1 << (i % 64)))
561 shockCollisions &= ~(1 << (i % 64));
562 }
563 }
564
565 if (liveShockwaves == 0)
566 shockCollisions = 0;
567 }
568
569 // Collision with laaaaaaaaaaaaaaasers.
laserCollisionDetect()570 void Object::laserCollisionDetect()
571 {
572 for (int i = 0; !IS_POWERUP(type) && i < liveLasers; i++)
573 {
574 if ((type == ENT_PLAYER && lasers[i].Live() > 0) ||
575 (type != ENT_PLAYER && lasers[i].Live() == 1))
576 {
577 Vector2D intersection;
578 for (int j = 0; j < lasers[i].beams.size(); j++)
579 {
580 if (circleSegmentIntersection(pos, objType[type].boundRadius,
581 lasers[i].beams[j].p1, lasers[i].beams[j].p2, &intersection))
582 {
583 if (type != ENT_PLAYER)
584 damage(-lasType[lasers[i].Type()].damage, DAMAGE_BULLET);
585 else
586 damage(-lasType[lasers[i].Type()].damage*0.3, DAMAGE_BULLET);
587
588 if (lasers[i].Type() == L_STANDARD)
589 {
590 LAUNCH_MULTI_PARTICLES(15, PART_LASER_SPARK1,
591 intersection.c[X], intersection.c[Y], h, torad(rand()%360),1.0,0);
592 }
593 else if (lasers[i].Type() == L_CHARGED)
594 {
595 LAUNCH_MULTI_PARTICLES(30, PART_LASER_SPARK2,
596 intersection.c[X], intersection.c[Y], h, torad(rand()%360),1.0,0);
597 }
598 }
599 }
600 }
601 }
602 }
603
604 // Collision with floors; returns true if on a floor.
floorCollisionDetect(int cell,float * fric)605 bool Object::floorCollisionDetect(int cell, float *fric)
606 {
607 if (h <= 0.2)
608 {
609 int s;
610 bool onFloor = false;
611 for (int i = 0; i < currLevel.grid[cell].floor.size(); i++)
612 {
613 s = currLevel.grid[cell].floor[i];
614 if (currLevel.floor[s].CollFlags() & COLLIDE_OBJECT)
615 {
616 if (currLevel.floor[s].pointInBB(pos) && currLevel.floor[s].pointInPolygon(pos))
617 {
618 if (fric)
619 *fric = currLevel.floor[s].Friction();
620
621 currLevel.floor[s].handleTrigger(false, type == ENT_PLAYER);
622 onFloor = true;
623 }
624 else
625 currLevel.floor[s].renewTrigger();
626 }
627 }
628 return onFloor;
629 }
630 return true;
631 }
632
633 // Collision with surfaces
surfaceCollisionDetect(int cell,float * fric,float * hlth)634 void Object::surfaceCollisionDetect(int cell, float *fric, float *hlth)
635 {
636 if (h <= 0.2)
637 {
638 int s;
639 for (int i = 0; i < currLevel.grid[cell].surf.size(); i++)
640 {
641 s = currLevel.grid[cell].surf[i];
642 if (currLevel.surf[s].pointInBB(pos) && currLevel.surf[s].pointInPolygon(pos))
643 {
644 if (fric) *fric = currLevel.surf[s].Friction();
645 if (hlth) *hlth = currLevel.surf[s].Health();
646
647 if (vel.mag() > 1.0 || hvel != 0)
648 currLevel.surf[s].spewParticles(pos);
649
650 currLevel.surf[s].handleTrigger(false, type == ENT_PLAYER);
651 }
652 else
653 currLevel.surf[s].renewTrigger();
654 }
655 }
656 }
657
658
objectCollisionDetect(int cell)659 void Object::objectCollisionDetect(int cell)
660 {
661 if (collideWithObjects && active)
662 {
663 Object *obj;
664
665 for (int j = 0; j < currLevelObjs.size(); j++)
666 {
667 obj = currLevelObjs[j];
668
669 if (obj->isActive() && obj->CollideWithObjects())
670 {
671 if (id != obj->ID() &&
672 collided != obj->ID() &&
673 obj->Collided() != id)
674 {
675 // Extend object's reach for powerups
676 if (IS_POWERUP(obj->Type()))
677 {
678 Vector2D offset = vel;
679 offset.normalize();
680 offset *= objType[type].boundRadius+(vel.mag()*timer.dT());
681 if (distSquared(pos+offset, obj->Pos()) <=
682 (objType[type].boundRadius +
683 objType[obj->Type()].boundRadius)*
684 (objType[type].boundRadius +
685 objType[obj->Type()].boundRadius))
686 {
687 if (type == ENT_PLAYER)
688 {
689 if (h == 0.0)
690 givePowerup(j);
691 }
692 }
693 }
694
695 if (distSquared(pos, obj->Pos()) <=
696 (objType[type].boundRadius +
697 objType[obj->Type()].boundRadius)*
698 (objType[type].boundRadius +
699 objType[obj->Type()].boundRadius))
700 {
701 if ((IS_POWERUP(obj->Type()) || IS_POWERUP(type)) && (h != 0.0 || obj->Height() != 0.0))
702 continue;
703 else
704 {
705 float m1 = objType[type].mass;
706 float m2 = objType[obj->Type()].mass;
707
708 float v1i = vel.mag();
709 float v2i = obj->Vel().mag();
710
711 float v1f = (m1*v1i - m2*(v1i - 2*v2i))/(m1+m2);
712 float v2f = (m1*(2*v1i - v2i) + m2*v2i)/(m1+m2);
713
714 float a1 = atan2(pos.c[Y]-obj->PosY(),
715 pos.c[X]-obj->PosX());
716 float a2 = atan2(obj->PosY()-pos.c[Y],
717 obj->PosX()-pos.c[X]);
718
719
720 setCollided(obj->ID());
721 obj->setCollided(id);
722
723 if (fabs(v1i) > fabs(v2i))
724 pos += Vector2D(cos(a1), sin(a1)) * ((vel*timer.dT()).mag());
725 else
726 obj->addPos(Vector2D(cos(a2), sin(a2)) * ((obj->Vel()*timer.dT()).mag()));
727
728 if (!fixed)
729 changeForceVelNoMass(Vector2D(cos(a1), sin(a1))*fabs(v1f)*0.1);
730
731 if (!obj->Fixed())
732 obj->changeForceVelNoMass(Vector2D(cos(a2), sin(a2))*fabs(v2f)*0.1);
733
734 setDirectionAlert(a2, ALERT_TOUCH);
735 obj->setDirectionAlert(a1, ALERT_TOUCH);
736 }
737 }
738 }
739 }
740 }
741 }
742 }
743
744 // Handles collision detection with walls and surfaces.
levelCollisionDetection()745 void Object::levelCollisionDetection()
746 {
747 if (currLevel.inGrid(pos))
748 {
749 int cell = currLevel.cellNumber(pos);
750
751 shockwaveCollisionDetect();
752 laserCollisionDetect();
753 if (forceVel.mag() > TERMINAL_VELOCITY)
754 { forceVel.normalize(); forceVel *= TERMINAL_VELOCITY; }
755 vel = moveVel + forceVel;
756
757 objectCollisionDetect(cell);
758 if (forceVel.mag() > TERMINAL_VELOCITY)
759 { forceVel.normalize(); forceVel *= TERMINAL_VELOCITY; }
760 vel = moveVel + forceVel;
761
762 Vector2D impactPoint;
763 Wall *closestHitWall = wallCollisionDetect(cell, &impactPoint);
764
765 // React to a collision, if there was one
766 if (closestHitWall)
767 {
768 // Push the object back by his velocity
769 // in the direction of the wall's normal
770 if (vel.mag() < WALL_COLLISION_THRESHOLD)
771 pos += closestHitWall->normal * ((vel*timer.dT()).mag()+0.1);
772 else
773 {
774 pos = impactPoint;
775 pos += closestHitWall->normal * (objType[type].boundRadius+0.1);
776 moveVel.set(0,0);
777 forceVel.set(0,0);
778 }
779
780 if (vel.mag() >= WALL_DAMAGE_THRESHOLD)
781 // Lose health based on your impact velocity.
782 damage(-vel.mag()/IMPACT_DAMAGE_FACTOR,false);
783
784 if (closestHitWall->Health() < 0)
785 damage(closestHitWall->Health(),false);
786 else if (closestHitWall->Health() > 0)
787 changeHealth(closestHitWall->Health());
788
789 if (closestHitWall->Force())
790 changeForceVel(closestHitWall->normal*closestHitWall->Force());
791
792 if (closestHitWall->Health() < 0)
793 {
794 if (type == ENT_PLAYER)
795 {
796 LAUNCH_MULTI_PARTICLES(10, PART_PARTICLE_BLOOD,
797 pos.c[X], pos.c[Y],0,torad(rand()%360),1.0,0);
798 }
799 }
800 closestHitWall->handleTrigger(false, type == ENT_PLAYER);
801 }
802
803 currSurfaceFriction = DEFAULT_SURFACE_FRICTION;
804 currSurfaceHealth = 0;
805
806 if (!floorCollisionDetect(cell, &currSurfaceFriction) && !fall)
807 {
808 fall = true;
809 currSurfaceFriction = 0;
810 if (type == ENT_PLAYER)
811 {
812 setMessage("Oh crap");
813 playSound(SND_FALL, 2);
814 }
815 }
816
817 surfaceCollisionDetect(cell, &currSurfaceFriction, &currSurfaceHealth);
818 }
819 }
820
turn(float amt)821 void Object::turn(float amt)
822 {
823 turnvel = amt;
824 printf("%f\n", turnvel);
825 }
826
setAnimation(int val,bool anim)827 void Object::setAnimation(int val, bool anim)
828 {
829 for (int i = 0; i < objType[type].numModels; i++)
830 model[i].setAnimation(val, anim);
831 }
832
833 // Basic framework for updating an object's position, velocity, heading.
834 // States, jumping, sprite representation is per-subtype.
update()835 void Object::update()
836 {
837 if (active && timer.dT() > 0)
838 {
839 if (!currLevel.inGrid(pos))
840 { active = false; return; }
841
842 for (int i = 0; i < objType[type].numModels; i++)
843 model[i].update();
844
845 if (objType[type].maxHealth != INF)
846 {
847 if (type == ENT_PLAYER && config.cheat_invul && currSurfaceHealth < 0) {}
848 else
849 changeHealth(currSurfaceHealth * timer.dT());
850 }
851
852 dyingCheck();
853
854 if (type == ENT_PLAYER)
855 {
856 if (currSurfaceHealth > 0)
857 {
858 if (surfaceHealthSound) { playSound(SND_HEAL, 2); surfaceHealthSound = false; }
859 }
860 else if (currSurfaceHealth < 0 && !config.cheat_invul)
861 {
862 if (surfaceHealthSound) { playSound(SND_HURTSURFACE, 2); surfaceHealthSound = false; }
863 }
864 else
865 surfaceHealthSound = true;
866 }
867
868 // Kill the object's ability to do anything if we're stunned
869 if (stunTimer > 0)
870 {
871 ev.reset();
872 stunTimer -= timer.dT();
873 }
874 else
875 stunTimer = 0;
876
877 turnvel += objType[type].turnAccel * -ev.getLR();
878
879 //float friction = turnvel * -currSurfaceFriction;
880 turnvel += (turnvel * -currSurfaceFriction);//friction;
881
882 if (turnvel > objType[type].maxTurnvel * fabs(ev.getLR()))
883 turnvel = objType[type].maxTurnvel * fabs(ev.getLR());
884 if (turnvel < -objType[type].maxTurnvel * fabs(ev.getLR()))
885 turnvel = -objType[type].maxTurnvel * fabs(ev.getLR());
886 lookAngle += turnvel * timer.dT();
887
888 if (h == 0)
889 {
890 heading = lookAngle;
891
892 if (fall == true)
893 h -= ACCEL_GRAVITY * timer.dT();
894 }
895
896 float maxVel = objType[type].maxVel;
897 if (augmented) maxVel *= AUG_SPEED_INCREASE;
898
899 if (moveVel.mag() <= maxVel)
900 {
901 if (h == 0 && jumpPause == 0.0)
902 {
903 moveVel.c[X] += cos(heading) * objType[type].accel * ev.getFB();
904 moveVel.c[Y] += sin(heading) * objType[type].accel * ev.getFB();
905
906 moveVel.c[X] += cos(heading-PIOVER2) * objType[type].accel * ev.getStrafe();
907 moveVel.c[Y] += sin(heading-PIOVER2) * objType[type].accel * ev.getStrafe();
908
909 }
910 else
911 {
912 moveVel.c[X] += cos(jumpHeading) * jumpSpeed;
913 moveVel.c[Y] += sin(jumpHeading) * jumpSpeed;
914 moveVel.c[X] += cos(jumpHeading-PIOVER2) * jumpStrafe;
915 moveVel.c[Y] += sin(jumpHeading-PIOVER2) * jumpStrafe;
916 }
917 }
918
919 if (moveVel.mag() > maxVel)
920 {
921 moveVel.normalize();
922 moveVel *= maxVel;
923 }
924
925 if (jumpPause > 0)
926 moveVel.set(0,0);
927
928 if (h == 0)
929 moveVel += moveVel * -currSurfaceFriction;
930 else
931 moveVel += moveVel * -0.1;
932
933 if (moveVel.mag() > TERMINAL_VELOCITY)
934 {
935 moveVel.normalize();
936 moveVel *= TERMINAL_VELOCITY;
937 }
938
939 forceVel += (forceVel * -currSurfaceFriction);
940 if (forceVel.mag() < 0.01)
941 forceVel.set(0,0);
942 if (forceVel.mag() > TERMINAL_VELOCITY)
943 {
944 forceVel.normalize();
945 forceVel *= TERMINAL_VELOCITY;
946 }
947
948 vel = moveVel + forceVel;
949 // Collision detection
950 levelCollisionDetection();
951
952 vel = moveVel + forceVel;
953 if (vel.mag() > TERMINAL_VELOCITY)
954 {
955 vel.normalize();
956 vel *= TERMINAL_VELOCITY;
957 }
958
959 pos += (vel) * timer.dT();
960
961 // Object is affected by gravity if it is not on the ground
962 if (objType[type].gravAffect)
963 {
964 if (h != 0)
965 hvel -= ACCEL_GRAVITY * timer.dT();
966 if (h < 0 && !fall)
967 hitGround = true; // response is per-object-type
968 }
969
970 h += hvel * timer.dT();
971
972 if (jumpPause > 0)
973 jumpPause -= timer.dT();
974 else if (jumpPause < 0)
975 jumpPause = 0;
976
977 if (jumpPause == 0)
978 {
979 spr1Anim.update();
980 spr2Anim.update();
981 }
982
983 // Respond to triggers
984 if (triggers[respondTo2].hit)
985 { kill(true); }
986
987 if (type == ENT_PLAYER)
988 {
989 if (health < 25)
990 setBlur(1.0, 1.0, 0.0, 0.0);
991 }
992
993 if (health != INF)
994 {
995 if (health < 0) health = 0;
996 if (health > objType[type].maxHealth) health = objType[type].maxHealth;
997 }
998
999 if (dying)
1000 {
1001 dyingTimer -= timer.dT();
1002 if (dyingTimer <= 0)
1003 {
1004 dying = false;
1005 kill(true);
1006 }
1007 }
1008 }
1009
1010 if (fall)
1011 {
1012 fallTimer += timer.dT();
1013 if (fallTimer >= FALL_KILL_DELAY)
1014 {
1015 kill(false);
1016 fallTimer = 0;
1017 fall = false;
1018 if (type == ENT_PLAYER)
1019 playSound(SND_EXPLODE, 2);
1020 }
1021 }
1022 }
1023
1024 // by = true for explosions, false for bullets/etc.
damage(float amt,bool by)1025 void Object::damage(float amt, bool by)
1026 {
1027 if (type == ENT_PLAYER)
1028 {
1029 if (amt < 0 && !config.cheat_invul)
1030 {
1031 changeHealth(amt*((augmented)?AUG_DAMAGE_SUPPRESSION:1.0));
1032 P.hits++;
1033 }
1034 }
1035 else
1036 changeHealth(amt);
1037
1038 if (!(Stunned() || Dying()))
1039 {
1040 if (type != ENT_PLAYER)
1041 setAnimation(MA_FLINCH, ANIM_TRANSIENT);
1042
1043 if (type == ENT_PLAYER)
1044 { if (!config.cheat_invul) playSound(SND_HURT1 + rand()%2, 2); }
1045 else
1046 playSound(SND_BULLET_HIT_HARD, 1);
1047
1048 damagedBy = by;
1049 lastDamageAmount = amt;
1050 }
1051 }
1052
stun(float amt)1053 void Object::stun(float amt)
1054 {
1055 stunTimer += amt;
1056
1057 if (type != ENT_PLAYER && !Dying())
1058 setAnimation(MA_IDLE, ANIM_BASE);
1059 }
1060
dyingCheck()1061 void Object::dyingCheck()
1062 {
1063 if (!dying && health <= 0 && health != INF)
1064 {
1065 switch(type)
1066 {
1067 case ENT_PLAYER:
1068 if (damagedBy == DAMAGE_BULLET)
1069 {
1070 model[0].setAnimation(MA_PL_DIESOFT, ANIM_TRANSIENT);
1071 model[0].setAnimation(MA_PL_DEADSOFT, ANIM_BASE);
1072 model[1].setAnimation(MA_PT_DIESOFT, ANIM_TRANSIENT);
1073 model[1].setAnimation(MA_PT_DEADSOFT, ANIM_BASE);
1074 }
1075 else
1076 {
1077 model[0].setAnimation(MA_PL_DIEHARD, ANIM_TRANSIENT);
1078 model[0].setAnimation(MA_PL_DEADHARD, ANIM_BASE);
1079 model[1].setAnimation(MA_PT_DIEHARD, ANIM_TRANSIENT);
1080 model[1].setAnimation(MA_PT_DEADHARD, ANIM_BASE);
1081 }
1082 die(2);
1083 stun(99999);
1084 playSound(SND_EXPLODE, 2);
1085 break;
1086 case ENT_UNARMED_GUARD: case ENT_MIB:
1087 case ENT_ARMED_DRONE: case ENT_HUNTER: case ENT_GLADIATOR:
1088 if (lastDamageAmount > GIB_THRESHOLD)
1089 {
1090 setAnimation(MA_DIE, ANIM_TRANSIENT);
1091 setAnimation(MA_DEAD, ANIM_BASE);
1092 die(1);
1093 stun(10);
1094 }
1095 else
1096 die(0);
1097 break;
1098 default:
1099 die(0);
1100 }
1101 }
1102 }
1103
kill(bool death)1104 void Object::kill(bool death)
1105 {
1106 health = 0;
1107 active = false;
1108 selectWeapon(0);
1109
1110 if (type == ENT_PLAYER)
1111 setBlur(1.0, 1.0, 0.0, 0.0);
1112 else if (IS_ENEMY(type))
1113 P.kills++;
1114
1115 if (death)
1116 doDeath();
1117 }
1118
doDeath()1119 void Object::doDeath()
1120 {
1121 float distToPlayer, randAng, randRad, px, py, ph, phead;
1122 switch(objType[type].deathType)
1123 {
1124 case DEATH_NONE: break;
1125 case DEATH_PLAYER:
1126 break;
1127 case DEATH_WOOD:
1128 LAUNCH_MULTI_PARTICLES(20, PART_PARTICLE_WOOD,
1129 pos.c[X]+frand()*3.0-1.5, pos.c[Y]+frand()*3.0-1.5,0,torad(rand()%360),1.0,0);
1130 break;
1131 case DEATH_SMALL_EXPLOSION:
1132 LAUNCH_MULTI_PARTICLES(4, PART_EXPLOSION,
1133 pos.c[X]+frand()*7.0-3.5, pos.c[Y]+frand()*7.0-3.5,0.5,torad(rand()%360),1.0,rand()%4);
1134
1135 LAUNCH_MULTI_PARTICLES(25, PART_EXPLOSIONPART,
1136 pos.c[X], pos.c[Y],0.5,torad(rand()%360),1.0,0);
1137
1138 addShockwave(pos.c[X], pos.c[Y], 0, 0, 9, 0, 0.5, 48000, -50);
1139
1140 distToPlayer = dist(pos, PLAYER_OBJECT->Pos());
1141 if (distToPlayer < 20)
1142 setScreenQuake(lerp(5, 0, distToPlayer/20));
1143
1144 playSound(SND_EXPLOSION, 5);
1145 break;
1146
1147
1148 case DEATH_GRENADE_EXPLOSION:
1149 LAUNCH_MULTI_PARTICLES(4, PART_EXPLOSION,
1150 pos.c[X]+frand()*7.0-3.5, pos.c[Y]+frand()*7.0-3.5,0.5,torad(rand()%360),1.0,rand()%4);
1151
1152 addShockwave(pos.c[X], pos.c[Y], 0, 0, 16, 0, 0.5, 40000, -70);
1153
1154 distToPlayer = dist(pos, PLAYER_OBJECT->Pos());
1155 if (distToPlayer < 20)
1156 setScreenQuake(lerp(5, 0, distToPlayer/20));
1157
1158 playSound(SND_GRENADE_EXPLOSION, 5);
1159
1160 for (int i = 0; i < 25; i++)
1161 launchBullet(-1, BLT_SHRAPNEL, pos.c[X], pos.c[Y], 0.5, torad(rand()%360));
1162
1163 break;
1164
1165 case DEATH_EXPLOSION:
1166 LAUNCH_MULTI_PARTICLES(4, PART_EXPLOSION,
1167 pos.c[X]+frand()*7.0-3.5, pos.c[Y]+frand()*7.0-3.5,0.5,torad(rand()%360),1.0,rand()%4);
1168
1169 LAUNCH_MULTI_PARTICLES(25, PART_EXPLOSIONPART,
1170 pos.c[X], pos.c[Y],0.5,torad(rand()%360),1.0,0);
1171
1172 LAUNCH_MULTI_PARTICLES(5, PART_SMOKE,
1173 pos.c[X], pos.c[Y],0.5,torad(rand()%360),1.0,0);
1174
1175 addShockwave(pos.c[X], pos.c[Y], 0, 0, 10, 0, 0.5, 48000, -65);
1176
1177 distToPlayer = dist(pos, PLAYER_OBJECT->Pos());
1178 if (distToPlayer < 20)
1179 setScreenQuake(lerp(5, 0, distToPlayer/20));
1180
1181 playSound(SND_EXPLOSION, 5);
1182 break;
1183
1184
1185 case DEATH_HEPA_EXPLOSION:
1186 LAUNCH_MULTI_PARTICLES(4, PART_HEPA_EXPLOSION,
1187 pos.c[X]+frand()*14.0-7, pos.c[Y]+frand()*14.0-7,0.5,torad(rand()%360),1.0,0);
1188 LAUNCH_MULTI_PARTICLES(50, PART_HEPA_SPARK,
1189 pos.c[X], pos.c[Y],0.5,torad(rand()%360),1.0,0);
1190
1191 addShockwave(pos.c[X], pos.c[Y], 0, 0, 20, 0, 0.5, 0, -200);
1192
1193 distToPlayer = dist(pos, PLAYER_OBJECT->Pos());
1194 if (distToPlayer < 80)
1195 setScreenQuake(lerp(5, 0, distToPlayer/80));
1196
1197 playSound(SND_HEPA_EXPLOSION, 5);
1198 break;
1199 case DEATH_POWERUP:
1200 for (int i = 0; i < 15; i++)
1201 {
1202 randAng = frand()*PI*2;
1203 randRad = frand()*0.25;
1204 launchParticle(PART_PARTICLE_GLOW,
1205 pos.c[X]+(cos(randAng)*randRad), pos.c[Y]+(sin(randAng)*randRad), 0.5, randAng,1.0,0);
1206 }
1207
1208 break;
1209 case DEATH_ROBOT:
1210 LAUNCH_MULTI_PARTICLES(25, PART_PARTICLE_METAL,
1211 pos.c[X], pos.c[Y],0,torad(rand()%360),1.0,0);
1212 LAUNCH_MULTI_PARTICLES(25, PART_EXPLOSIONPART,
1213 pos.c[X], pos.c[Y],0.5,torad(rand()%360),1.0,0);
1214
1215 playSound(SND_ROBOT_DEATH, 5);
1216
1217 break;
1218 case DEATH_GLADIATOR:
1219 LAUNCH_MULTI_PARTICLES(4, PART_HEPA_EXPLOSION,
1220 pos.c[X]+frand()*14.0-7, pos.c[Y]+frand()*14.0-7,0.5,torad(rand()%360),1.0,0);
1221 LAUNCH_MULTI_PARTICLES(50, PART_HEPA_SPARK,
1222 pos.c[X], pos.c[Y],0.5,torad(rand()%360),1.0,0);
1223
1224 addShockwave(pos.c[X], pos.c[Y], 0, 0, 20, 0, 0.5, 0, -200, false);
1225
1226 distToPlayer = dist(pos, PLAYER_OBJECT->Pos());
1227 if (distToPlayer < 80)
1228 setScreenQuake(lerp(5, 0, distToPlayer/80));
1229
1230 playSound(SND_HEPA_EXPLOSION, 5);
1231 break;
1232 case DEATH_ESG:
1233 px = PLAYER_OBJECT->PosX(); py = PLAYER_OBJECT->PosY();
1234 ph = PLAYER_OBJECT->Height();
1235 phead = PLAYER_OBJECT->LookAngle();
1236
1237 launchParticle(PART_ESG_SHOCKWAVE,
1238 px+(wpnType[WPN_ESG].spriteSize.c[X]/3.5*cos(phead)),
1239 py+(wpnType[WPN_ESG].spriteSize.c[X]/3.5*sin(phead)),
1240 ph, phead, 1.0, 0);
1241 setBlur(2.0, 1.0, 0.5, 1.0);
1242 setScreenQuake(7.0);
1243 addShockwave(px+(wpnType[WPN_ESG].spriteSize.c[X]/3.5*cos(phead)),
1244 py+(wpnType[WPN_ESG].spriteSize.c[X]/3.5*sin(phead)),
1245 0, 0, 0, 100.0, 0.5, 0, SHOCK_ESG);
1246 break;
1247 }
1248
1249 handleTrigger(false, type == ENT_PLAYER);
1250 }
1251
reset()1252 void Entity::reset()
1253 {
1254 Object::reset();
1255 jumping = false;
1256 currWeapon = 0;
1257 keys = 0;
1258 weapons = 1;
1259 walkSound = false;
1260 grenadeThrowTimer = 0;
1261 throwGrenade = false;
1262
1263 switch(type)
1264 {
1265 case ENT_PLAYER:
1266
1267 if (!augmented)
1268 {
1269 wpn[0].set(WPN_NONE, MAX_AMMO*(int)config.cheat_weapons, 0); wpnPos[0].set(0,0);
1270 wpn[1].set(WPN_RIFLE, MAX_AMMO*(int)config.cheat_weapons, 0); wpnPos[1].set(-0.25, 1.1);
1271 wpn[2].set(WPN_SHOTGUN, MAX_AMMO*(int)config.cheat_weapons, 0); wpnPos[2].set(-0.20, 1.1);
1272 wpn[3].set(WPN_HEPA, MAX_AMMO*(int)config.cheat_weapons, 0); wpnPos[3].set(-0.35, 0.9);
1273 wpn[4].set(WPN_GRENADE, MAX_AMMO*(int)config.cheat_weapons, 0); wpnPos[4].set(-0.45, 0.9);
1274 wpn[5].set(WPN_LASER, MAX_AMMO*(int)config.cheat_weapons, 0); wpnPos[5].set(-0.30, 0.9);
1275 wpn[6].set(WPN_ESG, MAX_AMMO*(int)config.cheat_weapons, 0); wpnPos[6].set(-0.30, 0.9);
1276 }
1277 else
1278 wpn[0].set(WPN_BLASTER, MAX_AMMO, 0); wpnPos[0].set(-0.30, 0.9);
1279
1280 break;
1281 case ENT_TURRET1: case ENT_SLAVE_TURRET:
1282 wpn[0].set(WPN_TURRET_GUN, MAX_AMMO, -1); wpnPos[0].set(0,2.2);
1283 break;
1284 case ENT_ARMED_DRONE:
1285 wpn[0].set(WPN_RIFLE, MAX_AMMO, -1); wpnPos[0].set(-1.6, 1.5);
1286 break;
1287 case ENT_HUNTER:
1288 wpn[0].set(WPN_RIFLE, MAX_AMMO, -1); wpnPos[0].set(-2.5, 0.4);
1289 wpn[1].set(WPN_RIFLE, MAX_AMMO, -1); wpnPos[1].set(2.5, 0.4);
1290 break;
1291 case ENT_GLADIATOR:
1292 wpn[0].set(WPN_SHOTGUN, MAX_AMMO, -1); wpnPos[0].set(1.6, 1.9);
1293 break;
1294
1295 }
1296
1297 if (type == ENT_PLAYER)
1298 {
1299 weapons = (!config.cheat_weapons || augmented) ? 1 : (1 << NUM_PLAYER_WEAPONS)-1;
1300 keys = (!config.cheat_keys) ? 0 : (1 << NUM_KEYS)-1;
1301 }
1302 }
1303
1304 // Draws an Entity.
draw()1305 void Entity::draw()
1306 {
1307 if (active)
1308 {
1309 if (type == ENT_PLAYER)
1310 {
1311 drawStart();
1312 glEnable(GL_TEXTURE_2D);
1313 glTranslatef(0.0, 0.0, h+0.02);
1314
1315 for (int i = 0; i < objType[type].numModels; i++)
1316 model[i].draw();
1317
1318 if (!Dying())
1319 {
1320 glTranslatef(wpnPos[currWeapon].c[Y], -wpnPos[currWeapon].c[X], 4.6);
1321 wpn[currWeapon].draw();
1322 }
1323
1324 drawEnd();
1325 }
1326 else
1327 Object::draw();
1328 }
1329 }
1330
setPlayerAnimations()1331 void Entity::setPlayerAnimations()
1332 {
1333 if (type == ENT_PLAYER && !dying)
1334 {
1335 // Torso animation
1336 if (ev.getFB() != 0 || ev.getStrafe() != 0)
1337 {
1338 switch(currWeapon)
1339 {
1340 case WPN_NONE: case WPN_GRENADE:
1341 model[1].setAnimation(MA_PT_RUN, ANIM_BASE);
1342 break;
1343 case WPN_RIFLE: case WPN_SHOTGUN: case WPN_LASER:
1344 model[1].setAnimation(MA_PT_RIFLERUN, ANIM_BASE);
1345 break;
1346 case WPN_HEPA: case WPN_ESG:
1347 model[1].setAnimation(MA_PT_HEPARUN, ANIM_BASE);
1348 break;
1349 }
1350 }
1351 else
1352 {
1353 switch(currWeapon)
1354 {
1355 case WPN_NONE: case WPN_GRENADE:
1356 model[1].setAnimation(MA_PT_STAND, ANIM_BASE);
1357 break;
1358 case WPN_RIFLE: case WPN_SHOTGUN: case WPN_LASER:
1359 model[1].setAnimation(MA_PT_RIFLE, ANIM_BASE);
1360 break;
1361 case WPN_HEPA: case WPN_ESG:
1362 model[1].setAnimation(MA_PT_HEPA, ANIM_BASE);
1363 break;
1364 }
1365 }
1366
1367 // Leg animation
1368 if (ev.getFB() > 0)
1369 model[0].setAnimation(MA_PL_FWD, ANIM_BASE);
1370 else if (ev.getFB() < 0)
1371 model[0].setAnimation(MA_PL_BACK, ANIM_BASE);
1372 else if (ev.getStrafe() > 0)
1373 model[0].setAnimation(MA_PL_FWDRIGHT, ANIM_BASE);
1374 else if (ev.getStrafe() < 0)
1375 model[0].setAnimation(MA_PL_FWDLEFT, ANIM_BASE);
1376 else
1377 model[0].setAnimation(MA_PL_STAND, ANIM_BASE);
1378 }
1379 }
1380
1381
1382 // Updates an Entity: controls sprite appearance based on sprite,
1383 // and handles jump event.
update()1384 void Entity::update()
1385 {
1386 spr1 = 0; spr2 = NO_SPRITE;
1387 Object::update();
1388
1389 if (type == ENT_PLAYER)
1390 {
1391 wpn[currWeapon].update();
1392
1393 if (!augmented)
1394 { if (currWeapon != WPN_GRENADE) wpn[WPN_GRENADE].update(); }
1395
1396 if (currWeapon > 0)
1397 { spr2 = 4; spr2Anim.set(1, 0, 0); }
1398 }
1399
1400 if (type == ENT_TURRET1 || type == ENT_SLAVE_TURRET || type == ENT_ARMED_DRONE || type == ENT_HUNTER || type == ENT_GLADIATOR)
1401 wpn[0].update();
1402
1403 if (type == ENT_HUNTER)
1404 wpn[1].update();
1405
1406 if (active && !fall)
1407 {
1408 if (type == ENT_PLAYER)
1409 setPlayerAnimations();
1410 else
1411 {
1412 if (ev.getFB() > 0 || ev.getStrafe() != 0)
1413 {
1414 if (objType[type].numModels)
1415 { if (!(Stunned() || Dying())) setAnimation((type != ENT_MIB) ? MA_WALK : MA_RUN, ANIM_BASE); }
1416 else
1417 { spr1Anim.setDirection(ANIM_FWD); spr2Anim.setDirection(ANIM_FWD); }
1418 }
1419 else if (ev.getFB() < 0 || ev.getStrafe() != 0)
1420 { spr1Anim.setDirection(ANIM_REV); spr2Anim.setDirection(ANIM_REV); }
1421 else
1422 {
1423 if (objType[type].numModels)
1424 { if (!(Stunned() || Dying())) setAnimation(MA_STAND, ANIM_BASE); }
1425 else
1426 { spr1Anim.freezeAtFrame(0); spr2Anim.freezeAtFrame(0); }
1427 }
1428 }
1429
1430 if (!ev.getJump() && jumpPause == 0)
1431 jumpReady = true;
1432
1433 if (h == 0 && !jumping && jumpReady && ev.getJump() && jumpPause == 0)
1434 {
1435 hvel = objType[type].jumpVel;
1436 jumping = true;
1437 jumpReady = false;
1438 jumpSpeed = vel.mag()*0.75 * ev.getFB();
1439 jumpStrafe = vel.mag()*0.75 * ev.getStrafe();
1440 jumpHeading = heading;
1441 playSound(SND_JUMP, 4);
1442
1443 if (type == ENT_PLAYER)
1444 {
1445 if (currWeapon == WPN_NONE || currWeapon == WPN_GRENADE)
1446 model[1].setAnimation(MA_PT_RUN, ANIM_TRANSIENT);
1447
1448 model[0].setAnimation(MA_PL_JUMP, ANIM_TRANSIENT);
1449 }
1450 }
1451 if (hitGround)
1452 {
1453 jumpPause = JUMP_PAUSE_TIME;
1454 hvel = 0;
1455 h = 0;
1456 jumping = false;
1457 hitGround = false;
1458 playSound(SND_LAND, 4);
1459 }
1460
1461 if (ev.getSwitchWpn() && !throwGrenade)
1462 {
1463 if (switchReady)
1464 { selectNextWeapon(); switchReady = false; }
1465 }
1466 else
1467 switchReady = true;
1468
1469 if (type == ENT_PLAYER)
1470 {
1471 if (ev.getFire() && !Dying())
1472 {
1473 if (currWeapon == WPN_GRENADE)
1474 {
1475 if (wpn[currWeapon].Ready() && !throwGrenade)
1476 {
1477 throwGrenade = true;
1478 grenadeThrowTimer = 0.5;
1479 model[1].setAnimation(MA_PT_GRENADE, ANIM_TRANSIENT);
1480 }
1481 }
1482 else
1483 {
1484 if (wpn[currWeapon].fire(pos.c[X]+(wpnPos[currWeapon].c[X]*cos(lookAngle-PIOVER2))+
1485 (wpnPos[currWeapon].c[Y]*cos(lookAngle)),
1486 pos.c[Y]+(wpnPos[currWeapon].c[X]*sin(lookAngle-PIOVER2))+
1487 (wpnPos[currWeapon].c[Y]*sin(lookAngle)), lookAngle, 4.5))
1488 {
1489 switch(currWeapon)
1490 {
1491 case WPN_RIFLE: case WPN_SHOTGUN:
1492 model[1].setAnimation(MA_PT_RIFLESHOOT, ANIM_TRANSIENT);
1493 break;
1494 case WPN_HEPA:
1495 model[1].setAnimation(MA_PT_HEPASHOOT, ANIM_TRANSIENT);
1496 break;
1497 case WPN_NONE:
1498 if (augmented)
1499 model[1].setAnimation(MA_PT_PALMSHOOT, ANIM_TRANSIENT);
1500 break;
1501
1502 }
1503 }
1504 }
1505 }
1506 else
1507 wpn[currWeapon].releaseTrigger(pos.c[X]+(wpnPos[currWeapon].c[X]*cos(lookAngle-PIOVER2))+
1508 (wpnPos[currWeapon].c[Y]*cos(lookAngle)),
1509 pos.c[Y]+(wpnPos[currWeapon].c[X]*sin(lookAngle-PIOVER2))+
1510 (wpnPos[currWeapon].c[Y]*sin(lookAngle)), lookAngle, 4.5);
1511
1512 if (!augmented)
1513 {
1514 if (ev.getGrenade() && !Dying() && currWeapon != WPN_HEPA && currWeapon != WPN_NONE && currWeapon != WPN_ESG)
1515 {
1516 if (wpn[WPN_GRENADE].Ready() && !throwGrenade)
1517 {
1518 throwGrenade = true;
1519 grenadeThrowTimer = 0.5;
1520
1521 switch(currWeapon)
1522 {
1523 case WPN_RIFLE: case WPN_SHOTGUN: case WPN_LASER:
1524 model[1].setAnimation(MA_PT_RIFLEGRENADE, ANIM_TRANSIENT);
1525 break;
1526 default:
1527 model[1].setAnimation(MA_PT_GRENADE, ANIM_TRANSIENT);
1528 }
1529 }
1530 }
1531 else
1532 wpn[WPN_GRENADE].releaseTrigger(0, 0, 0, 0);
1533
1534 if (throwGrenade)
1535 {
1536 grenadeThrowTimer -= timer.dT();
1537 if (grenadeThrowTimer <= 0)
1538 {
1539 int hand = (currWeapon == WPN_GRENADE) ? 1 : -1;
1540 wpn[WPN_GRENADE].fire(pos.c[X]+(wpnPos[WPN_GRENADE].c[X]*hand*cos(lookAngle-PIOVER2))+
1541 (wpnPos[WPN_GRENADE].c[Y]*cos(lookAngle)),
1542 pos.c[Y]+(wpnPos[WPN_GRENADE].c[X]*hand*sin(lookAngle-PIOVER2))+
1543 (wpnPos[WPN_GRENADE].c[Y]*sin(lookAngle)), lookAngle, 4.5);
1544 grenadeThrowTimer = 0;
1545 throwGrenade = false;
1546 setMessage("Grenades: %d", wpn[WPN_GRENADE].ReserveAmmo());
1547 }
1548 }
1549 }
1550
1551 if (ev.getReload())
1552 wpn[currWeapon].reload();
1553
1554 if (wpn[currWeapon].isReloading() && !(Dying() || Stunned()))
1555 {
1556 switch(currWeapon)
1557 {
1558 case WPN_RIFLE: case WPN_SHOTGUN: case WPN_LASER:
1559 model[1].setAnimation(MA_PT_RIFLERELOAD, ANIM_TRANSIENT);
1560 break;
1561 case WPN_HEPA: case WPN_ESG:
1562 model[1].setAnimation(MA_PT_HEPARELOAD, ANIM_TRANSIENT);
1563 break;
1564 }
1565 }
1566 }
1567
1568 if (type == ENT_TURRET1 || type == ENT_SLAVE_TURRET || type == ENT_ARMED_DRONE || type == ENT_HUNTER || type == ENT_GLADIATOR)
1569 {
1570 if (ev.getFire())
1571 {
1572 wpn[0].fire(pos.c[X]+(wpnPos[0].c[X]*cos(lookAngle-PIOVER2))+
1573 (wpnPos[0].c[Y]*cos(lookAngle)),
1574 pos.c[Y]+(wpnPos[0].c[X]*sin(lookAngle-PIOVER2))+
1575 (wpnPos[0].c[Y]*sin(lookAngle)), lookAngle, 0.5);
1576 }
1577 else
1578 wpn[0].releaseTrigger(0,0,0,0);
1579
1580 if (ev.getReload())
1581 wpn[0].reload();
1582 }
1583
1584 if (type == ENT_HUNTER)
1585 {
1586 if (ev.getFire())
1587 {
1588 wpn[1].fire(pos.c[X]+(wpnPos[1].c[X]*cos(lookAngle-PIOVER2))+
1589 (wpnPos[1].c[Y]*cos(lookAngle)),
1590 pos.c[Y]+(wpnPos[1].c[X]*sin(lookAngle-PIOVER2))+
1591 (wpnPos[1].c[Y]*sin(lookAngle)), lookAngle, 0.5);
1592 }
1593 else
1594 wpn[1].releaseTrigger(0,0,0,0);
1595
1596 if (ev.getReload())
1597 wpn[1].reload();
1598 }
1599 }
1600 }
1601
selectNextWeapon()1602 void Entity::selectNextWeapon()
1603 {
1604 currWeapon++;
1605 if (currWeapon >= NUM_PLAYER_WEAPONS)
1606 currWeapon = 0;
1607 else if (!HAS_WEAPON(weapons, currWeapon))
1608 selectNextWeapon();
1609 else
1610 playSound(SND_CHANGE_WEAPON, 7);
1611 }
1612
givePowerup(int idx)1613 void Entity::givePowerup(int idx)
1614 {
1615 bool pickedUp = true;
1616 int iAmt;
1617 float fAmt;
1618 int snd = 1;
1619 switch(currLevelObjs[idx]->Type())
1620 {
1621 case POWERUP_HEALTH:
1622 if ((fAmt = changeHealthAmt(POWERUP_HEALTH_AMOUNT)) > 0)
1623 {
1624 setMessage("Health +%0.0f", fAmt);
1625 snd = 2;
1626 }
1627 else
1628 {
1629 setMessage("Health full");
1630 pickedUp = false;
1631 }
1632 break;
1633 case POWERUP_ENERGY_CLIP:
1634 addAmmoPowerup(WPN_RIFLE, "Rifle ammo +%d", "Rifle ammo full");
1635 break;
1636 case POWERUP_SHOTGUN_AMMO:
1637 addAmmoPowerup(WPN_SHOTGUN, "Shotgun ammo +%d", "Shotgun ammo full");
1638 break;
1639 case POWERUP_HEPA_CLIP:
1640 addAmmoPowerup(WPN_HEPA, "Positron cells +%d", "H.E.P.A. ammo full");
1641 break;
1642 case POWERUP_LASER_CELL:
1643 addAmmoPowerup(WPN_LASER, "Laser cells +%d", "Laser ammo full");
1644 break;
1645 case POWERUP_ESG_BATTERY:
1646 addAmmoPowerup(WPN_ESG, "ESG battery +%d", "ESG ammo full");
1647 break;
1648
1649 case POWERUP_KEY1:
1650 case POWERUP_KEY2:
1651 case POWERUP_KEY3:
1652 giveKey(currLevelObjs[idx]->Type() - POWERUP_KEY1 + 1);
1653 setMessage("Key %d", currLevelObjs[idx]->Type() - POWERUP_KEY1 + 1);
1654 break;
1655
1656 case WEAPON_RIFLE:
1657 addWeaponSwitch(WPN_RIFLE, "Energy rifle", "Rifle ammo full");
1658 break;
1659 case WEAPON_SHOTGUN:
1660 addWeaponSwitch(WPN_SHOTGUN, "Shotgun", "Shotgun ammo full");
1661 break;
1662 case WEAPON_HEPA:
1663 addWeaponSwitch(WPN_HEPA, "H.E.P.A.", "H.E.P.A. ammo full");
1664 break;
1665 case POWERUP_GRENADE:
1666 addWeapon(WPN_GRENADE, "Frag grenade", "Grenades full");
1667 break;
1668 case WEAPON_RED_LASER:
1669 addWeaponSwitch(WPN_LASER, "Laser", "Laser ammo full");
1670 break;
1671 case WEAPON_ESG:
1672 addWeaponSwitch(WPN_ESG, "EMP Shock Gen", "ESG ammo full");
1673 break;
1674
1675 }
1676
1677 if (pickedUp)
1678 {
1679 currLevelObjs[idx]->kill(true);
1680
1681 if (snd == 1) playSound(SND_PICKUP_ITEM, 7);
1682 else if (snd == 2) playSound(SND_PICKUP_HEALTH, 7);
1683 }
1684 }
1685
weaponState(int wp)1686 char Entity::weaponState(int wp)
1687 {
1688 if (!HAS_WEAPON(weapons, wp))
1689 return ' ';
1690 else
1691 return '1'+wp;
1692 }
1693
keyState(int k)1694 char Entity::keyState(int k)
1695 {
1696 if (!HAS_KEY(keys, k))
1697 return ' ';
1698 else
1699 return '?';
1700 }
1701
launch(int typ,float x,float y,float height,float head,float alph,int sprite)1702 void Particle::launch(int typ, float x, float y, float height, float head, float alph, int sprite)
1703 {
1704 Object::set(typ, x, y, height, head, 0, 0, 0, 0);
1705 color.white();
1706 pType = typ - PART_PARTICLE;
1707 alpha = alph;
1708 active = true;
1709 rotation = head;
1710 spr1 = sprite;
1711 additionalSpeed = 0;
1712
1713 health += frand()*partType[pType].maxRandAddedToLife;
1714
1715 speed = objType[type].maxVel + frand()*partType[pType].maxRandAddedToSpeed;
1716
1717 hvel = objType[type].jumpVel + frand()*partType[pType].maxRandAddedToJumpSpeed;
1718
1719 rotVel = partType[pType].minRotVel + frand()*partType[pType].maxRandAddedToRotVel;
1720 fadeSpeed = partType[pType].minFadeSpeed + frand()*partType[pType].maxRandAddedToFadeSpeed;
1721
1722 anim.set(partType[pType].animNumFrames, partType[pType].animDelay + frand()*partType[pType].maxRandAddedToAnimDelay, partType[pType].animLoop);
1723 anim.setDirection(1);
1724 }
1725
launch(int typ,float x,float y,float height,float head,float alph,int sprite,float r,float g,float b)1726 void Particle::launch(int typ, float x, float y, float height, float head, float alph, int sprite, float r, float g, float b)
1727 {
1728 launch(typ, x, y, height, head, alph, sprite);
1729 color.set(r, g, b);
1730 }
1731
1732
reset()1733 void Particle::reset()
1734 {
1735 Object::reset();
1736 bounceCount = 0;
1737 scale = 1.0;
1738 }
1739
drawShadow()1740 void Particle::drawShadow()
1741 {
1742 if (active && objType[type].hasShadow && h >= 0)
1743 {
1744 glPushMatrix();
1745 // translate ever so slightly above the ground to avoid being
1746 // screwed over by the depth test
1747 glTranslatef(pos.c[X], pos.c[Y], 0.01);
1748 glRotatef(todeg(heading), 0.0, 0.0, 1.0);
1749 glEnable(GL_TEXTURE_2D);
1750 glColor4f(1.0, 1.0, 1.0, alpha);
1751 texQuadSize(TEX_SHADOW, objType[type].spriteSize.c[X]*scale, objType[type].spriteSize.c[Y]*scale);
1752 glDisable(GL_TEXTURE_2D);
1753 glPopMatrix();
1754 }
1755 }
1756
draw()1757 void Particle::draw()
1758 {
1759 if (active)
1760 {
1761 glPushMatrix();
1762
1763 glTranslatef(pos.c[X], pos.c[Y], 0);
1764 glRotatef(todeg(rotation), 0.0, 0.0, 1.0);
1765
1766 glEnable(GL_TEXTURE_2D);
1767 glColor4f(color.r, color.g, color.b, alpha);
1768
1769 // translate ever so slightly above the ground to avoid being
1770 // screwed over by the depth test
1771 glTranslatef(0.0, 0.0, 0.01);
1772 if (objType[type].hasShadow) {texQuadSize(TEX_SHADOW, objType[type].spriteSize.c[X]*scale, objType[type].spriteSize.c[Y]*scale); }
1773
1774 glTranslatef(0.0, 0.0, h);
1775
1776 if (partType[pType].glow)
1777 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
1778
1779 if (partType[pType].animNumFrames <= 1)
1780 {
1781 if (spr1 != NO_SPRITE) {texQuadSize(objType[type].sprites[spr1], objType[type].spriteSize.c[X]*scale, objType[type].spriteSize.c[Y]*scale); }
1782 glTranslatef(0.0, 0.0, 0.01);
1783 if (spr2 != NO_SPRITE) {texQuadSize(objType[type].sprites[spr2], objType[type].spriteSize.c[X]*scale, objType[type].spriteSize.c[Y]*scale); }
1784 }
1785 else
1786 {
1787 if (spr1 != NO_SPRITE) {texQuadClip(objType[type].sprites[spr1], anim.Frame(), 1.0/anim.NumFrames());}
1788 glTranslatef(0.0, 0.0, 0.01);
1789 if (spr2 != NO_SPRITE) {texQuadClip(objType[type].sprites[spr2], anim.Frame(), 1.0/anim.NumFrames());}
1790 }
1791 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1792
1793 glDisable(GL_TEXTURE_2D);
1794 glPopMatrix();
1795 }
1796 }
1797
levelCollisionDetection()1798 void Particle::levelCollisionDetection()
1799 {
1800 if (currLevel.inGrid(pos))
1801 {
1802 int cell = currLevel.cellNumber(pos);
1803 if (objType[type].collide)
1804 {
1805 Vector2D intersect(0,0);
1806 Wall *closestHitWall = wallCollisionDetect(cell, &intersect);
1807
1808 if (closestHitWall)
1809 {
1810 // Bullet ricochets if ricochet was specified.
1811 if (closestHitWall->CollFlags() & RICOCHET_BULLET)
1812 {
1813 vel -= closestHitWall->normal * (closestHitWall->normal * vel) * 2;
1814 heading = atan2(vel.c[Y], vel.c[X]);
1815 rotation = heading;
1816 }
1817 else
1818 switch (partType[pType].hitBehavior)
1819 {
1820 case HIT_DIE:
1821 health = 0; break;
1822 case HIT_STOP:
1823 vel.set(0, 0); break;
1824 case HIT_BOUNCE:
1825 // Snell's law, woot.
1826 vel -= closestHitWall->normal * (closestHitWall->normal * vel) * 2;
1827 heading = atan2(vel.c[Y], vel.c[X]);
1828 rotation = heading;
1829 }
1830
1831 if (IS_BULLET(type))
1832 {
1833 closestHitWall->spewParticles(intersect+closestHitWall->normal*0.25);
1834 closestHitWall->handleTrigger(true, false);
1835 }
1836 }
1837 }
1838 if (!floorCollisionDetect(cell, NULL))
1839 fall = true;
1840 }
1841 }
1842
1843 // Updates a Particle.
update()1844 void Particle::update()
1845 {
1846 spr2 = NO_SPRITE;
1847
1848 if (active)
1849 {
1850 if (!currLevel.inGrid(pos))
1851 { active = false; return; }
1852
1853 ev.setFB(1);
1854
1855 speed += partType[pType].accel * timer.dT();
1856
1857 if (speed < 0)
1858 speed = 0;
1859
1860 vel.set(cos(heading)*speed, sin(heading)*speed);
1861
1862 levelCollisionDetection();
1863
1864 pos += vel * timer.dT();
1865
1866 if (type == ESG_EXPLODER)
1867 {
1868 pos.c[X] = PLAYER_OBJECT->PosX()+(wpnType[WPN_ESG].spriteSize.c[X]/3.5*cos(PLAYER_OBJECT->LookAngle()));
1869 pos.c[Y] = PLAYER_OBJECT->PosY()+(wpnType[WPN_ESG].spriteSize.c[X]/3.5*sin(PLAYER_OBJECT->LookAngle()));
1870 }
1871
1872 // Object is affected by gravity if it is not on the ground
1873 h += hvel * timer.dT();
1874
1875 if (objType[type].gravAffect)
1876 {
1877 if (h != 0)
1878 hvel -= ACCEL_GRAVITY * timer.dT();
1879 if (h < 0 && !fall)
1880 hitGround = true;
1881 }
1882
1883 if (type == BLT_HEPA)
1884 {
1885 launchParticle(PART_HEPA_TRAIL,
1886 pos.c[X], pos.c[Y], 0.5, torad(rand()%360),1.0,0);
1887 }
1888 else if (type == BLT_GRENADE || type == BLT_SHRAPNEL)
1889 {
1890 launchParticle(PART_GRENADE_TRAIL,
1891 pos.c[X], pos.c[Y], h, -rotation,1.0,0);
1892 }
1893
1894 if (partType[pType].startFade == 0 || health <= partType[pType].startFade)
1895 alpha -= fadeSpeed * timer.dT();
1896
1897 if (alpha <= 0)
1898 health = 0;
1899
1900 rotation += rotVel * timer.dT();
1901
1902 if (partType[pType].scaleStop == 0 ||
1903 (partType[pType].scaleStop > 0 && scale <= partType[pType].scaleStop))
1904 scale += partType[pType].scaleSpeed * timer.dT();
1905
1906 if (scale <= 0)
1907 health = 0;
1908
1909 anim.update();
1910
1911 if (anim.isDone())
1912 health = 0;
1913
1914 health -= 1 * timer.dT();
1915
1916 if (health != INF && health <= 0)
1917 { active = false; destroyMe = true; }
1918
1919 if (hitGround)
1920 {
1921 hitGround = false;
1922 h = 0.00001;
1923 hvel = -hvel*0.5;
1924
1925 if (type == BLT_GRENADE)
1926 playSound(SND_GRENADE_BOUNCE, 1);
1927 }
1928 }
1929 }
1930
update()1931 void Bullet::update()
1932 {
1933 if (frameDelay > 0)
1934 frameDelay--;
1935
1936 Particle::update();
1937 }
1938
LaserBeam(Vector2D np1,Vector2D np2)1939 LaserBeam::LaserBeam(Vector2D np1, Vector2D np2)
1940 {
1941 p1 = np1; p2 = np2;
1942 angle = atan2(p2.c[Y]-p1.c[Y], p2.c[X]-p1.c[X]);
1943 length = (p2-p1).mag();
1944 }
1945
launch(int typ,float x,float y,float height,float head)1946 void Laser::launch(int typ, float x, float y, float height, float head)
1947 {
1948 h = height;
1949 alpha = 1.0;
1950 type = typ;
1951 beams.clear();
1952
1953 float distLeft = lasType[type].maxRange;
1954 int bounces = 0;
1955 Vector2D startPos(x-cos(head)*wpnType[WPN_LASER].spriteSize.c[X], y-sin(head)*wpnType[WPN_LASER].spriteSize.c[X]);
1956 while (distLeft > 0.001 && bounces < lasType[type].maxBounces)
1957 {
1958 Vector2D endPos;
1959 Wall *closestHitWall = wallCollisionDetect(startPos, head, distLeft, &endPos);
1960
1961 // If we didn't hit a wall, the beam keeps going for the remainder of its distance
1962 if (closestHitWall == NULL)
1963 endPos = startPos + Vector2D(cos(head)*distLeft, sin(head)*distLeft);
1964 else
1965 {
1966 closestHitWall->handleTrigger(true, false);
1967 Vector2D dir = endPos-startPos; dir.normalize();
1968
1969 dir -= closestHitWall->normal *
1970 (closestHitWall->normal * dir) * 2;
1971 head = atan2(dir.c[Y], dir.c[X]);
1972
1973 endPos += closestHitWall->normal * 0.1;
1974
1975 bounces++;
1976 }
1977
1978 distLeft -= (endPos-startPos).mag();
1979 beams.push_back(LaserBeam(startPos, endPos));
1980 startPos.set(endPos.c[X], endPos.c[Y]);
1981 }
1982 beams[0].p1.set(x, y);
1983
1984 live = 2;
1985 active = true;
1986 destroyMe = false;
1987 }
1988
wallCollisionDetect(Vector2D pos,float head,float distLeft,Vector2D * inter)1989 Wall* Laser::wallCollisionDetect(Vector2D pos, float head, float distLeft, Vector2D *inter)
1990 {
1991 Vector2D intersect;
1992 int numWalls = currLevel.wall.size();
1993 Wall *currWall;
1994
1995 Wall *closestHitWall = NULL;
1996 Vector2D closestHitIntersect;
1997 float closestHitDistance = 1e37;
1998
1999 for (int i = 0; i < numWalls + 4*currLevel.door.size(); i++)
2000 {
2001 // A pointer allows us to combine collision detection with
2002 // walls and doors into one loop.
2003 if (i < numWalls)
2004 currWall = &currLevel.wall[i];
2005 else
2006 currWall = &currLevel.door[(i-numWalls)/4].sides[(i-numWalls)%4];
2007
2008 if (currWall->CollFlags() & COLLIDE_BULLET)
2009 {
2010 Vector2D offset(cos(head)*distLeft, sin(head)*distLeft);
2011
2012 if (currWall->intersection(pos, pos+offset, &intersect))
2013 {
2014 if ((intersect-pos).mag() < closestHitDistance)
2015 {
2016 closestHitDistance = (intersect-pos).mag();
2017 closestHitWall = currWall;
2018 closestHitIntersect.set(intersect.c[X],intersect.c[Y]);
2019 }
2020 }
2021 else
2022 currWall->renewTrigger();
2023 }
2024 }
2025 if (closestHitWall)
2026 {
2027 if (inter)
2028 inter->set(closestHitIntersect.c[X], closestHitIntersect.c[Y]);
2029
2030 return closestHitWall;
2031 }
2032
2033 return NULL;
2034 }
2035
draw()2036 void Laser::draw()
2037 {
2038 if (active)
2039 {
2040 glPushMatrix();
2041
2042 glEnable(GL_TEXTURE_2D);
2043 glColor4f(1.0, 1.0, 1.0, alpha);
2044 glTranslatef(0.0, 0.0, h);
2045
2046 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
2047
2048 glBindTexture(GL_TEXTURE_2D, textures[lasType[type].sprite]);
2049 for (int i = 0; i < beams.size(); i++)
2050 {
2051 glPushMatrix();
2052 glTranslatef(beams[i].p1.c[X], beams[i].p1.c[Y], 0.0);
2053 glRotatef(todeg(beams[i].angle), 0.0, 0.0, 1.0);
2054 glBegin(GL_QUADS);
2055 glTexCoord2f(0.05, 1);
2056 glVertex2f(0, -lasType[type].width*0.5*alpha);
2057 glTexCoord2f(1, 1);
2058 glVertex2f(beams[i].length, -lasType[type].width*0.5*alpha);
2059 glTexCoord2f(1, 0);
2060 glVertex2f(beams[i].length, lasType[type].width*0.5*alpha);
2061 glTexCoord2f(0.05, 0);
2062 glVertex2f(0, lasType[type].width*0.5*alpha);
2063 glEnd();
2064 glPopMatrix();
2065 }
2066
2067
2068 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2069 glPopMatrix();
2070 glDisable(GL_TEXTURE_2D);
2071 }
2072
2073 }
2074
update()2075 void Laser::update()
2076 {
2077 if (active)
2078 {
2079 if (live > 0)
2080 live--;
2081
2082 alpha -= lasType[type].fadeSpeed * timer.dT();
2083
2084 if (alpha <= 0)
2085 {
2086 alpha = 0;
2087 active = false;
2088 destroyMe = true;
2089 }
2090 }
2091 }
2092
2093 // Defines the type of all objects
initObjectTypes()2094 void initObjectTypes()
2095 {
2096 for (int i = 0; i < NUM_OBJ_TYPES; i++)
2097 switch(i)
2098 {
2099 // float im, float iw, float ih, float ir, float imv, float ijv, float imtv, float ia, float ita, float hlth, bool grav, bool shad)
2100 case ENT_PLAYER:
2101 objType[i].set(160, 1.5, 6.0, 1.0, 17, 8, torad(150), 25, torad(5000), 100, DEATH_PLAYER, true, true, true);
2102 objType[i].setModels(MDL_PLAYER_LEGS, MDL_PLAYER_TORSO);
2103 break;
2104 case ENT_BOX:
2105 objType[i].set(140, 3.0, 3.0, 2.0, 0, 0, 0, 0, 0, 25, DEATH_WOOD, true, true, true);
2106 objType[i].setModels(MDL_BOX, NO_MODEL);
2107 break;
2108 case ENT_BARREL:
2109 objType[i].set(180, 2.5, 2.5, 1.25, 0, 0, 0, 0, 0, 35, DEATH_EXPLOSION, true, true, true);
2110 objType[i].setModels(MDL_BARREL, NO_MODEL);
2111 break;
2112 case ENT_STEEL_CRATE:
2113 objType[i].set(250, 3.0, 3.0, 2.0, 0, 0, 0, 0, 0, INF, DEATH_NONE, true, true, true);
2114 objType[i].setModels(MDL_STEELBOX, NO_MODEL);
2115 break;
2116
2117 case ENT_UNARMED_GUARD:
2118 objType[i].set(100, 3.0, 3.0, 1.5, 15, 0, torad(150), 25, torad(5000), 50, DEATH_SMALL_EXPLOSION, true, true, true);
2119 objType[i].setModels(MDL_UNARMED_GUARD, NO_MODEL);
2120 break;
2121 case ENT_MIB:
2122 objType[i].set(100, 3.0, 3.0, 1.5, 14, 0, torad(150), 25, torad(5000), 50, DEATH_NONE, true, true, true);
2123 objType[i].setModels(MDL_MIB, NO_MODEL);
2124 break;
2125 case ENT_TURRET1: case ENT_SLAVE_TURRET:
2126 objType[i].set(10000, 5.0, 5.0, 1.5, 0, 0, torad(75), 0, torad(5000), 50, DEATH_ROBOT, true, true, true);
2127 objType[i].setModels(MDL_TURRET, NO_MODEL);
2128 break;
2129 case ENT_ARMED_DRONE:
2130 objType[i].set(200, 4.0, 4.0, 1.25, 13, 0, torad(200), 25, torad(4000), 75, DEATH_ROBOT, true, true, true);
2131 objType[i].setModels(MDL_ARMED_DRONE, NO_MODEL);
2132 break;
2133 case ENT_HUNTER:
2134 objType[i].set(450, 4.0, 4.0, 1.25, 14, 0, torad(200), 25, torad(4000), 100, DEATH_ROBOT, true, true, true);
2135 objType[i].setModels(MDL_HUNTER, NO_MODEL);
2136 break;
2137 case ENT_GLADIATOR:
2138 objType[i].set(700, 4.0, 4.0, 1.5, 8, 0, torad(200), 25, torad(4000), 250, DEATH_GLADIATOR, true, true, true);
2139 objType[i].setModels(MDL_GLADIATOR, NO_MODEL);
2140 break;
2141 case POWERUP_HEALTH:
2142 objType[i].set(5, 1.5, 0.75, 0.5, 0, 0, 0, 0, 0, INF, DEATH_NONE, true, true, true);
2143 objType[i].setSprites(1, TEX_POWERUP_HEALTH);
2144 break;
2145 case POWERUP_ENERGY_CLIP:
2146 objType[i].set(1, 0.5, 1.0, 0.25, 0, 0, 0, 0, 0, INF, DEATH_NONE, true, true, true);
2147 objType[i].setSprites(1, TEX_POWERUP_ENERGY_CLIP);
2148 break;
2149 case POWERUP_SHOTGUN_AMMO:
2150 objType[i].set(1, 1.0, 1.0, 0.5, 0, 0, 0, 0, 0, INF, DEATH_NONE, true, true, true);
2151 objType[i].setSprites(1, TEX_POWERUP_SHOTGUN_AMMO);
2152 break;
2153 case POWERUP_HEPA_CLIP:
2154 objType[i].set(1, 0.5, 1.0, 0.25, 0, 0, 0, 0, 0, INF, DEATH_NONE, true, true, true);
2155 objType[i].setSprites(1, TEX_POWERUP_HEPA_CLIP);
2156 break;
2157 case POWERUP_LASER_CELL:
2158 objType[i].set(1, 1.5, 0.45, 0.3, 0, 0, 0, 0, 0, INF, DEATH_NONE, true, true, true);
2159 objType[i].setSprites(1, TEX_POWERUP_LASER_CELL);
2160 break;
2161 case POWERUP_ESG_BATTERY:
2162 objType[i].set(1, 1.0, 1.0, 0.5, 0, 0, 0, 0, 0, INF, DEATH_NONE, true, true, true);
2163 objType[i].setSprites(1, TEX_POWERUP_ESG_BATTERY);
2164 break;
2165 case POWERUP_KEY1:
2166 objType[i].set(1, 0.5, 1.0, 0.25, 0, 0, 0, 0, 0, INF, DEATH_NONE, true, true, true);
2167 objType[i].setSprites(1, TEX_POWERUP_KEY1);
2168 break;
2169 case POWERUP_KEY2:
2170 objType[i].set(1, 0.5, 1.0, 0.25, 0, 0, 0, 0, 0, INF, DEATH_NONE, true, true, true);
2171 objType[i].setSprites(1, TEX_POWERUP_KEY2);
2172 break;
2173 case POWERUP_KEY3:
2174 objType[i].set(1, 0.5, 1.0, 0.25, 0, 0, 0, 0, 0, INF, DEATH_NONE, true, true, true);
2175 objType[i].setSprites(1, TEX_POWERUP_KEY3);
2176 break;
2177 case WEAPON_RIFLE:
2178 objType[i].set(10, 4.0, 1.0, 1.0, 0, 0, 0, 0, 0, INF, DEATH_NONE, true, true, true);
2179 objType[i].setSprites(1, TEX_WEAPON_RIFLE);
2180 break;
2181 case WEAPON_SHOTGUN:
2182 objType[i].set(10, 4.0, 1.0, 1.0, 0, 0, 0, 0, 0, INF, DEATH_NONE, true, true, true);
2183 objType[i].setSprites(1, TEX_WEAPON_SHOTGUN);
2184 break;
2185 case WEAPON_HEPA:
2186 objType[i].set(10, 4.0, 2.0, 1.0, 0, 0, 0, 0, 0, INF, DEATH_NONE, true, true, true);
2187 objType[i].setSprites(1, TEX_WEAPON_HEPA);
2188 break;
2189 case POWERUP_GRENADE:
2190 objType[i].set(1, 0.75, 0.75, 0.25, 0, 0, 0, 0, 0, INF, DEATH_NONE, true, true, true);
2191 objType[i].setSprites(1, TEX_WPN_GRENADE);
2192 break;
2193 case WEAPON_RED_LASER:
2194 objType[i].set(10, 4.0, 2.0, 1.0, 0, 0, 0, 0, 0, INF, DEATH_NONE, true, true, true);
2195 objType[i].setSprites(1, TEX_WEAPON_LASER);
2196 break;
2197 case WEAPON_ESG:
2198 objType[i].set(10, 4.0, 4.0, 1.0, 0, 0, 0, 0, 0, INF, DEATH_NONE, true, true, true);
2199 objType[i].setSprites(1, TEX_WEAPON_ESG);
2200 break;
2201
2202 case EXPLODER:
2203 objType[i].set(1000000, 0, 0, 0, 0, 0, 0, 0, 0, INF, DEATH_EXPLOSION, false, false, false);
2204 break;
2205
2206 case PART_PARTICLE:
2207 objType[i].set(0.01, 0.25, 0.25, 0.25, 1, 0, 0, 100, 0, 2.0, DEATH_NONE, false, true, false);
2208 objType[i].setSprites(1, TEX_PARTICLE);
2209 break;
2210 case PART_PARTICLE_BLOOD:
2211 objType[i].set(0.01, 0.25, 0.25, 0.25, 1, 0, 0, 100, 0, 4, DEATH_NONE, false, true, false);
2212 objType[i].setSprites(1, TEX_PARTICLE_BLOOD);
2213 break;
2214 case PART_PARTICLE_SLIME:
2215 objType[i].set(0.01, 0.25, 0.25, 0.25, 1, 0, 0, 100, 0, 1, DEATH_NONE, false, true, false);
2216 objType[i].setSprites(1, TEX_PARTICLE_SLIME);
2217 break;
2218 case PART_PARTICLE_SPARK:
2219 objType[i].set(0.01, 1.5, 0.125, 0.25, 15, 0, 0, 100, 0, 4, DEATH_NONE, false, true, false);
2220 objType[i].setSprites(1, TEX_PARTICLE_SPARK);
2221 break;
2222 case PART_PARTICLE_ENERGY:
2223 objType[i].set(0.01, 0.75, 0.75, 0.75, 0.5, 0, 0, 100, 0, 1, DEATH_NONE, false, true, false);
2224 objType[i].setSprites(1, TEX_PARTICLE_ENERGY);
2225 break;
2226 case PART_PARTICLE_WOOD:
2227 objType[i].set(0.01, 1.0, 0.5, 0.5, 1, 10, 0, 100, 0, 2, DEATH_NONE, false, true, true);
2228 objType[i].setSprites(1, TEX_PARTICLE_WOOD);
2229 break;
2230 case PART_PARTICLE_METAL:
2231 objType[i].set(0.01, 1.0, 0.25, 0.5,3, 20, 0, 100, 0, 2, DEATH_NONE, true, true, true);
2232 objType[i].setSprites(1, TEX_PARTICLE_METAL);
2233 break;
2234 case PART_MF_RIFLE:
2235 objType[i].set(0.01, 1.0, 0.5, 1.0, 0, 0, 0, 0, 0, 0.0625, DEATH_NONE, false, false, false);
2236 objType[i].setSprites(1, TEX_MF_RIFLE);
2237 break;
2238 case PART_MF_SHOTGUN:
2239 objType[i].set(0.01, 1.0, 1.0, 1.0, 0, 0, 0, 0, 0, 0.0625, DEATH_NONE, false, false, false);
2240 objType[i].setSprites(1, TEX_MF_SHOTGUN);
2241 break;
2242 case BLT_RIFLE:
2243 objType[i].set(0.0044, 2.0, 0.15, 0.5, 150, 0, 0, 0, 0, 1.0, DEATH_NONE, true, false, false);
2244 objType[i].setSprites(1, TEX_BLT_RIFLE);
2245 break;
2246 case BLT_SHOTGUN:
2247 objType[i].set(0.0044, 0.5, 0.15, 0.5, 150, 0, 0, 0, 0, 1.0, DEATH_NONE, true, false, false);
2248 objType[i].setSprites(1, TEX_BLT_RIFLE);
2249 break;
2250 case BLT_HEPA:
2251 objType[i].set(0.0001, 1.0, 1.0, 0.5, 20, 0, 0, 0, 0, 2.0, DEATH_HEPA_EXPLOSION, true, false, false);
2252 objType[i].setSprites(1, TEX_PARTICLE_ENERGY);
2253 break;
2254 case BLT_GRENADE:
2255 objType[i].set(1, 0.65, 0.65, 0.3, 20, 0, 0, 0, 0, 3.0, DEATH_GRENADE_EXPLOSION, true, true, true);
2256 objType[i].setSprites(1, TEX_BLT_GRENADE);
2257 break;
2258 case BLT_SHRAPNEL:
2259 objType[i].set(1, 0.5, 0.5, 0.25, 100, 0, 0, 0, 0, .35, DEATH_NONE, true, false, false);
2260 objType[i].setSprites(1, TEX_BLT_SHRAPNEL);
2261 break;
2262 case BLT_BLASTER:
2263 objType[i].set(0.0044, 6.0, 2.0, 0.5, 110, 0, 0, 0, 0, 1.0, DEATH_NONE, true, false, false);
2264 objType[i].setSprites(1, TEX_BLT_BLASTER);
2265 break;
2266 case PART_RIFLE_SHELL:
2267 objType[i].set(0.0044, 0.03125, 0.125, 0.5, 0.5, 10, 0, 0.1, 0, 2.0, DEATH_NONE, false, true, true);
2268 objType[i].setSprites(1, TEX_RIFLE_SHELL);
2269 break;
2270 case PART_SHOTGUN_SHELL:
2271 objType[i].set(0.0044, 0.125, 0.25, 0.5, 0.5, 5, 0, 0.1, 0, 2.0, DEATH_NONE, false, true, true);
2272 objType[i].setSprites(1, TEX_SHOTGUN_SHELL);
2273 break;
2274 case PART_EXPLOSION:
2275 objType[i].set(0, 8.0, 8.0, 8.0, 0, 0, 0, 0, 0, 0.4, DEATH_NONE, false, false, false);
2276 objType[i].setSprites(4, TEX_EXPLOSION1, TEX_EXPLOSION2, TEX_EXPLOSION3, TEX_EXPLOSION4);
2277 break;
2278 case PART_EXPLOSIONPART:
2279 objType[i].set(0, 1.0, 0.5, 0.5, 10, 0, 0, 0, 0, 0.5, DEATH_NONE, false, true, false);
2280 objType[i].setSprites(1, TEX_EXPLOSIONPART);
2281 break;
2282 case PART_SMOKE:
2283 objType[i].set(0, 2.0, 2.0, 2.0, 2, 1, 0, 0, 0, 30, DEATH_NONE, false, false, false);
2284 objType[i].setSprites(1, TEX_SMOKE);
2285 break;
2286 case PART_PARTICLE_GLOW:
2287 objType[i].set(0, 0.4, 0.4, 0.4, 0, 0, 0, 0, 0, 5, DEATH_NONE, false, false, false);
2288 objType[i].setSprites(1, TEX_PARTICLE_GLOW);
2289 break;
2290 case PART_HEPA_TRAIL:
2291 objType[i].set(0, 2.0, 2.0, 1.0, 0, 0, 0, 0, 0, 5, DEATH_NONE, false, false, false);
2292 objType[i].setSprites(1, TEX_PARTICLE_ENERGY);
2293 break;
2294 case PART_HEPA_EXPLOSION:
2295 objType[i].set(0, 4.0, 4.0, 2.0, 0, 0, 0, 0, 0, 5, DEATH_NONE, false, false, false);
2296 objType[i].setSprites(1, TEX_LIGHT);
2297 break;
2298 case PART_HEPA_SPARK:
2299 objType[i].set(0.01, 3.0, 3.0, 0.25, 1, 0, 0, 100, 0, 6, DEATH_NONE, false, true, false);
2300 objType[i].setSprites(1, TEX_PARTICLE_ENERGY);
2301 break;
2302 case PART_GRENADE_TRAIL:
2303 objType[i].set(0.01, 0.125, 0.125, 0.125, 2, 0, 0, 100, 0, 1, DEATH_NONE, false, true, false);
2304 objType[i].setSprites(1, TEX_PARTICLE_SLIME);
2305 break;
2306 case PART_LASER_SPARK1:
2307 objType[i].set(0.01, 1.0, 1.0, 0.25, 0.5, 5, 0, 100, 0, 6, DEATH_NONE, false, false, false);
2308 objType[i].setSprites(1, TEX_LASER_SPARK1);
2309 break;
2310 case PART_LASER_SPARK2:
2311 objType[i].set(0.01, 2.0, 2.0, 1.0, 0.5, 5, 0, 100, 0, 6, DEATH_NONE, false, false, false);
2312 objType[i].setSprites(1, TEX_LASER_SPARK2);
2313 break;
2314 case PART_ESG_SHOCKWAVE:
2315 objType[i].set(0.01, 1.0, 1.0, 2.5, 0, 0, 0, 100, 0, 0.5, DEATH_NONE, false, false, false);
2316 objType[i].setSprites(1, TEX_ESG_SHOCKWAVE);
2317 break;
2318 case PART_BIT:
2319 objType[i].set(0.01, 5.0, 5.0, 1.0, 0, 0, 0, 0, 0, 10, DEATH_NONE, false, false, false);
2320 objType[i].setSprites(1, TEX_BIT);
2321 break;
2322 case ESG_EXPLODER:
2323 objType[i].set(1, 0, 0, 0, 0, 0, 0, 0, 0, 0.75, DEATH_ESG, false, false, false);
2324 break;
2325 }
2326
2327 for (int i = 0; i < NUM_PART_TYPES; i++)
2328 switch(i)
2329 {
2330 // float mrl, float mrs, float mrjs, float rv, float mrrv, float fs, float mrfs, float sf, float ss, float sst, int hb, bool gl, float acc, int anf, float ad, float mrad, int al
2331 case P_PART_PARTICLE: case P_PART_PARTICLE_SLIME: case P_PART_PARTICLE_SPARK:
2332 partType[i].set(0, 5, 25, 0, 0, 0.5, 0.1, 0, 0.0, 0.0, HIT_BOUNCE, true, 0, 1, 0, 0, 0);
2333 break;
2334 case P_PART_PARTICLE_WOOD: case P_PART_PARTICLE_METAL:
2335 partType[i].set(0, 5, 5, -torad(90), torad(180), 2.0, 0.1, 0.5, 0.0, 0.0, HIT_BOUNCE, false, 0, 1, 0, 0, 0);
2336 break;
2337 case P_PART_PARTICLE_BLOOD:
2338 partType[i].set(0, 5, 5, 0, 0, 0.25, 0.1, 0, 0.0, 0.0, HIT_BOUNCE, false, 0, 1, 0, 0, 0);
2339 break;
2340 case P_PART_PARTICLE_ENERGY:
2341 partType[i].set(0, 2, 20, 0, 0, 1.0, 0.1, 0, 0.0, 0.0, HIT_BOUNCE, true, 0, 1, 0, 0, 0);
2342 break;
2343 break;
2344 case P_PART_MF_RIFLE: case P_PART_MF_SHOTGUN:
2345 partType[i].set(0, 0, 0, 0, 0, 4.0, 0, 0, 0.0, 0.0, HIT_NOTHING, false, 0, 1, 0, 0, 0);
2346 break;
2347 case P_BLT_RIFLE: case P_BLT_SHOTGUN:
2348 partType[i].set(0, 0, 0, 0, 0, 0, 0, 0, 0.0, 0.0, HIT_DIE, true, 0, 1, 0, 0, 0);
2349 break;
2350 case P_BLT_BLASTER:
2351 partType[i].set(0, 0, 0, 0, 0, 0, 0, 0, 0.0, 0.0, HIT_DIE, true, 0, 1, 0, 0, 0);
2352 break;
2353 case P_BLT_HEPA:
2354 partType[i].set(0, 0, 0, 0, 0, 0, 0, 0, 0.0, 0.0, HIT_DIE, true, 100, 1, 0, 0, 0);
2355 break;
2356 case P_BLT_SHRAPNEL:
2357 partType[i].set(.5, 50, 0, -torad(360), torad(360), 0, 0, 0, 0.0, 0.0, HIT_BOUNCE, true, 0, 4, 0.25, 0, INF);
2358 break;
2359 case P_BLT_GRENADE:
2360 partType[i].set(0, 0, 20, -torad(360), torad(360), 0, 0, 0, 0.0, 0.0, HIT_BOUNCE, false, -7.5, 2, 0.5, 0, INF);
2361 break;
2362 case P_PART_RIFLE_SHELL: case P_PART_SHOTGUN_SHELL:
2363 partType[i].set(0, 1, 5, -torad(180), 0, 0.5, 0, 0, 0.0, 0.0, HIT_DIE, false, 0, 1, 0, 0, 0);
2364 break;
2365 case P_PART_EXPLOSION:
2366 partType[i].set(0, 0, 0, 0, 0, 4.0, 0, 0.25, 2, 0.0, HIT_NOTHING, true, 0, 1, 0, 0, 0);
2367 break;
2368 case P_PART_EXPLOSIONPART:
2369 partType[i].set(0, 25, 5, 0, 0, 4.0, 0.0, 0.25, 0.0, 0.0, HIT_DIE, true, 0, 1, 0, 0, 0);
2370 break;
2371 case P_PART_SMOKE:
2372 partType[i].set(1, 2, 5, 0, 0, 0.5, 0.0, 0, 2, 0.0, HIT_NOTHING, false, 0, 1, 0, 0, 0);
2373 break;
2374 case P_PART_PARTICLE_GLOW:
2375 partType[i].set(0, 1, 0, 0, 0, 0.9, 0.3, 0, 3.5, 0.0, HIT_NOTHING, true, 0, 1, 0, 0, 0);
2376 break;
2377 case P_PART_HEPA_TRAIL:
2378 partType[i].set(0, 5, 0, 0, 0, 0.5, 0.5, 0, -1, 0.0, HIT_NOTHING, true, 0, 1, 0, 0, 0);
2379 break;
2380 case P_PART_HEPA_EXPLOSION:
2381 partType[i].set(0, 0, 0, 0, 0, 0.5, 0.5, 0, 50, 3.0, HIT_NOTHING, true, 0, 1, 0, 0, 0);
2382 break;
2383 case P_PART_HEPA_SPARK:
2384 partType[i].set(1, 10, 15, 0, 0, 0, 0, 0, -0.5, 0.0, HIT_NOTHING, true, 0, 1, 0, 0, 0);
2385 break;
2386 case P_PART_GRENADE_TRAIL:
2387 partType[i].set(0, 5, 25, 0, 0, 1.0, 0.1, 0, 0.0, 0.0, HIT_BOUNCE, true, 0, 1, 0, 0, 0);
2388 break;
2389 case P_PART_LASER_SPARK1:
2390 partType[i].set(1, 2, 25, 0, 0, 0, 0, 0, -0.5, 0.0, HIT_NOTHING, true, 0, 1, 0, 0, 0);
2391 break;
2392 case P_PART_LASER_SPARK2:
2393 partType[i].set(1, 3, 15, 0, 0, 0, 0, 0, 0, 0.0, HIT_NOTHING, true, 0, 1, 0, 0, 0);
2394 break;
2395 case P_PART_ESG_SHOCKWAVE:
2396 partType[i].set(0, 0, 0, 0, 0, 0, 0, 0, 200.0, 0.0, HIT_NOTHING, true, 0, 1, 0, 0, 0);
2397 break;
2398 case P_PART_BIT:
2399 partType[i].set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0, HIT_NOTHING, false, 0, 2, 0.25, 1.0, INF);
2400 break;
2401 case P_ESG_EXPLODER:
2402 partType[i].set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, HIT_NOTHING, true, 0, 1, 0, 0, 0);
2403 break;
2404 }
2405
2406 for (int i = 0; i < NUM_BLT_TYPES; i++)
2407 switch(i)
2408 {
2409 case B_BLT_RIFLE:
2410 bltType[i].set(5, 260);
2411 break;
2412 case B_BLT_SHOTGUN:
2413 bltType[i].set(6, 400);
2414 break;
2415 case B_BLT_HEPA:
2416 bltType[i].set(10, 1200);
2417 break;
2418 case B_BLT_GRENADE:
2419 bltType[i].set(10, 1200);
2420 break;
2421 case B_BLT_SHRAPNEL:
2422 bltType[i].set(13, 400);
2423 break;
2424 case B_BLT_BLASTER:
2425 bltType[i].set(45, 480);
2426 break;
2427 }
2428
2429 // void set(int spr, float w, float mr, int mb, float dmg, float fs)
2430 for (int i = 0; i < NUM_LASER_TYPES; i++)
2431 switch(i)
2432 {
2433 case L_STANDARD:
2434 lasType[i].set(TEX_BEAM1, 0.5, 200, 7, 40, 0.5);
2435 break;
2436 case L_CHARGED:
2437 lasType[i].set(TEX_BEAM2, 3.0, 100, 1, 240, 0.25);
2438 }
2439 }
2440