]> icculus.org git repositories - btb/d2x.git/blob - main/collide.c
This commit was generated by cvs2svn to compensate for changes in r2,
[btb/d2x.git] / main / collide.c
1 /*
2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
12 */
13
14
15 #ifdef RCS
16 static char rcsid[] = "$Id: collide.c,v 1.1.1.1 2001-01-19 03:30:00 bradleyb Exp $";
17 #endif
18
19 #include <conf.h>
20 #include <string.h>     // for memset
21 #include <stdlib.h>
22 #include <stdio.h>
23
24 #include "rle.h"
25 #include "inferno.h"
26 #include "game.h"
27 #include "gr.h"
28 #include "stdlib.h"
29 #include "bm.h"
30 //#include "error.h"
31 #include "mono.h"
32 #include "3d.h"
33 #include "segment.h"
34 #include "texmap.h"
35 #include "laser.h"
36 #include "key.h"
37 #include "gameseg.h"
38 #include "object.h"
39 #include "physics.h"
40 #include "slew.h"               
41 #include "render.h"
42 #include "wall.h"
43 #include "vclip.h"
44 #include "polyobj.h"
45 #include "fireball.h"
46 #include "laser.h"
47 #include "error.h"
48 #include "ai.h"
49 #include "hostage.h"
50 #include "fuelcen.h"
51 #include "sounds.h"
52 #include "robot.h"
53 #include "weapon.h"
54 #include "player.h"
55 #include "gauges.h"
56 #include "powerup.h"
57 #include "network.h"
58 #include "newmenu.h"
59 #include "scores.h"
60 #include "effects.h"
61 #include "textures.h"
62 #include "multi.h"
63 #include "cntrlcen.h"
64 #include "newdemo.h"
65 #include "endlevel.h"
66 #include "multibot.h"
67 #include "piggy.h"
68 #include "text.h"
69 #include "automap.h"
70 #include "switch.h"
71 #include "palette.h"
72
73 #ifdef TACTILE
74 #include "tactile.h"
75 #endif 
76
77 #ifdef EDITOR
78 #include "editor\editor.h"
79 #endif
80
81 #include "collide.h"
82
83 #define STANDARD_EXPL_DELAY (f1_0/4)
84
85 //##void collide_fireball_and_wall(object *fireball,fix hitspeed, short hitseg, short hitwall, vms_vector * hitpt)      {
86 //##    return; 
87 //##}
88
89 //      -------------------------------------------------------------------------------------------------------------
90 //      The only reason this routine is called (as of 10/12/94) is so Brain guys can open doors.
91 void collide_robot_and_wall( object * robot, fix hitspeed, short hitseg, short hitwall, vms_vector * hitpt)
92 {
93         ai_local                *ailp = &Ai_local_info[robot-Objects];
94
95         if ((robot->id == ROBOT_BRAIN) || (robot->ctype.ai_info.behavior == AIB_RUN_FROM) || (Robot_info[robot->id].companion == 1) || (robot->ctype.ai_info.behavior == AIB_SNIPE)) {
96                 int     wall_num = Segments[hitseg].sides[hitwall].wall_num;
97                 if (wall_num != -1) {
98                         if ((Walls[wall_num].type == WALL_DOOR) && (Walls[wall_num].keys == KEY_NONE) && (Walls[wall_num].state == WALL_DOOR_CLOSED) && !(Walls[wall_num].flags & WALL_DOOR_LOCKED)) {
99                                 // -- mprintf((0, "Trying to open door at segment %i, side %i\n", hitseg, hitwall));
100                                 wall_open_door(&Segments[hitseg], hitwall);
101                         // -- Changed from this, 10/19/95, MK: Don't want buddy getting stranded from player
102                         //-- } else if ((Robot_info[robot->id].companion == 1) && (Walls[wall_num].type == WALL_DOOR) && (Walls[wall_num].keys != KEY_NONE) && (Walls[wall_num].state == WALL_DOOR_CLOSED) && !(Walls[wall_num].flags & WALL_DOOR_LOCKED)) {
103                         } else if ((Robot_info[robot->id].companion == 1) && (Walls[wall_num].type == WALL_DOOR)) {
104                                 if ((ailp->mode == AIM_GOTO_PLAYER) || (Escort_special_goal == ESCORT_GOAL_SCRAM)) {
105                                         if (Walls[wall_num].keys != KEY_NONE) {
106                                                 if (Walls[wall_num].keys & Players[Player_num].flags)
107                                                         wall_open_door(&Segments[hitseg], hitwall);
108                                         } else if (!(Walls[wall_num].flags & WALL_DOOR_LOCKED))
109                                                 wall_open_door(&Segments[hitseg], hitwall);
110                                 }
111                         } else if (Robot_info[robot->id].thief) {               //      Thief allowed to go through doors to which player has key.
112                                 if (Walls[wall_num].keys != KEY_NONE)
113                                         if (Walls[wall_num].keys & Players[Player_num].flags)
114                                                 wall_open_door(&Segments[hitseg], hitwall);
115                         }
116                 }
117         }
118
119         return;
120 }
121
122 //##void collide_hostage_and_wall( object * hostage, fix hitspeed, short hitseg, short hitwall, vms_vector * hitpt)     {
123 //##    return;
124 //##}
125
126 //      -------------------------------------------------------------------------------------------------------------
127
128 int apply_damage_to_clutter(object *clutter, fix damage)
129 {
130         if ( clutter->flags&OF_EXPLODING) return 0;
131
132         if (clutter->shields < 0 ) return 0;    //clutter already dead...
133
134         clutter->shields -= damage;
135
136         if (clutter->shields < 0) {
137                 explode_object(clutter,0);
138                 return 1;
139         } else
140                 return 0;
141 }
142
143 char    Monster_mode = 0;               //      A cheat.  Do massive damage when collide.
144
145 //given the specified force, apply damage from that force to an object
146 void apply_force_damage(object *obj,fix force,object *other_obj)
147 {
148         int     result;
149         fix damage;
150
151         if (obj->flags & (OF_EXPLODING|OF_SHOULD_BE_DEAD))
152                 return;         //already exploding or dead
153
154         damage = fixdiv(force,obj->mtype.phys_info.mass) / 8;
155
156         if ((other_obj->type == OBJ_PLAYER) && Monster_mode)
157                 damage = 0x7fffffff;
158
159 //mprintf((0,"obj %d, damage=%x\n",obj-Objects,damage));
160
161         switch (obj->type) {
162
163                 case OBJ_ROBOT:
164
165                         if (Robot_info[obj->id].attack_type == 1) {
166                                 if (other_obj->type == OBJ_WEAPON)
167                                         result = apply_damage_to_robot(obj,damage/4, other_obj->ctype.laser_info.parent_num);
168                                 else
169                                         result = apply_damage_to_robot(obj,damage/4, other_obj-Objects);
170                         }
171                         else {
172                                 if (other_obj->type == OBJ_WEAPON)
173                                         result = apply_damage_to_robot(obj,damage/2, other_obj->ctype.laser_info.parent_num);
174                                 else
175                                         result = apply_damage_to_robot(obj,damage/2, other_obj-Objects);
176                         }               
177
178                         if (result && (other_obj->ctype.laser_info.parent_signature == ConsoleObject->signature))
179                                 add_points_to_score(Robot_info[obj->id].score_value);
180                         break;
181
182                 case OBJ_PLAYER:
183
184                         //      If colliding with a claw type robot, do damage proportional to FrameTime because you can collide with those
185                         //      bots every frame since they don't move.
186                         if ( (other_obj->type == OBJ_ROBOT) && (Robot_info[other_obj->id].attack_type) )
187                                 damage = fixmul(damage, FrameTime*2);
188
189                         //      Make trainee easier.
190                         if (Difficulty_level == 0)
191                                 damage /= 2;
192
193                         apply_damage_to_player(obj,other_obj,damage);
194                         break;
195
196                 case OBJ_CLUTTER:
197
198                         apply_damage_to_clutter(obj,damage);
199                         break;
200
201                 case OBJ_CNTRLCEN:
202
203                         apply_damage_to_controlcen(obj,damage, other_obj-Objects);
204                         break;
205
206                 case OBJ_WEAPON:
207
208                         break;          //weapons don't take damage
209
210                 default:
211
212                         Int3();
213
214         }
215 }
216
217 //      -----------------------------------------------------------------------------
218 void bump_this_object(object *objp, object *other_objp, vms_vector *force, int damage_flag)
219 {
220         fix force_mag;
221
222         if (! (objp->mtype.phys_info.flags & PF_PERSISTENT))
223         {
224                 if (objp->type == OBJ_PLAYER) {
225                         vms_vector force2;
226                         force2.x = force->x/4;
227                         force2.y = force->y/4;
228                         force2.z = force->z/4;
229                         phys_apply_force(objp,&force2);
230                         if (damage_flag && ((other_objp->type != OBJ_ROBOT) || !Robot_info[other_objp->id].companion)) {
231                                 force_mag = vm_vec_mag_quick(&force2);
232                                 apply_force_damage(objp, force_mag, other_objp);
233                         }
234                 } else if ((objp->type == OBJ_ROBOT) || (objp->type == OBJ_CLUTTER) || (objp->type == OBJ_CNTRLCEN)) {
235                         if (!Robot_info[objp->id].boss_flag) {
236                                 vms_vector force2;
237                                 force2.x = force->x/(4 + Difficulty_level);
238                                 force2.y = force->y/(4 + Difficulty_level);
239                                 force2.z = force->z/(4 + Difficulty_level);
240
241                                 phys_apply_force(objp, force);
242                                 phys_apply_rot(objp, &force2);
243                                 if (damage_flag) {
244                                         force_mag = vm_vec_mag_quick(force);
245                                         apply_force_damage(objp, force_mag, other_objp);
246                                 }
247                         }
248                 }
249         }
250 }
251
252 //      -----------------------------------------------------------------------------
253 //deal with two objects bumping into each other.  Apply force from collision
254 //to each robot.  The flags tells whether the objects should take damage from
255 //the collision.
256 void bump_two_objects(object *obj0,object *obj1,int damage_flag)
257 {
258         vms_vector      force;
259         object          *t=NULL;
260
261         if (obj0->movement_type != MT_PHYSICS)
262                 t=obj1;
263         else if (obj1->movement_type != MT_PHYSICS)
264                 t=obj0;
265
266         if (t) {
267                 Assert(t->movement_type == MT_PHYSICS);
268                 vm_vec_copy_scale(&force,&t->mtype.phys_info.velocity,-t->mtype.phys_info.mass);
269                 phys_apply_force(t,&force);
270                 return;
271         }
272
273         vm_vec_sub(&force,&obj0->mtype.phys_info.velocity,&obj1->mtype.phys_info.velocity);
274         vm_vec_scale2(&force,2*fixmul(obj0->mtype.phys_info.mass,obj1->mtype.phys_info.mass),(obj0->mtype.phys_info.mass+obj1->mtype.phys_info.mass));
275
276         bump_this_object(obj1, obj0, &force, damage_flag);
277         vm_vec_negate(&force);
278         bump_this_object(obj0, obj1, &force, damage_flag);
279
280 }
281
282 void bump_one_object(object *obj0, vms_vector *hit_dir, fix damage)
283 {
284         vms_vector      hit_vec;
285
286         hit_vec = *hit_dir;
287         vm_vec_scale(&hit_vec, damage);
288
289         phys_apply_force(obj0,&hit_vec);
290
291 }
292
293 #define DAMAGE_SCALE            128     //      Was 32 before 8:55 am on Thursday, September 15, changed by MK, walls were hurting me more than robots!
294 #define DAMAGE_THRESHOLD        (F1_0/3)
295 #define WALL_LOUDNESS_SCALE (20)
296
297 fix force_force = i2f(50);
298
299 void collide_player_and_wall( object * playerobj, fix hitspeed, short hitseg, short hitwall, vms_vector * hitpt)
300 {
301         fix damage;
302         char ForceFieldHit=0;
303         int tmap_num,tmap_num2;
304
305         if (playerobj->id != Player_num) // Execute only for local player
306                 return;
307
308         tmap_num = Segments[hitseg].sides[hitwall].tmap_num;
309
310         //      If this wall does damage, don't make *BONK* sound, we'll be making another sound.
311         if (TmapInfo[tmap_num].damage > 0)
312                 return;
313
314         if (TmapInfo[tmap_num].flags & TMI_FORCE_FIELD) {
315                 vms_vector force;
316
317                 PALETTE_FLASH_ADD(0, 0, 60);    //flash blue
318
319                 //knock player around
320                 force.x = 40*(d_rand() - 16384);
321                 force.y = 40*(d_rand() - 16384);
322                 force.z = 40*(d_rand() - 16384);
323                 phys_apply_rot(playerobj, &force);
324
325 #ifdef TACTILE
326                 if (TactileStick)
327                  Tactile_apply_force (&force,&playerobj->orient);
328 #endif
329
330                 //make sound
331                 digi_link_sound_to_pos( SOUND_FORCEFIELD_BOUNCE_PLAYER, hitseg, 0, hitpt, 0, f1_0 );
332 #ifdef NETWORK
333                 if (Game_mode & GM_MULTI)
334                         multi_send_play_sound(SOUND_FORCEFIELD_BOUNCE_PLAYER, f1_0);
335 #endif
336                 ForceFieldHit=1;
337         } 
338         else {
339
340         #ifdef TACTILE
341                 vms_vector force;
342                 if (TactileStick) {
343                         force.x = -playerobj->mtype.phys_info.velocity.x;
344                         force.y = -playerobj->mtype.phys_info.velocity.y;
345                         force.z = -playerobj->mtype.phys_info.velocity.z;
346                         Tactile_do_collide(&force, &playerobj->orient);
347                 }
348         #endif
349
350         wall_hit_process( &Segments[hitseg], hitwall, 20, playerobj->id, playerobj );
351         }
352
353         //      ** Damage from hitting wall **
354         //      If the player has less than 10% shields, don't take damage from bump
355         // Note: Does quad damage if hit a force field - JL
356         damage = (hitspeed / DAMAGE_SCALE) * (ForceFieldHit*8 + 1);
357
358         tmap_num2 = Segments[hitseg].sides[hitwall].tmap_num2;
359
360         //don't do wall damage and sound if hit lava or water
361         if ((TmapInfo[tmap_num].flags & (TMI_WATER|TMI_VOLATILE)) || (tmap_num2 && (TmapInfo[tmap_num2&0x3fff].flags & (TMI_WATER|TMI_VOLATILE))))
362                 damage = 0;
363
364         if (damage >= DAMAGE_THRESHOLD) {
365                 int     volume;
366                 volume = (hitspeed-(DAMAGE_SCALE*DAMAGE_THRESHOLD)) / WALL_LOUDNESS_SCALE ;
367
368                 create_awareness_event(playerobj, PA_WEAPON_WALL_COLLISION);
369
370                 if ( volume > F1_0 )
371                         volume = F1_0;
372                 if (volume > 0 && !ForceFieldHit) {  // uhhhgly hack
373                         digi_link_sound_to_pos( SOUND_PLAYER_HIT_WALL, hitseg, 0, hitpt, 0, volume );
374                         #ifdef NETWORK
375                         if (Game_mode & GM_MULTI)
376                                 multi_send_play_sound(SOUND_PLAYER_HIT_WALL, volume);   
377                         #endif
378                 }
379
380                 if (!(Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
381                         if ( Players[Player_num].shields > f1_0*10 || ForceFieldHit)
382                                 apply_damage_to_player( playerobj, playerobj, damage );
383
384                 // -- No point in doing this unless we compute a reasonable hitpt.  Currently it is just the player's position. --MK, 01/18/96
385                 // -- if (!(TmapInfo[Segments[hitseg].sides[hitwall].tmap_num].flags & TMI_FORCE_FIELD)) {
386                 // --   vms_vector      hitpt1;
387                 // --   int                     hitseg1;
388                 // --
389                 // --           vm_vec_avg(&hitpt1, hitpt, &Objects[Players[Player_num].objnum].pos);
390                 // --   hitseg1 = find_point_seg(&hitpt1, Objects[Players[Player_num].objnum].segnum);
391                 // --   if (hitseg1 != -1)
392                 // --           object_create_explosion( hitseg, hitpt, Weapon_info[0].impact_size, Weapon_info[0].wall_hit_vclip );
393                 // -- }
394
395         }
396
397         return;
398 }
399
400 fix     Last_volatile_scrape_sound_time = 0;
401
402 void collide_weapon_and_wall( object * weapon, fix hitspeed, short hitseg, short hitwall, vms_vector * hitpt);
403 void collide_debris_and_wall( object * debris, fix hitspeed, short hitseg, short hitwall, vms_vector * hitpt);
404
405 //see if wall is volatile or water
406 //if volatile, cause damage to player  
407 //returns 1=lava, 2=water
408 int check_volatile_wall(object *obj,int segnum,int sidenum,vms_vector *hitpt)
409 {
410         fix tmap_num,d,water;
411
412         Assert(obj->type==OBJ_PLAYER);
413
414         tmap_num = Segments[segnum].sides[sidenum].tmap_num;
415
416         d = TmapInfo[tmap_num].damage;
417         water = (TmapInfo[tmap_num].flags & TMI_WATER);
418
419         if (d > 0 || water) {
420
421                 if (obj->id == Player_num) {
422
423                         if (d > 0) {
424                                 fix damage = fixmul(d,FrameTime);
425
426                                 if (Difficulty_level == 0)
427                                         damage /= 2;
428
429                                 if (!(Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
430                                         apply_damage_to_player( obj, obj, damage );
431
432 #ifdef TACTILE
433                                 if (TactileStick)
434                                  Tactile_Xvibrate (50,25);
435 #endif
436
437                                 PALETTE_FLASH_ADD(f2i(damage*4), 0, 0); //flash red
438                         }
439
440                         obj->mtype.phys_info.rotvel.x = (d_rand() - 16384)/2;
441                         obj->mtype.phys_info.rotvel.z = (d_rand() - 16384)/2;
442                 }
443
444                 return (d>0)?1:2;
445         }
446         else
447          {
448 #ifdef TACTILE
449                 if (TactileStick && !(FrameCount & 15))
450                  Tactile_Xvibrate_clear ();
451 #endif
452
453                 return 0;
454          }
455 }
456
457 //this gets called when an object is scraping along the wall
458 void scrape_object_on_wall(object *obj, short hitseg, short hitside, vms_vector * hitpt )
459 {
460         switch (obj->type) {
461
462                 case OBJ_PLAYER:
463
464                         if (obj->id==Player_num) {
465                                 int type;
466
467                                 //mprintf((0, "Scraped segment #%3i, side #%i\n", hitseg, hitside));
468
469                                 if ((type=check_volatile_wall(obj,hitseg,hitside,hitpt))!=0) {
470                                         vms_vector      hit_dir, rand_vec;
471
472                                         if ((GameTime > Last_volatile_scrape_sound_time + F1_0/4) || (GameTime < Last_volatile_scrape_sound_time)) {
473                                                 int sound = (type==1)?SOUND_VOLATILE_WALL_HISS:SOUND_SHIP_IN_WATER;
474
475                                                 Last_volatile_scrape_sound_time = GameTime;
476
477                                                 digi_link_sound_to_pos( sound, hitseg, 0, hitpt, 0, F1_0 );
478 #ifdef NETWORK
479                                                 if (Game_mode & GM_MULTI)
480                                                         multi_send_play_sound(sound, F1_0);
481 #endif
482                                         }
483
484                                         #ifdef COMPACT_SEGS
485                                                 get_side_normal(&Segments[hitseg], higside, 0, &hit_dir );      
486                                         #else
487                                                 hit_dir = Segments[hitseg].sides[hitside].normals[0];
488                                         #endif
489                         
490                                         make_random_vector(&rand_vec);
491                                         vm_vec_scale_add2(&hit_dir, &rand_vec, F1_0/8);
492                                         vm_vec_normalize_quick(&hit_dir);
493                                         bump_one_object(obj, &hit_dir, F1_0*8);
494                                 }
495
496                                 //@@} else {
497                                 //@@    //what scrape sound
498                                 //@@    //PLAY_SOUND( SOUND_PLAYER_SCRAPE_WALL );
499                                 //@@}
500                 
501                         }
502
503                         break;
504
505                 //these two kinds of objects below shouldn't really slide, so
506                 //if this scrape routine gets called (which it might if the
507                 //object (such as a fusion blob) was created already poking
508                 //through the wall) call the collide routine.
509
510                 case OBJ_WEAPON:
511                         collide_weapon_and_wall(obj,0,hitseg,hitside,hitpt); 
512                         break;
513
514                 case OBJ_DEBRIS:                
515                         collide_debris_and_wall(obj,0,hitseg,hitside,hitpt); 
516                         break;
517         }
518
519 }
520
521 //if an effect is hit, and it can blow up, then blow it up
522 //returns true if it blew up
523 int check_effect_blowup(segment *seg,int side,vms_vector *pnt, object *blower, int force_blowup_flag)
524 {
525         int tm,tmf,ec,db;
526
527         //      If this wall has a trigger and the blower-upper is not the player or the buddy, abort!
528         {
529                 int     ok_to_blow = 0;
530
531                 if (blower->ctype.laser_info.parent_type == OBJ_ROBOT)
532                         if (Robot_info[Objects[blower->ctype.laser_info.parent_num].id].companion)
533                                 ok_to_blow = 1;
534
535                 if (!(ok_to_blow || (blower->ctype.laser_info.parent_type == OBJ_PLAYER))) {
536                         int     trigger_num, wall_num;
537
538                         wall_num = seg->sides[side].wall_num;
539                         if ( wall_num != -1 ) {
540                                 trigger_num = Walls[wall_num].trigger;
541
542                                 if (trigger_num != -1)
543                                         return 0;
544                         }
545                 }
546         }
547
548
549         if ((tm=seg->sides[side].tmap_num2) != 0) {
550
551                 tmf = tm&0xc000;                //tm flags
552                 tm &= 0x3fff;                   //tm without flags
553
554                 //check if it's an animation (monitor) or casts light
555                 if ((((ec=TmapInfo[tm].eclip_num)!=-1) && ((db=Effects[ec].dest_bm_num)!=-1 && !(Effects[ec].flags&EF_ONE_SHOT))) ||    (ec==-1 && (TmapInfo[tm].destroyed!=-1))) {
556                         fix u,v;
557                         grs_bitmap *bm = &GameBitmaps[Textures[tm].index];
558                         int x,y,t;
559
560                         PIGGY_PAGE_IN(Textures[tm]);
561
562                         //this can be blown up...did we hit it?
563
564                         if (!force_blowup_flag) {
565                                 find_hitpoint_uv(&u,&v,NULL,pnt,seg,side,0);    //evil: always say face zero
566
567                                 x = ((unsigned) f2i(u*bm->bm_w)) % bm->bm_w;
568                                 y = ((unsigned) f2i(v*bm->bm_h)) % bm->bm_h;
569
570                                 switch (tmf) {          //adjust for orientation of paste-on
571                                         case 0x0000:    break;
572                                         case 0x4000:    t=y; y=x; x=bm->bm_w-t-1; break;
573                                         case 0x8000:    y=bm->bm_h-y-1; x=bm->bm_w-x-1; break;
574                                         case 0xc000:    t=x; x=y; y=bm->bm_h-t-1; break;
575                                 }
576
577                                 //mprintf((0,"u,v = %x,%x   x,y=%x,%x",u,v,x,y));
578                         
579                                 if (bm->bm_flags & BM_FLAG_RLE)
580                                         bm = rle_expand_texture(bm);
581                         }
582
583                         if (force_blowup_flag || (bm->bm_data[y*bm->bm_w+x] != TRANSPARENCY_COLOR)) {           //not trans, thus on effect
584                                 int vc,sound_num;
585                                 fix dest_size;
586
587
588 #ifdef NETWORK
589                                 if ((Game_mode & GM_MULTI) && Netgame.AlwaysLighting)
590                                 if (!(ec!=-1 && db!=-1 && !(Effects[ec].flags&EF_ONE_SHOT)))
591                                         return(0);
592 #endif
593
594                                 //mprintf((0,"  HIT!\n"));
595
596                                 //note: this must get called before the texture changes, 
597                                 //because we use the light value of the texture to change
598                                 //the static light in the segment
599                                 subtract_light(seg-Segments,side);
600
601                                 if (Newdemo_state == ND_STATE_RECORDING)
602                                         newdemo_record_effect_blowup( seg-Segments, side, pnt);
603
604                                 if (ec!=-1) {
605                                         dest_size = Effects[ec].dest_size;
606                                         vc = Effects[ec].dest_vclip;
607                                 } else {
608                                         dest_size = i2f(20);
609                                         vc = 3;
610                                 }
611
612                                 object_create_explosion( seg-Segments, pnt, dest_size, vc );
613
614                                 if (ec!=-1 && db!=-1 && !(Effects[ec].flags&EF_ONE_SHOT)) {
615
616                                         if ((sound_num = Vclip[vc].sound_num) != -1)
617                                                 digi_link_sound_to_pos( sound_num, seg-Segments, 0, pnt,  0, F1_0 );
618
619                                         if ((sound_num=Effects[ec].sound_num)!=-1)              //kill sound
620                                                 digi_kill_sound_linked_to_segment(seg-Segments,side,sound_num);
621
622                                         if (Effects[ec].dest_eclip!=-1 && Effects[Effects[ec].dest_eclip].segnum==-1) {
623                                                 int bm_num;
624                                                 eclip *new_ec;
625                                         
626                                                 new_ec = &Effects[Effects[ec].dest_eclip];
627                                                 bm_num = new_ec->changing_wall_texture;
628
629                                                 mprintf((0,"bm_num = %d\n",bm_num));
630
631                                                 new_ec->time_left = new_ec->vc.frame_time;
632                                                 new_ec->frame_count = 0;
633                                                 new_ec->segnum = seg-Segments;
634                                                 new_ec->sidenum = side;
635                                                 new_ec->flags |= EF_ONE_SHOT;
636                                                 new_ec->dest_bm_num = Effects[ec].dest_bm_num;
637
638                                                 Assert(bm_num!=0 && seg->sides[side].tmap_num2!=0);
639                                                 seg->sides[side].tmap_num2 = bm_num | tmf;              //replace with destoyed
640
641                                         }
642                                         else {
643                                                 Assert(db!=0 && seg->sides[side].tmap_num2!=0);
644                                                 seg->sides[side].tmap_num2 = db | tmf;          //replace with destoyed
645                                         }
646                                 }
647                                 else {
648                                         seg->sides[side].tmap_num2 = TmapInfo[tm].destroyed | tmf;
649
650                                         //assume this is a light, and play light sound
651                                         digi_link_sound_to_pos( SOUND_LIGHT_BLOWNUP, seg-Segments, 0, pnt,  0, F1_0 );
652                                 }
653
654
655                                 return 1;               //blew up!
656                         }
657                 }
658         }
659
660         return 0;               //didn't blow up
661 }
662
663 //      Copied from laser.c!
664 #define MIN_OMEGA_BLOBS         3                               //      No matter how close the obstruction, at this many blobs created.
665 #define MIN_OMEGA_DIST                  (F1_0*3)                //      At least this distance between blobs, unless doing so would violate MIN_OMEGA_BLOBS
666 #define DESIRED_OMEGA_DIST      (F1_0*5)                //      This is the desired distance between blobs.  For distances > MIN_OMEGA_BLOBS*DESIRED_OMEGA_DIST, but not very large, this will apply.
667 #define MAX_OMEGA_BLOBS         16                              //      No matter how far away the obstruction, this is the maximum number of blobs.
668 #define MAX_OMEGA_DIST                  (MAX_OMEGA_BLOBS * DESIRED_OMEGA_DIST)          //      Maximum extent of lightning blobs.
669
670 //      -------------------------------------------------
671 //      Return true if ok to do Omega damage.
672 int ok_to_do_omega_damage(object *weapon)
673 {
674         int     parent_sig = weapon->ctype.laser_info.parent_signature;
675         int     parent_num = weapon->ctype.laser_info.parent_num;
676
677         if (!(Game_mode & GM_MULTI))
678                 return 1;
679
680         if (Objects[parent_num].signature != parent_sig)
681                 mprintf((0, "Parent of omega blob not consistent with object information.\n"));
682         else {
683                 fix     dist = vm_vec_dist_quick(&Objects[parent_num].pos, &weapon->pos);
684
685                 if (dist > MAX_OMEGA_DIST) {
686                         // -- mprintf((0, "Not doing damage in frame %i, too far away.\n", FrameCount));
687                         return 0;
688                 } else
689                         ; // -- mprintf((0, "*** Doing damage in frame %i ***\n", FrameCount));
690         }
691
692         return 1;
693 }
694
695 //these gets added to the weapon's values when the weapon hits a volitle wall
696 #define VOLATILE_WALL_EXPL_STRENGTH i2f(10)
697 #define VOLATILE_WALL_IMPACT_SIZE       i2f(3)
698 #define VOLATILE_WALL_DAMAGE_FORCE      i2f(5)
699 #define VOLATILE_WALL_DAMAGE_RADIUS     i2f(30)
700
701 // int Show_seg_and_side = 0;
702
703 void collide_weapon_and_wall( object * weapon, fix hitspeed, short hitseg, short hitwall, vms_vector * hitpt)
704 {
705         segment *seg = &Segments[hitseg];
706         int blew_up;
707         int wall_type;
708         int playernum;
709         int     robot_escort;
710
711         if (weapon->id == OMEGA_ID)
712                 if (!ok_to_do_omega_damage(weapon))
713                         return;
714
715         //      If this is a guided missile and it strikes fairly directly, clear bounce flag.
716         if (weapon->id == GUIDEDMISS_ID) {
717                 fix     dot;
718
719                 dot = vm_vec_dot(&weapon->orient.fvec, &Segments[hitseg].sides[hitwall].normals[0]);
720                 mprintf((0, "Guided missile dot = %7.3f\n", f2fl(dot)));
721                 if (dot < -F1_0/6) {
722                         mprintf((0, "Guided missile loses bounciness.\n"));
723                         weapon->mtype.phys_info.flags &= ~PF_BOUNCE;
724                 }
725         }
726
727         //if an energy weapon hits a forcefield, let it bounce
728         if ((TmapInfo[seg->sides[hitwall].tmap_num].flags & TMI_FORCE_FIELD) &&
729                  !(weapon->type == OBJ_WEAPON && Weapon_info[weapon->id].energy_usage==0)) {
730
731                 //make sound
732                 digi_link_sound_to_pos( SOUND_FORCEFIELD_BOUNCE_WEAPON, hitseg, 0, hitpt, 0, f1_0 );
733 #ifdef NETWORK
734                 if (Game_mode & GM_MULTI)
735                         multi_send_play_sound(SOUND_FORCEFIELD_BOUNCE_WEAPON, f1_0);
736 #endif
737
738                 return; //bail here. physics code will bounce this object
739         }
740
741         #ifndef NDEBUG
742         if (keyd_pressed[KEY_LAPOSTRO])
743                 if (weapon->ctype.laser_info.parent_num == Players[Player_num].objnum) {
744                         //      MK: Real pain when you need to know a seg:side and you've got quad lasers.
745                         mprintf((0, "Your laser hit at segment = %i, side = %i\n", hitseg, hitwall));
746                         HUD_init_message("Hit at segment = %i, side = %i", hitseg, hitwall);
747                         if (weapon->id < 4)
748                                 subtract_light(hitseg, hitwall);
749                         else if (weapon->id == FLARE_ID)
750                                 add_light(hitseg, hitwall);
751                 }
752
753                 //@@#ifdef EDITOR
754                 //@@Cursegp = &Segments[hitseg];
755                 //@@Curside = hitwall;
756                 //@@#endif
757         #endif
758
759         if ((weapon->mtype.phys_info.velocity.x == 0) && (weapon->mtype.phys_info.velocity.y == 0) && (weapon->mtype.phys_info.velocity.z == 0)) {
760                 Int3(); //      Contact Matt: This is impossible.  A weapon with 0 velocity hit a wall, which doesn't move.
761                 return;
762         }
763
764         blew_up = check_effect_blowup(seg,hitwall,hitpt, weapon, 0);
765
766         //if ((seg->sides[hitwall].tmap_num2==0) && (TmapInfo[seg->sides[hitwall].tmap_num].flags & TMI_VOLATILE)) {
767
768         if ((weapon->ctype.laser_info.parent_type == OBJ_ROBOT) && (Robot_info[Objects[weapon->ctype.laser_info.parent_num].id].companion==1)) {
769                 robot_escort = 1;
770
771                 if (Game_mode & GM_MULTI)
772                  {
773                          Int3();  // Get Jason!
774                     return;
775             }   
776
777
778                 playernum = Player_num;         //if single player, he's the player's buddy
779         }
780         else {
781                 robot_escort = 0;
782
783                 if (Objects[weapon->ctype.laser_info.parent_num].type == OBJ_PLAYER)
784                         playernum = Objects[weapon->ctype.laser_info.parent_num].id;
785                 else
786                         playernum = -1;         //not a player (thus a robot)
787         }
788
789         if (blew_up) {          //could be a wall switch
790                 //for wall triggers, always say that the player shot it out.  This is
791                 //because robots can shoot out wall triggers, and so the trigger better
792                 //take effect  
793                 //      NO -- Changed by MK, 10/18/95.  We don't want robots blowing puzzles.  Only player or buddy can open!
794                 check_trigger(seg,hitwall,weapon->ctype.laser_info.parent_num,1);
795         }
796
797         if (weapon->id == EARTHSHAKER_ID)
798                 smega_rock_stuff();
799
800         wall_type = wall_hit_process( seg, hitwall, weapon->shields, playernum, weapon );
801
802         // Wall is volatile if either tmap 1 or 2 is volatile
803         if ((TmapInfo[seg->sides[hitwall].tmap_num].flags & TMI_VOLATILE) || (seg->sides[hitwall].tmap_num2 && (TmapInfo[seg->sides[hitwall].tmap_num2&0x3fff].flags & TMI_VOLATILE))) {
804                 weapon_info *wi = &Weapon_info[weapon->id];
805                 int vclip;
806
807                 //we've hit a volatile wall
808
809                 digi_link_sound_to_pos( SOUND_VOLATILE_WALL_HIT,hitseg, 0, hitpt, 0, F1_0 );
810
811                 //for most weapons, use volatile wall hit.  For mega, use its special vclip
812                 vclip = (weapon->id == MEGA_ID)?Weapon_info[weapon->id].robot_hit_vclip:VCLIP_VOLATILE_WALL_HIT;
813
814                 //      New by MK: If powerful badass, explode as badass, not due to lava, fixes megas being wimpy in lava.
815                 if (wi->damage_radius >= VOLATILE_WALL_DAMAGE_RADIUS/2) {
816                         // -- mprintf((0, "Big weapon doing badass in lava instead.\n"));
817                         explode_badass_weapon(weapon,hitpt);
818                 } else {
819                         object_create_badass_explosion( weapon, hitseg, hitpt, 
820                                 wi->impact_size + VOLATILE_WALL_IMPACT_SIZE,
821                                 vclip,
822                                 wi->strength[Difficulty_level]/4+VOLATILE_WALL_EXPL_STRENGTH,   //      diminished by mk on 12/08/94, i was doing 70 damage hitting lava on lvl 1.
823                                 wi->damage_radius+VOLATILE_WALL_DAMAGE_RADIUS,
824                                 wi->strength[Difficulty_level]/2+VOLATILE_WALL_DAMAGE_FORCE,
825                                 weapon->ctype.laser_info.parent_num );
826                 }
827
828                 weapon->flags |= OF_SHOULD_BE_DEAD;             //make flares die in lava
829
830         }
831         else if ((TmapInfo[seg->sides[hitwall].tmap_num].flags & TMI_WATER) || (seg->sides[hitwall].tmap_num2 && (TmapInfo[seg->sides[hitwall].tmap_num2&0x3fff].flags & TMI_WATER))) {
832                 weapon_info *wi = &Weapon_info[weapon->id];
833
834                 //we've hit water
835
836                 //      MK: 09/13/95: Badass in water is 1/2 normal intensity.
837                 if ( Weapon_info[weapon->id].matter ) {
838
839                         digi_link_sound_to_pos( SOUND_MISSILE_HIT_WATER,hitseg, 0, hitpt, 0, F1_0 );
840
841                         if ( Weapon_info[weapon->id].damage_radius ) {
842
843                                 digi_link_sound_to_object(SOUND_BADASS_EXPLOSION, weapon-Objects, 0, F1_0);
844
845                                 //      MK: 09/13/95: Badass in water is 1/2 normal intensity.
846                                 object_create_badass_explosion( weapon, hitseg, hitpt, 
847                                         wi->impact_size/2,
848                                         wi->robot_hit_vclip, 
849                                         wi->strength[Difficulty_level]/4,
850                                         wi->damage_radius,
851                                         wi->strength[Difficulty_level]/2,
852                                         weapon->ctype.laser_info.parent_num );
853                         }
854                         else
855                                 object_create_explosion( weapon->segnum, &weapon->pos, Weapon_info[weapon->id].impact_size, Weapon_info[weapon->id].wall_hit_vclip );
856
857                 } else {
858                         digi_link_sound_to_pos( SOUND_LASER_HIT_WATER,hitseg, 0, hitpt, 0, F1_0 );
859                         object_create_explosion( weapon->segnum, &weapon->pos, Weapon_info[weapon->id].impact_size, VCLIP_WATER_HIT );
860                 }
861
862                 weapon->flags |= OF_SHOULD_BE_DEAD;             //make flares die in water
863
864         }
865         else {
866
867                 if (weapon->mtype.phys_info.flags & PF_BOUNCE) {
868
869                         //do special bound sound & effect
870
871                 }
872                 else {
873
874                         //if it's not the player's weapon, or it is the player's and there
875                         //is no wall, and no blowing up monitor, then play sound
876                         if ((weapon->ctype.laser_info.parent_type != OBJ_PLAYER) ||     ((seg->sides[hitwall].wall_num == -1 || wall_type==WHP_NOT_SPECIAL) && !blew_up))
877                                 if ((Weapon_info[weapon->id].wall_hit_sound > -1 ) && (!(weapon->flags & OF_SILENT)))
878                                 digi_link_sound_to_pos( Weapon_info[weapon->id].wall_hit_sound,weapon->segnum, 0, &weapon->pos, 0, F1_0 );
879                 
880                         if ( Weapon_info[weapon->id].wall_hit_vclip > -1 )      {
881                                 if ( Weapon_info[weapon->id].damage_radius )
882                                         explode_badass_weapon(weapon,hitpt);
883                                 else
884                                         object_create_explosion( weapon->segnum, &weapon->pos, Weapon_info[weapon->id].impact_size, Weapon_info[weapon->id].wall_hit_vclip );
885                         }
886                 }
887         }
888
889         //      If weapon fired by player or companion...
890         if (( weapon->ctype.laser_info.parent_type== OBJ_PLAYER ) || robot_escort) {
891
892                 if (!(weapon->flags & OF_SILENT) && (weapon->ctype.laser_info.parent_num == Players[Player_num].objnum))
893                         create_awareness_event(weapon, PA_WEAPON_WALL_COLLISION);                       // object "weapon" can attract attention to player
894         
895 //              if (weapon->id != FLARE_ID) {
896 //      We now allow flares to open doors.
897                 {
898
899                         if (((weapon->id != FLARE_ID) || (weapon->ctype.laser_info.parent_type != OBJ_PLAYER)) && !(weapon->mtype.phys_info.flags & PF_BOUNCE))
900                                 weapon->flags |= OF_SHOULD_BE_DEAD;
901
902                         //don't let flares stick in force fields
903                         if ((weapon->id == FLARE_ID) && (TmapInfo[seg->sides[hitwall].tmap_num].flags & TMI_FORCE_FIELD))
904                                 weapon->flags |= OF_SHOULD_BE_DEAD;
905
906                         if (!(weapon->flags & OF_SILENT)) {
907                                 switch (wall_type) {
908
909                                         case WHP_NOT_SPECIAL:
910                                                 //should be handled above
911                                                 //digi_link_sound_to_pos( Weapon_info[weapon->id].wall_hit_sound, weapon->segnum, 0, &weapon->pos, 0, F1_0 );
912                                                 break;
913
914                                         case WHP_NO_KEY:
915                                                 //play special hit door sound (if/when we get it)
916                                                 digi_link_sound_to_pos( SOUND_WEAPON_HIT_DOOR, weapon->segnum, 0, &weapon->pos, 0, F1_0 );
917 #ifdef NETWORK
918                                  if (Game_mode & GM_MULTI)
919                                                         multi_send_play_sound( SOUND_WEAPON_HIT_DOOR, F1_0 );
920 #endif
921
922                                                 break;
923
924                                         case WHP_BLASTABLE:
925                                                 //play special blastable wall sound (if/when we get it)
926                                                 if ((Weapon_info[weapon->id].wall_hit_sound > -1 ) && (!(weapon->flags & OF_SILENT)))
927                                                         digi_link_sound_to_pos( SOUND_WEAPON_HIT_BLASTABLE, weapon->segnum, 0, &weapon->pos, 0, F1_0 );
928                                                 break;
929
930                                         case WHP_DOOR:
931                                                 //don't play anything, since door open sound will play
932                                                 break;
933                                 }
934                         } // else
935                                 //mprintf((0, "Weapon %i hits wall, but has silent bit set.\n", weapon-Objects));
936                 } // else {
937                         //      if (weapon->lifeleft <= 0)
938                         //      weapon->flags |= OF_SHOULD_BE_DEAD;
939                 // }
940
941         } else {
942                 // This is a robot's laser
943                 if (!(weapon->mtype.phys_info.flags & PF_BOUNCE))
944                         weapon->flags |= OF_SHOULD_BE_DEAD;
945         }
946
947         return;
948 }
949
950 //##void collide_camera_and_wall( object * camera, fix hitspeed, short hitseg, short hitwall, vms_vector * hitpt)       {
951 //##    return;
952 //##}
953
954 //##void collide_powerup_and_wall( object * powerup, fix hitspeed, short hitseg, short hitwall, vms_vector * hitpt)     {
955 //##    return;
956 //##}
957
958 void collide_debris_and_wall( object * debris, fix hitspeed, short hitseg, short hitwall, vms_vector * hitpt)   {       
959         explode_object(debris,0);
960         return;
961 }
962
963 //##void collide_fireball_and_fireball( object * fireball1, object * fireball2, vms_vector *collision_point ) {
964 //##    return; 
965 //##}
966
967 //##void collide_fireball_and_robot( object * fireball, object * robot, vms_vector *collision_point ) {
968 //##    return; 
969 //##}
970
971 //##void collide_fireball_and_hostage( object * fireball, object * hostage, vms_vector *collision_point ) {
972 //##    return; 
973 //##}
974
975 //##void collide_fireball_and_player( object * fireball, object * player, vms_vector *collision_point ) {
976 //##    return; 
977 //##}
978
979 //##void collide_fireball_and_weapon( object * fireball, object * weapon, vms_vector *collision_point ) { 
980 //##    //weapon->flags |= OF_SHOULD_BE_DEAD;
981 //##    return; 
982 //##}
983
984 //##void collide_fireball_and_camera( object * fireball, object * camera, vms_vector *collision_point ) { 
985 //##    return; 
986 //##}
987
988 //##void collide_fireball_and_powerup( object * fireball, object * powerup, vms_vector *collision_point ) { 
989 //##    return; 
990 //##}
991
992 //##void collide_fireball_and_debris( object * fireball, object * debris, vms_vector *collision_point ) { 
993 //##    return; 
994 //##}
995
996 //      -------------------------------------------------------------------------------------------------------------------
997 void collide_robot_and_robot( object * robot1, object * robot2, vms_vector *collision_point ) { 
998 //      mprintf((0, "Coll: [%2i %4i %4i %4i] [%2i %4i %4i %4i] at [%4i %4i %4i]", 
999 //              robot1-Objects, f2i(robot1->pos.x), f2i(robot1->pos.y), f2i(robot1->pos.z),
1000 //              robot2-Objects, f2i(robot2->pos.x), f2i(robot2->pos.y), f2i(robot2->pos.z),
1001 //              f2i(collision_point->x), f2i(collision_point->y), f2i(collision_point->z)));
1002
1003         bump_two_objects(robot1, robot2, 1);
1004         return; 
1005 }
1006
1007 void collide_robot_and_controlcen( object * obj1, object * obj2, vms_vector *collision_point )
1008 {
1009
1010         if (obj1->type == OBJ_ROBOT) {
1011                 vms_vector      hitvec;
1012                 vm_vec_normalize_quick(vm_vec_sub(&hitvec, &obj2->pos, &obj1->pos));
1013                 bump_one_object(obj1, &hitvec, 0);
1014         } else {
1015                 vms_vector      hitvec;
1016                 vm_vec_normalize_quick(vm_vec_sub(&hitvec, &obj1->pos, &obj2->pos));
1017                 bump_one_object(obj2, &hitvec, 0);
1018         }
1019
1020 }
1021
1022 //##void collide_robot_and_hostage( object * robot, object * hostage, vms_vector *collision_point ) { 
1023 //##    return; 
1024 //##}
1025
1026 fix Last_thief_hit_time;
1027
1028 void collide_robot_and_player( object * robot, object * playerobj, vms_vector *collision_point )
1029
1030         int     steal_attempt = 0;
1031         int     collision_seg;
1032
1033         if (robot->flags&OF_EXPLODING)
1034                 return;
1035
1036         collision_seg = find_point_seg(collision_point, playerobj->segnum);
1037         if (collision_seg != -1)
1038                 object_create_explosion( collision_seg, collision_point, Weapon_info[0].impact_size, Weapon_info[0].wall_hit_vclip );
1039
1040         if (playerobj->id == Player_num) {
1041                 if (Robot_info[robot->id].companion)    //      Player and companion don't collide.
1042                         return;
1043                 if (Robot_info[robot->id].kamikaze) {
1044                         apply_damage_to_robot(robot, robot->shields+1, playerobj-Objects);
1045                         if (playerobj == ConsoleObject)
1046                                 add_points_to_score(Robot_info[robot->id].score_value);
1047                 }
1048
1049                 if (Robot_info[robot->id].thief) {
1050                         if (Ai_local_info[robot-Objects].mode == AIM_THIEF_ATTACK) {
1051                                 Last_thief_hit_time = GameTime;
1052                                 attempt_to_steal_item(robot, playerobj->id);
1053                                 steal_attempt = 1;
1054                         } else if (GameTime - Last_thief_hit_time < F1_0*2)
1055                                 return;         //      ZOUNDS!  BRILLIANT!  Thief not collide with player if not stealing!
1056                                                                 // NO!  VERY DUMB!  makes thief look very stupid if player hits him while cloaked! -AP
1057                         else
1058                                 Last_thief_hit_time = GameTime;
1059                 }
1060
1061                 create_awareness_event(playerobj, PA_PLAYER_COLLISION);                 // object robot can attract attention to player
1062                 do_ai_robot_hit_attack(robot, playerobj, collision_point);
1063                 do_ai_robot_hit(robot, PA_WEAPON_ROBOT_COLLISION);
1064         } 
1065 #ifdef NETWORK
1066         else
1067                 multi_robot_request_change(robot, playerobj->id);
1068 #endif
1069
1070         // added this if to remove the bump sound if it's the thief.
1071         // A "steal" sound was added and it was getting obscured by the bump. -AP 10/3/95
1072         //      Changed by MK to make this sound unless the robot stole.
1073         if ((!steal_attempt) && !Robot_info[robot->id].energy_drain)
1074                 digi_link_sound_to_pos( SOUND_ROBOT_HIT_PLAYER, playerobj->segnum, 0, collision_point, 0, F1_0 );
1075
1076         bump_two_objects(robot, playerobj, 1);
1077         return; 
1078 }
1079
1080 // Provide a way for network message to instantly destroy the control center
1081 // without awarding points or anything.
1082
1083 //      if controlcen == NULL, that means don't do the explosion because the control center
1084 //      was actually in another object.
1085 void net_destroy_controlcen(object *controlcen)
1086 {
1087         if (Control_center_destroyed != 1) {
1088
1089                 do_controlcen_destroyed_stuff(controlcen);
1090
1091                 if ((controlcen != NULL) && !(controlcen->flags&(OF_EXPLODING|OF_DESTROYED))) {
1092                         digi_link_sound_to_pos( SOUND_CONTROL_CENTER_DESTROYED, controlcen->segnum, 0, &controlcen->pos, 0, F1_0 );
1093                         explode_object(controlcen,0);
1094                 }
1095         }
1096
1097 }
1098
1099 //      -----------------------------------------------------------------------------
1100 void apply_damage_to_controlcen(object *controlcen, fix damage, short who)
1101 {
1102         int     whotype;
1103
1104         //      Only allow a player to damage the control center.
1105
1106         if ((who < 0) || (who > Highest_object_index))
1107                 return;
1108
1109         whotype = Objects[who].type;
1110         if (whotype != OBJ_PLAYER) {
1111                 mprintf((0, "Damage to control center by object of type %i prevented by MK!\n", whotype));
1112                 return;
1113         }
1114
1115         #ifdef NETWORK
1116         if ((Game_mode & GM_MULTI) && !(Game_mode & GM_MULTI_COOP) && (Players[Player_num].time_level < Netgame.control_invul_time))
1117         {
1118                 if (Objects[who].id == Player_num) {
1119                         int secs = f2i(Netgame.control_invul_time-Players[Player_num].time_level) % 60;
1120                         int mins = f2i(Netgame.control_invul_time-Players[Player_num].time_level) / 60;
1121                         HUD_init_message("%s %d:%02d.", TXT_CNTRLCEN_INVUL, mins, secs);
1122                 }
1123                 return;
1124         }
1125         #endif
1126
1127         if (Objects[who].id == Player_num) {
1128                 Control_center_been_hit = 1;
1129                 ai_do_cloak_stuff();
1130         }
1131
1132         if ( controlcen->shields >= 0 )
1133                 controlcen->shields -= damage;
1134
1135         if ( (controlcen->shields < 0) && !(controlcen->flags&(OF_EXPLODING|OF_DESTROYED)) ) {
1136
1137                 do_controlcen_destroyed_stuff(controlcen);
1138
1139                 #ifdef NETWORK
1140                 if (Game_mode & GM_MULTI) {
1141                         if (who == Players[Player_num].objnum)
1142                                 add_points_to_score(CONTROL_CEN_SCORE);
1143                         multi_send_destroy_controlcen((ushort)(controlcen-Objects), Objects[who].id );
1144                 }
1145                 #endif
1146
1147                 if (!(Game_mode & GM_MULTI))
1148                         add_points_to_score(CONTROL_CEN_SCORE);
1149
1150                 digi_link_sound_to_pos( SOUND_CONTROL_CENTER_DESTROYED, controlcen->segnum, 0, &controlcen->pos, 0, F1_0 );
1151
1152                 explode_object(controlcen,0);
1153         }
1154 }
1155
1156 void collide_player_and_controlcen( object * controlcen, object * playerobj, vms_vector *collision_point )
1157
1158         if (playerobj->id == Player_num) {
1159                 Control_center_been_hit = 1;
1160                 ai_do_cloak_stuff();                            //      In case player cloaked, make control center know where he is.
1161         }
1162
1163         digi_link_sound_to_pos( SOUND_ROBOT_HIT_PLAYER, playerobj->segnum, 0, collision_point, 0, F1_0 );
1164         bump_two_objects(controlcen, playerobj, 1);
1165
1166         return; 
1167 }
1168
1169 void collide_player_and_marker( object * marker, object * playerobj, vms_vector *collision_point )
1170
1171    mprintf ((0,"Collided with marker %d!\n",marker->id));
1172
1173         if (playerobj->id==Player_num) {
1174                 int drawn;
1175
1176                 if (Game_mode & GM_MULTI)
1177                 {
1178                         drawn = HUD_init_message ("MARKER %s: %s",Players[marker->id/2].callsign,MarkerMessage[marker->id]);
1179                 }
1180                 else
1181                 {
1182                         if (MarkerMessage[marker->id][0])
1183                                 drawn = HUD_init_message("MARKER %d: %s", marker->id+1,MarkerMessage[marker->id]);
1184                         else
1185                                 drawn = HUD_init_message("MARKER %d", marker->id+1);
1186            }
1187
1188                 if (drawn)
1189                         digi_play_sample( SOUND_MARKER_HIT, F1_0 );
1190
1191                 detect_escort_goal_accomplished(marker-Objects);
1192    }
1193 }
1194
1195 //      If a persistent weapon and other object is not a weapon, weaken it, else kill it.
1196 //      If both objects are weapons, weaken the weapon.
1197 void maybe_kill_weapon(object *weapon, object *other_obj)
1198 {
1199         if ((weapon->id == PROXIMITY_ID) || (weapon->id == SUPERPROX_ID) || (weapon->id == PMINE_ID)) {
1200                 weapon->flags |= OF_SHOULD_BE_DEAD;
1201                 return;
1202         }
1203
1204         //      Changed, 10/12/95, MK: Make weapon-weapon collisions always kill both weapons if not persistent.
1205         //      Reason: Otherwise you can't use proxbombs to detonate incoming homing missiles (or mega missiles).
1206         if (weapon->mtype.phys_info.flags & PF_PERSISTENT) {
1207                 //      Weapons do a lot of damage to weapons, other objects do much less.
1208                 if (!(weapon->mtype.phys_info.flags & PF_PERSISTENT)) {
1209                         if (other_obj->type == OBJ_WEAPON)
1210                                 weapon->shields -= other_obj->shields/2;
1211                         else
1212                                 weapon->shields -= other_obj->shields/4;
1213
1214                         if (weapon->shields <= 0) {
1215                                 weapon->shields = 0;
1216                                 weapon->flags |= OF_SHOULD_BE_DEAD;     // weapon->lifeleft = 1;
1217                         }
1218                 }
1219         } else
1220                 weapon->flags |= OF_SHOULD_BE_DEAD;     // weapon->lifeleft = 1;
1221
1222 // --   if ((weapon->mtype.phys_info.flags & PF_PERSISTENT) || (other_obj->type == OBJ_WEAPON)) {
1223 // --           //      Weapons do a lot of damage to weapons, other objects do much less.
1224 // --           if (!(weapon->mtype.phys_info.flags & PF_PERSISTENT)) {
1225 // --                   if (other_obj->type == OBJ_WEAPON)
1226 // --                           weapon->shields -= other_obj->shields/2;
1227 // --                   else
1228 // --                           weapon->shields -= other_obj->shields/4;
1229 // -- 
1230 // --                   if (weapon->shields <= 0) {
1231 // --                           weapon->shields = 0;
1232 // --                           weapon->flags |= OF_SHOULD_BE_DEAD;
1233 // --                   }
1234 // --           }
1235 // --   } else
1236 // --           weapon->flags |= OF_SHOULD_BE_DEAD;
1237 }
1238
1239 void collide_weapon_and_controlcen( object * weapon, object *controlcen, vms_vector *collision_point  )
1240 {
1241
1242         if (weapon->id == OMEGA_ID)
1243                 if (!ok_to_do_omega_damage(weapon))
1244                         return;
1245
1246         if (weapon->ctype.laser_info.parent_type == OBJ_PLAYER) {
1247                 fix     damage = weapon->shields;
1248
1249                 if (Objects[weapon->ctype.laser_info.parent_num].id == Player_num)
1250                         Control_center_been_hit = 1;
1251
1252                 if ( Weapon_info[weapon->id].damage_radius )
1253                         explode_badass_weapon(weapon,collision_point);
1254                 else
1255                         object_create_explosion( controlcen->segnum, collision_point, controlcen->size*3/20, VCLIP_SMALL_EXPLOSION );
1256
1257                 digi_link_sound_to_pos( SOUND_CONTROL_CENTER_HIT, controlcen->segnum, 0, collision_point, 0, F1_0 );
1258
1259                 damage = fixmul(damage, weapon->ctype.laser_info.multiplier);
1260
1261                 apply_damage_to_controlcen(controlcen, damage, weapon->ctype.laser_info.parent_num);
1262
1263                 maybe_kill_weapon(weapon,controlcen);
1264         } else {        //      If robot weapon hits control center, blow it up, make it go away, but do no damage to control center.
1265                 object_create_explosion( controlcen->segnum, collision_point, controlcen->size*3/20, VCLIP_SMALL_EXPLOSION );
1266                 maybe_kill_weapon(weapon,controlcen);
1267         }
1268
1269 }
1270
1271 void collide_weapon_and_clutter( object * weapon, object *clutter, vms_vector *collision_point  )       {
1272         short exp_vclip = VCLIP_SMALL_EXPLOSION;
1273
1274         if ( clutter->shields >= 0 )
1275                 clutter->shields -= weapon->shields;
1276
1277         digi_link_sound_to_pos( SOUND_LASER_HIT_CLUTTER, weapon->segnum, 0, collision_point, 0, F1_0 );
1278  
1279         object_create_explosion( clutter->segnum, collision_point, ((clutter->size/3)*3)/4, exp_vclip );
1280
1281         if ( (clutter->shields < 0) && !(clutter->flags&(OF_EXPLODING|OF_DESTROYED)))
1282                 explode_object(clutter,STANDARD_EXPL_DELAY);
1283
1284         maybe_kill_weapon(weapon,clutter);
1285 }
1286
1287 //--mk, 121094 -- extern void spin_robot(object *robot, vms_vector *collision_point);
1288
1289 extern object *explode_badass_object(object *objp, fix damage, fix distance, fix force);
1290
1291 int     Final_boss_is_dead = 0;
1292 fix     Final_boss_countdown_time = 0;
1293
1294 //      ------------------------------------------------------------------------------------------------------
1295 void do_final_boss_frame(void)
1296 {
1297
1298         if (!Final_boss_is_dead)
1299                 return;
1300
1301         if (!Control_center_destroyed)
1302                 return;
1303
1304         if (Final_boss_countdown_time == 0)
1305                 Final_boss_countdown_time = F1_0*2;
1306
1307         Final_boss_countdown_time -= FrameTime;
1308         if (Final_boss_countdown_time > 0)
1309                 return;
1310
1311         gr_palette_fade_out( gr_palette, 256, 0 );
1312         start_endlevel_sequence();              //pretend we hit the exit trigger
1313
1314 }
1315
1316 //      ------------------------------------------------------------------------------------------------------
1317 //      This is all the ugly stuff we do when you kill the final boss so that you don't die or something
1318 //      which would ruin the logic of the cut sequence.
1319 void do_final_boss_hacks(void)
1320 {
1321         if (Player_is_dead) {
1322                 Int3();         //      Uh-oh, player is dead.  Try to rescue him.
1323                 Player_is_dead = 0;
1324         }
1325
1326         if (Players[Player_num].shields <= 0)
1327                 Players[Player_num].shields = 1;
1328
1329         //      If you're not invulnerable, get invulnerable!
1330         if (!(Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)) {
1331                 Players[Player_num].invulnerable_time = GameTime;
1332                 Players[Player_num].flags |= PLAYER_FLAGS_INVULNERABLE;
1333         }
1334         if (!(Game_mode & GM_MULTI))
1335                 buddy_message("Nice job, %s!", Players[Player_num].callsign);
1336
1337         Final_boss_is_dead = 1;
1338 }
1339
1340 extern int Buddy_dude_cheat;
1341 extern int multi_all_players_alive();
1342 void multi_send_finish_game ();
1343
1344 //      ------------------------------------------------------------------------------------------------------
1345 //      Return 1 if robot died, else return 0
1346 int apply_damage_to_robot(object *robot, fix damage, int killer_objnum)
1347 {
1348    char isthief;
1349         char i,temp_stolen[MAX_STOLEN_ITEMS];   
1350         
1351         if ( robot->flags&OF_EXPLODING) return 0;
1352
1353         if (robot->shields < 0 ) return 0;      //robot already dead...
1354
1355         if (Robot_info[robot->id].boss_flag)
1356                 Boss_hit_time = GameTime;
1357
1358         //      Buddy invulnerable on level 24 so he can give you his important messages.  Bah.
1359         //      Also invulnerable if his cheat for firing weapons is in effect.
1360         if (Robot_info[robot->id].companion)
1361 //              if ((Current_mission_num == 0 && Current_level_num == Last_level) || Buddy_dude_cheat)
1362 #ifdef NETWORK
1363                 if ((Current_mission_num == 0 && Current_level_num == Last_level) )
1364                         return 0;
1365 #endif
1366
1367 //      if (robot->control_type == CT_REMOTE)
1368 //              return 0; // Can't damange a robot controlled by another player
1369
1370 // -- MK, 10/21/95, unused! --  if (Robot_info[robot->id].boss_flag)
1371 //              Boss_been_hit = 1;
1372
1373         robot->shields -= damage;
1374
1375         //      Do unspeakable hacks to make sure player doesn't die after killing boss.  Or before, sort of.
1376         if (Robot_info[robot->id].boss_flag)
1377 #ifdef NETWORK
1378                 if ((Current_mission_num == 0) && Current_level_num == Last_level)
1379 #endif
1380                         if (robot->shields < 0)
1381                          {
1382 #ifdef NETWORK
1383                                 if (Game_mode & GM_MULTI)
1384                                   {
1385                                          if (!multi_all_players_alive()) // everyones gotta be alive
1386                                            robot->shields=1;
1387                                          else
1388                                           {
1389                                             multi_send_finish_game();
1390                                             do_final_boss_hacks();
1391                                           }             
1392                                   
1393                                         }               
1394                                 else
1395 #endif
1396                                   {     // NOTE LINK TO ABOVE!!!
1397                                         if ((Players[Player_num].shields < 0) || Player_is_dead)
1398                                                 robot->shields = 1;             //      Sorry, we can't allow you to kill the final boss after you've died.  Rough luck.
1399                                         else
1400                                                 do_final_boss_hacks();
1401                                   }
1402                           }
1403
1404         if (robot->shields < 0) {
1405 #ifdef NETWORK
1406                 if (Game_mode & GM_MULTI) {
1407                  if (Robot_info[robot->id].thief)       
1408                         isthief=1;
1409                  else
1410                         isthief=0;
1411
1412                  if (isthief)
1413                         for (i=0;i<MAX_STOLEN_ITEMS;i++)
1414                          temp_stolen[(int)i]=Stolen_items[(int)i];
1415                                 
1416                         if (multi_explode_robot_sub(robot-Objects, killer_objnum,Robot_info[robot->id].thief))
1417                         {
1418                          if (isthief)   
1419                         for (i=0;i<MAX_STOLEN_ITEMS;i++)
1420                                   Stolen_items[(int)i]=temp_stolen[(int)i];
1421                                         
1422                                 multi_send_robot_explode(robot-Objects, killer_objnum,Robot_info[robot->id].thief);
1423
1424                    if (isthief) 
1425                                 for (i=0;i<MAX_STOLEN_ITEMS;i++)
1426                                           Stolen_items[(int)i]=255;
1427
1428                                 return 1;
1429                         }
1430                         else
1431                                 return 0;
1432                 }
1433 #endif
1434
1435                 Players[Player_num].num_kills_level++;
1436                 Players[Player_num].num_kills_total++;
1437
1438                 if (Robot_info[robot->id].boss_flag) {
1439                         start_boss_death_sequence(robot);       //do_controlcen_destroyed_stuff(NULL);
1440                 } else if (Robot_info[robot->id].death_roll) {
1441                         start_robot_death_sequence(robot);      //do_controlcen_destroyed_stuff(NULL);
1442                 } else {
1443                         if (robot->id == SPECIAL_REACTOR_ROBOT)
1444                                 special_reactor_stuff();
1445                         //if (Robot_info[robot->id].smart_blobs)
1446                         //      create_smart_children(robot, Robot_info[robot->id].smart_blobs);
1447                         //if (Robot_info[robot->id].badass)
1448                         //      explode_badass_object(robot, F1_0*Robot_info[robot->id].badass, F1_0*40, F1_0*150);
1449                         if (Robot_info[robot->id].kamikaze)
1450                                 explode_object(robot,1);                //      Kamikaze, explode right away, IN YOUR FACE!
1451                         else
1452                                 explode_object(robot,STANDARD_EXPL_DELAY);
1453                 }
1454                 return 1;
1455         } else
1456                 return 0;
1457 }
1458
1459 extern int boss_spew_robot(object *objp, vms_vector *pos);
1460
1461 //--ubyte       Boss_teleports[NUM_D2_BOSSES] =                                 {1,1,1,1,1,1};          // Set byte if this boss can teleport
1462 //--ubyte       Boss_cloaks[NUM_D2_BOSSES] =                                    {1,1,1,1,1,1};          // Set byte if this boss can cloak
1463 //--ubyte       Boss_spews_bots_energy[NUM_D2_BOSSES] =         {1,1,0,0,1,1};          //      Set byte if boss spews bots when hit by energy weapon.
1464 //--ubyte       Boss_spews_bots_matter[NUM_D2_BOSSES] =         {0,0,1,0,1,1};          //      Set byte if boss spews bots when hit by matter weapon.
1465 //--ubyte       Boss_invulnerable_energy[NUM_D2_BOSSES] = {0,0,1,1,0,0};                //      Set byte if boss is invulnerable to energy weapons.
1466 //--ubyte       Boss_invulnerable_matter[NUM_D2_BOSSES] = {0,0,0,1,0,0};                //      Set byte if boss is invulnerable to matter weapons.
1467 //--ubyte       Boss_invulnerable_spot[NUM_D2_BOSSES] =         {0,0,0,0,1,1};          //      Set byte if boss is invulnerable in all but a certain spot.  (Dot product fvec|vec_to_collision < BOSS_INVULNERABLE_DOT)
1468
1469 //#define       BOSS_INVULNERABLE_DOT   0               //      If a boss is invulnerable over most of his body, fvec(dot)vec_to_collision must be less than this for damage to occur.
1470 int     Boss_invulnerable_dot = 0;
1471
1472 int     Buddy_gave_hint_count = 5;
1473 fix     Last_time_buddy_gave_hint = 0;
1474
1475 //      ------------------------------------------------------------------------------------------------------
1476 //      Return true if damage done to boss, else return false.
1477 int do_boss_weapon_collision(object *robot, object *weapon, vms_vector *collision_point)
1478 {
1479         int     d2_boss_index;
1480         int     damage_flag;
1481
1482         damage_flag = 1;
1483
1484         d2_boss_index = Robot_info[robot->id].boss_flag - BOSS_D2;
1485
1486         Assert((d2_boss_index >= 0) && (d2_boss_index < NUM_D2_BOSSES));
1487
1488         //      See if should spew a bot.
1489         if (weapon->ctype.laser_info.parent_type == OBJ_PLAYER)
1490                 if ((Weapon_info[weapon->id].matter && Boss_spews_bots_matter[d2_boss_index]) || (!Weapon_info[weapon->id].matter && Boss_spews_bots_energy[d2_boss_index])) {
1491                         if (Boss_spew_more[d2_boss_index])
1492                                 if (d_rand() > 16384) {
1493                                         if (boss_spew_robot(robot, collision_point) != -1)
1494                                                 Last_gate_time = GameTime - Gate_interval - 1;  //      Force allowing spew of another bot.
1495                                 }
1496                         boss_spew_robot(robot, collision_point);
1497                 }
1498
1499         if (Boss_invulnerable_spot[d2_boss_index]) {
1500                 fix                     dot;
1501                 vms_vector      tvec1;
1502
1503                 //      Boss only vulnerable in back.  See if hit there.
1504                 vm_vec_sub(&tvec1, collision_point, &robot->pos);
1505                 vm_vec_normalize_quick(&tvec1); //      Note, if BOSS_INVULNERABLE_DOT is close to F1_0 (in magnitude), then should probably use non-quick version.
1506                 dot = vm_vec_dot(&tvec1, &robot->orient.fvec);
1507                 mprintf((0, "Boss hit vec dot = %7.3f\n", f2fl(dot)));
1508
1509                 if (dot > Boss_invulnerable_dot) {
1510                         int     new_obj;
1511                         int     segnum;
1512
1513                         segnum = find_point_seg(collision_point, robot->segnum);
1514                         digi_link_sound_to_pos( SOUND_WEAPON_HIT_DOOR, segnum, 0, collision_point, 0, F1_0);
1515                         damage_flag = 0;
1516
1517                         if (Last_time_buddy_gave_hint == 0)
1518                                 Last_time_buddy_gave_hint = d_rand()*32 + F1_0*16;
1519
1520                         if (Buddy_gave_hint_count) {
1521                                 if (Last_time_buddy_gave_hint + F1_0*20 < GameTime) {
1522                                         int     sval;
1523
1524                                         Buddy_gave_hint_count--;
1525                                         Last_time_buddy_gave_hint = GameTime;
1526                                         sval = (d_rand()*4) >> 15;
1527                                         switch (sval) {
1528                                                 case 0: buddy_message("Hit him in the back!");  break;
1529                                                 case 1: buddy_message("He's invulnerable there!");      break;
1530                                                 case 2: buddy_message("Get behind him and fire!");      break;
1531                                                 case 3:
1532                                                 default:
1533                                                                         buddy_message("Hit the glowing spot!"); break;
1534                                         }
1535                                 }
1536                         }
1537
1538                         //      Cause weapon to bounce.
1539                         //      Make a copy of this weapon, because the physics wants to destroy it.
1540                         if (!Weapon_info[weapon->id].matter) {
1541                                 new_obj = obj_create(weapon->type, weapon->id, weapon->segnum, &weapon->pos,
1542                                         &weapon->orient, weapon->size, weapon->control_type, weapon->movement_type, weapon->render_type);
1543
1544                                 if (new_obj != -1) {
1545                                         vms_vector      vec_to_point;
1546                                         vms_vector      weap_vec;
1547                                         fix                     speed;
1548
1549                                         if (weapon->render_type == RT_POLYOBJ) {
1550                                                 Objects[new_obj].rtype.pobj_info.model_num = Weapon_info[Objects[new_obj].id].model_num;
1551                                                 Objects[new_obj].size = fixdiv(Polygon_models[Objects[new_obj].rtype.pobj_info.model_num].rad,Weapon_info[Objects[new_obj].id].po_len_to_width_ratio);
1552                                         }
1553
1554                                         Objects[new_obj].mtype.phys_info.mass = Weapon_info[weapon->type].mass;
1555                                         Objects[new_obj].mtype.phys_info.drag = Weapon_info[weapon->type].drag;
1556                                         vm_vec_zero(&Objects[new_obj].mtype.phys_info.thrust);
1557
1558                                         vm_vec_sub(&vec_to_point, collision_point, &robot->pos);
1559                                         vm_vec_normalize_quick(&vec_to_point);
1560                                         weap_vec = weapon->mtype.phys_info.velocity;
1561                                         speed = vm_vec_normalize_quick(&weap_vec);
1562                                         vm_vec_scale_add2(&vec_to_point, &weap_vec, -F1_0*2);
1563                                         vm_vec_scale(&vec_to_point, speed/4);
1564                                         Objects[new_obj].mtype.phys_info.velocity = vec_to_point;
1565                                 }
1566                         }
1567                 }
1568         } else if ((Weapon_info[weapon->id].matter && Boss_invulnerable_matter[d2_boss_index]) || (!Weapon_info[weapon->id].matter && Boss_invulnerable_energy[d2_boss_index])) {
1569                 int     segnum;
1570
1571                 segnum = find_point_seg(collision_point, robot->segnum);
1572                 digi_link_sound_to_pos( SOUND_WEAPON_HIT_DOOR, segnum, 0, collision_point, 0, F1_0);
1573                 damage_flag = 0;
1574         }
1575
1576         return damage_flag;
1577 }
1578
1579 extern int Robots_kill_robots_cheat;
1580
1581 //      ------------------------------------------------------------------------------------------------------
1582 void collide_robot_and_weapon( object * robot, object * weapon, vms_vector *collision_point )
1583
1584         int     damage_flag=1;
1585         int     boss_invul_flag=0;
1586
1587         if (weapon->id == OMEGA_ID)
1588                 if (!ok_to_do_omega_damage(weapon))
1589                         return;
1590
1591         if (Robot_info[robot->id].boss_flag) {
1592                 Boss_hit_time = GameTime;
1593                 if (Robot_info[robot->id].boss_flag >= BOSS_D2) {
1594                         damage_flag = do_boss_weapon_collision(robot, weapon, collision_point);
1595                         boss_invul_flag = !damage_flag;
1596                 }
1597         }
1598
1599         //      Put in at request of Jasen (and Adam) because the Buddy-Bot gets in their way.
1600         //      MK has so much fun whacking his butt around the mine he never cared...
1601         if ((Robot_info[robot->id].companion) && ((weapon->ctype.laser_info.parent_type != OBJ_ROBOT) && !Robots_kill_robots_cheat))
1602                 return;
1603
1604         if (weapon->id == EARTHSHAKER_ID)
1605                 smega_rock_stuff();
1606
1607         //      If a persistent weapon hit robot most recently, quick abort, else we cream the same robot many times,
1608         //      depending on frame rate.
1609         if (weapon->mtype.phys_info.flags & PF_PERSISTENT) {
1610                 if (weapon->ctype.laser_info.last_hitobj == robot-Objects)
1611                         return;
1612                 else
1613                         weapon->ctype.laser_info.last_hitobj = robot-Objects;
1614
1615                 // mprintf((0, "weapon #%i with power %i hits robot #%i.\n", weapon - Objects, f2i(weapon->shields), robot - Objects));
1616         }
1617
1618         if (weapon->ctype.laser_info.parent_signature == robot->signature)
1619                 return;
1620
1621         //      Changed, 10/04/95, put out blobs based on skill level and power of weapon doing damage.
1622         //      Also, only a weapon hit from a player weapon causes smart blobs.
1623         if ((weapon->ctype.laser_info.parent_type == OBJ_PLAYER) && (Robot_info[robot->id].energy_blobs))
1624                 if ((robot->shields > 0) && Weapon_is_energy[weapon->id]) {
1625                         fix     probval;
1626                         int     num_blobs;
1627
1628                         probval = (Difficulty_level+2) * min(weapon->shields, robot->shields);
1629                         probval = Robot_info[robot->id].energy_blobs * probval/(NDL*32);
1630
1631                         num_blobs = probval >> 16;
1632                         if (2*d_rand() < (probval & 0xffff))
1633                                 num_blobs++;
1634
1635                         if (num_blobs)
1636                                 create_smart_children(robot, num_blobs);
1637                 }
1638
1639         //      Note: If weapon hits an invulnerable boss, it will still do badass damage, including to the boss,
1640         //      unless this is trapped elsewhere.
1641         if ( Weapon_info[weapon->id].damage_radius )
1642         {
1643                 if (boss_invul_flag) {                  //don't make badass sound
1644                         weapon_info *wi = &Weapon_info[weapon->id];
1645
1646                         //this code copied from explode_badass_weapon()
1647                 
1648                         object_create_badass_explosion( weapon, weapon->segnum, collision_point, 
1649                                                         wi->impact_size, 
1650                                                         wi->robot_hit_vclip, 
1651                                                         wi->strength[Difficulty_level], 
1652                                                         wi->damage_radius,wi->strength[Difficulty_level],
1653                                                         weapon->ctype.laser_info.parent_num );
1654                 
1655                 }
1656                 else            //normal badass explosion
1657                         explode_badass_weapon(weapon,collision_point);
1658         }
1659
1660         if ( ((weapon->ctype.laser_info.parent_type==OBJ_PLAYER) || Robots_kill_robots_cheat) && !(robot->flags & OF_EXPLODING) )       {       
1661                 object *expl_obj=NULL;
1662
1663                 if (weapon->ctype.laser_info.parent_num == Players[Player_num].objnum) {
1664                         create_awareness_event(weapon, PA_WEAPON_ROBOT_COLLISION);                      // object "weapon" can attract attention to player
1665                         do_ai_robot_hit(robot, PA_WEAPON_ROBOT_COLLISION);
1666                 }
1667 #ifdef NETWORK
1668                 else
1669                         multi_robot_request_change(robot, Objects[weapon->ctype.laser_info.parent_num].id);
1670 #endif
1671
1672                 if ( Robot_info[robot->id].exp1_vclip_num > -1 )
1673                         expl_obj = object_create_explosion( weapon->segnum, collision_point, (robot->size/2*3)/4, Robot_info[robot->id].exp1_vclip_num );
1674                 else if ( Weapon_info[weapon->id].robot_hit_vclip > -1 )
1675                         expl_obj = object_create_explosion( weapon->segnum, collision_point, Weapon_info[weapon->id].impact_size, Weapon_info[weapon->id].robot_hit_vclip );
1676
1677                 if (expl_obj)
1678                         obj_attach(robot,expl_obj);
1679
1680                 if ( damage_flag && (Robot_info[robot->id].exp1_sound_num > -1 ))
1681                         digi_link_sound_to_pos( Robot_info[robot->id].exp1_sound_num, robot->segnum, 0, collision_point, 0, F1_0 );
1682
1683                 if (!(weapon->flags & OF_HARMLESS)) {
1684                         fix     damage = weapon->shields;
1685
1686                         if (damage_flag)
1687                                 damage = fixmul(damage, weapon->ctype.laser_info.multiplier);
1688                         else
1689                                 damage = 0;
1690
1691                         //      Cut Gauss damage on bosses because it just breaks the game.  Bosses are so easy to
1692                         //      hit, and missing a robot is what prevents the Gauss from being game-breaking.
1693                         if (weapon->id == GAUSS_ID)
1694                                 if (Robot_info[robot->id].boss_flag)
1695                                         damage = damage * (2*NDL-Difficulty_level)/(2*NDL);
1696
1697                         if (! apply_damage_to_robot(robot, damage, weapon->ctype.laser_info.parent_num))
1698                                 bump_two_objects(robot, weapon, 0);             //only bump if not dead. no damage from bump
1699                         else if (weapon->ctype.laser_info.parent_signature == ConsoleObject->signature) {
1700                                 add_points_to_score(Robot_info[robot->id].score_value);
1701                                 detect_escort_goal_accomplished(robot-Objects);
1702                         }
1703                 }
1704
1705
1706                 //      If Gauss Cannon, spin robot.
1707                 if ((robot != NULL) && (!Robot_info[robot->id].companion) && (!Robot_info[robot->id].boss_flag) && (weapon->id == GAUSS_ID)) {
1708                         ai_static       *aip = &robot->ctype.ai_info;
1709
1710                         if (aip->SKIP_AI_COUNT * FrameTime < F1_0) {
1711                                 aip->SKIP_AI_COUNT++;
1712                                 robot->mtype.phys_info.rotthrust.x = fixmul((d_rand() - 16384), FrameTime * aip->SKIP_AI_COUNT);
1713                                 robot->mtype.phys_info.rotthrust.y = fixmul((d_rand() - 16384), FrameTime * aip->SKIP_AI_COUNT);
1714                                 robot->mtype.phys_info.rotthrust.z = fixmul((d_rand() - 16384), FrameTime * aip->SKIP_AI_COUNT);
1715                                 robot->mtype.phys_info.flags |= PF_USES_THRUST;
1716
1717                         }
1718                 }
1719
1720         }
1721
1722         maybe_kill_weapon(weapon,robot);
1723
1724         return; 
1725 }
1726
1727 //##void collide_robot_and_camera( object * robot, object * camera, vms_vector *collision_point ) { 
1728 //##    return; 
1729 //##}
1730
1731 //##void collide_robot_and_powerup( object * robot, object * powerup, vms_vector *collision_point ) { 
1732 //##    return; 
1733 //##}
1734
1735 //##void collide_robot_and_debris( object * robot, object * debris, vms_vector *collision_point ) { 
1736 //##    return; 
1737 //##}
1738
1739 //##void collide_hostage_and_hostage( object * hostage1, object * hostage2, vms_vector *collision_point ) { 
1740 //##    return; 
1741 //##}
1742
1743 void collide_hostage_and_player( object * hostage, object * player, vms_vector *collision_point ) { 
1744         // Give player points, etc.
1745         if ( player == ConsoleObject )  {
1746                 detect_escort_goal_accomplished(hostage-Objects);
1747                 add_points_to_score(HOSTAGE_SCORE);
1748
1749                 // Do effect
1750                 hostage_rescue(hostage->id);
1751
1752                 // Remove the hostage object.
1753                 hostage->flags |= OF_SHOULD_BE_DEAD;
1754
1755                 #ifdef NETWORK  
1756                 if (Game_mode & GM_MULTI)
1757                         multi_send_remobj(hostage-Objects);
1758                 #endif
1759         }
1760         return; 
1761 }
1762
1763 //--unused-- void collide_hostage_and_weapon( object * hostage, object * weapon, vms_vector *collision_point )
1764 //--unused-- { 
1765 //--unused--    //      Cannot kill hostages, as per Matt's edict!
1766 //--unused--    //      (A fine edict, but in contradiction to the milestone: "Robots attack hostages.")
1767 //--unused--    hostage->shields -= weapon->shields/2;
1768 //--unused-- 
1769 //--unused--    create_awareness_event(weapon, PA_WEAPON_ROBOT_COLLISION);                      // object "weapon" can attract attention to player
1770 //--unused-- 
1771 //--unused--    //PLAY_SOUND_3D( SOUND_HOSTAGE_KILLED, collision_point, hostage->segnum );
1772 //--unused--    digi_link_sound_to_pos( SOUND_HOSTAGE_KILLED, hostage->segnum , 0, collision_point, 0, F1_0 );
1773 //--unused-- 
1774 //--unused-- 
1775 //--unused--    if (hostage->shields <= 0) {
1776 //--unused--            explode_object(hostage,0);
1777 //--unused--            hostage->flags |= OF_SHOULD_BE_DEAD;
1778 //--unused--    }
1779 //--unused-- 
1780 //--unused--    if ( Weapon_info[weapon->id].damage_radius )
1781 //--unused--            explode_badass_weapon(weapon);
1782 //--unused-- 
1783 //--unused--    maybe_kill_weapon(weapon,hostage);
1784 //--unused-- 
1785 //--unused-- }
1786
1787 //##void collide_hostage_and_camera( object * hostage, object * camera, vms_vector *collision_point ) { 
1788 //##    return; 
1789 //##}
1790
1791 //##void collide_hostage_and_powerup( object * hostage, object * powerup, vms_vector *collision_point ) { 
1792 //##    return; 
1793 //##}
1794
1795 //##void collide_hostage_and_debris( object * hostage, object * debris, vms_vector *collision_point ) { 
1796 //##    return; 
1797 //##}
1798
1799 void collide_player_and_player( object * player1, object * player2, vms_vector *collision_point ) { 
1800         digi_link_sound_to_pos( SOUND_ROBOT_HIT_PLAYER, player1->segnum, 0, collision_point, 0, F1_0 );
1801         bump_two_objects(player1, player2, 1);
1802         return;
1803 }
1804
1805 int maybe_drop_primary_weapon_egg(object *playerobj, int weapon_index)
1806 {
1807         int weapon_flag = HAS_FLAG(weapon_index);
1808         int powerup_num;
1809
1810         powerup_num = Primary_weapon_to_powerup[weapon_index];
1811
1812         if (Players[playerobj->id].primary_weapon_flags & weapon_flag)
1813                 return call_object_create_egg(playerobj, 1, OBJ_POWERUP, powerup_num);
1814         else
1815                 return -1;
1816 }
1817
1818 void maybe_drop_secondary_weapon_egg(object *playerobj, int weapon_index, int count)
1819 {
1820         int weapon_flag = HAS_FLAG(weapon_index);
1821         int powerup_num;
1822
1823         powerup_num = Secondary_weapon_to_powerup[weapon_index];
1824
1825         if (Players[playerobj->id].secondary_weapon_flags & weapon_flag) {
1826                 int     i, max_count;
1827
1828                 max_count = min(count, 3);
1829                 for (i=0; i<max_count; i++)
1830                         call_object_create_egg(playerobj, 1, OBJ_POWERUP, powerup_num);
1831         }
1832 }
1833
1834 void drop_missile_1_or_4(object *playerobj,int missile_index)
1835 {
1836         int num_missiles,powerup_id;
1837
1838         num_missiles = Players[playerobj->id].secondary_ammo[missile_index];
1839         powerup_id = Secondary_weapon_to_powerup[missile_index];
1840
1841         if (num_missiles > 10)
1842                 num_missiles = 10;
1843
1844         call_object_create_egg(playerobj, num_missiles/4, OBJ_POWERUP, powerup_id+1);
1845         call_object_create_egg(playerobj, num_missiles%4, OBJ_POWERUP, powerup_id);
1846 }
1847
1848 // -- int       Items_destroyed = 0;
1849
1850 void drop_player_eggs(object *playerobj)
1851 {
1852 //      mprintf((0, "In drop_player_eggs...\n"));
1853
1854         if ((playerobj->type == OBJ_PLAYER) || (playerobj->type == OBJ_GHOST)) {
1855                 int     rthresh;
1856                 int     pnum = playerobj->id;
1857                 int     objnum;
1858                 int     vulcan_ammo=0;
1859                 vms_vector      randvec;
1860
1861                 // -- Items_destroyed = 0;
1862
1863                 // Seed the random number generator so in net play the eggs will always
1864                 // drop the same way
1865                 #ifdef NETWORK
1866                 if (Game_mode & GM_MULTI) 
1867                 {
1868                         Net_create_loc = 0;
1869                         d_srand(5483L);
1870                 }
1871                 #endif
1872
1873                 //      If the player had smart mines, maybe arm one of them.
1874                 rthresh = 30000;
1875                 while ((Players[playerobj->id].secondary_ammo[SMART_MINE_INDEX]%4==1) && (d_rand() < rthresh)) {
1876                         int                     newseg;
1877                         vms_vector      tvec;
1878
1879                         make_random_vector(&randvec);
1880                         rthresh /= 2;
1881                         vm_vec_add(&tvec, &playerobj->pos, &randvec);
1882                         newseg = find_point_seg(&tvec, playerobj->segnum);
1883                         if (newseg != -1)
1884                                 Laser_create_new(&randvec, &tvec, newseg, playerobj-Objects, SUPERPROX_ID, 0);
1885                 }
1886
1887                 //      If the player had proximity bombs, maybe arm one of them.
1888
1889                 if ((Game_mode & GM_MULTI) && !(Game_mode & GM_HOARD))
1890                 {
1891                         rthresh = 30000;
1892                         while ((Players[playerobj->id].secondary_ammo[PROXIMITY_INDEX]%4==1) && (d_rand() < rthresh)) {
1893                                 int                     newseg;
1894                                 vms_vector      tvec;
1895         
1896                                 make_random_vector(&randvec);
1897                                 rthresh /= 2;
1898                                 vm_vec_add(&tvec, &playerobj->pos, &randvec);
1899                                 newseg = find_point_seg(&tvec, playerobj->segnum);
1900                                 if (newseg != -1)
1901                                         Laser_create_new(&randvec, &tvec, newseg, playerobj-Objects, PROXIMITY_ID, 0);
1902         
1903                         }
1904                 }
1905
1906                 //      If the player dies and he has powerful lasers, create the powerups here.
1907
1908                 if (Players[pnum].laser_level > MAX_LASER_LEVEL)
1909                         call_object_create_egg(playerobj, Players[pnum].laser_level-MAX_LASER_LEVEL, OBJ_POWERUP, POW_SUPER_LASER);
1910                 else if (Players[pnum].laser_level >= 1)
1911                         call_object_create_egg(playerobj, Players[pnum].laser_level, OBJ_POWERUP, POW_LASER);   // Note: laser_level = 0 for laser level 1.
1912
1913                 //      Drop quad laser if appropos
1914                 if (Players[pnum].flags & PLAYER_FLAGS_QUAD_LASERS)
1915                         call_object_create_egg(playerobj, 1, OBJ_POWERUP, POW_QUAD_FIRE);
1916
1917                 if (Players[pnum].flags & PLAYER_FLAGS_CLOAKED)
1918                         call_object_create_egg(playerobj, 1, OBJ_POWERUP, POW_CLOAK);
1919
1920                 if (Players[pnum].flags & PLAYER_FLAGS_MAP_ALL)
1921                         call_object_create_egg(playerobj, 1, OBJ_POWERUP, POW_FULL_MAP);
1922
1923                 if (Players[pnum].flags & PLAYER_FLAGS_AFTERBURNER)
1924                         call_object_create_egg(playerobj, 1, OBJ_POWERUP, POW_AFTERBURNER);
1925
1926                 if (Players[pnum].flags & PLAYER_FLAGS_AMMO_RACK)
1927                         call_object_create_egg(playerobj, 1, OBJ_POWERUP, POW_AMMO_RACK);
1928
1929                 if (Players[pnum].flags & PLAYER_FLAGS_CONVERTER)
1930                         call_object_create_egg(playerobj, 1, OBJ_POWERUP, POW_CONVERTER);
1931
1932                 if (Players[pnum].flags & PLAYER_FLAGS_HEADLIGHT)
1933                         call_object_create_egg(playerobj, 1, OBJ_POWERUP, POW_HEADLIGHT);
1934
1935                 // drop the other enemies flag if you have it
1936
1937 #ifdef NETWORK
1938                 if ((Game_mode & GM_CAPTURE) && (Players[pnum].flags & PLAYER_FLAGS_FLAG))
1939                 {
1940                  if ((get_team (pnum)==TEAM_RED))
1941                         call_object_create_egg(playerobj, 1, OBJ_POWERUP, POW_FLAG_BLUE);
1942                  else
1943                         call_object_create_egg(playerobj, 1, OBJ_POWERUP, POW_FLAG_RED);
1944                 }
1945
1946         
1947                 if (Game_mode & GM_HOARD)
1948                 {
1949                         // Drop hoard orbs
1950                         
1951                         int max_count,i;
1952
1953                         mprintf ((0,"HOARD MODE: Dropping %d orbs\n",Players[pnum].secondary_ammo[PROXIMITY_INDEX]));
1954         
1955                         max_count = min(Players[pnum].secondary_ammo[PROXIMITY_INDEX], 12);
1956                         for (i=0; i<max_count; i++)
1957                                 call_object_create_egg(playerobj, 1, OBJ_POWERUP, POW_HOARD_ORB);
1958                 }
1959 #endif
1960
1961                 //Drop the vulcan, gauss, and ammo
1962                 vulcan_ammo = Players[pnum].primary_ammo[VULCAN_INDEX];
1963                 if ((Players[pnum].primary_weapon_flags & HAS_FLAG(VULCAN_INDEX)) && (Players[pnum].primary_weapon_flags & HAS_FLAG(GAUSS_INDEX)))
1964                         vulcan_ammo /= 2;               //if both vulcan & gauss, each gets half
1965                 if (vulcan_ammo < VULCAN_AMMO_AMOUNT)
1966                         vulcan_ammo = VULCAN_AMMO_AMOUNT;       //make sure gun has at least as much as a powerup
1967                 objnum = maybe_drop_primary_weapon_egg(playerobj, VULCAN_INDEX);
1968                 if (objnum!=-1)
1969                         Objects[objnum].ctype.powerup_info.count = vulcan_ammo;
1970                 objnum = maybe_drop_primary_weapon_egg(playerobj, GAUSS_INDEX);
1971                 if (objnum!=-1)
1972                         Objects[objnum].ctype.powerup_info.count = vulcan_ammo;
1973
1974                 //      Drop the rest of the primary weapons
1975                 maybe_drop_primary_weapon_egg(playerobj, SPREADFIRE_INDEX);
1976                 maybe_drop_primary_weapon_egg(playerobj, PLASMA_INDEX);
1977                 maybe_drop_primary_weapon_egg(playerobj, FUSION_INDEX);
1978
1979                 maybe_drop_primary_weapon_egg(playerobj, HELIX_INDEX);
1980                 maybe_drop_primary_weapon_egg(playerobj, PHOENIX_INDEX);
1981
1982                 objnum = maybe_drop_primary_weapon_egg(playerobj, OMEGA_INDEX);
1983                 if (objnum!=-1)
1984                         Objects[objnum].ctype.powerup_info.count = (playerobj->id==Player_num)?Omega_charge:MAX_OMEGA_CHARGE;
1985
1986                 //      Drop the secondary weapons
1987                 //      Note, proximity weapon only comes in packets of 4.  So drop n/2, but a max of 3 (handled inside maybe_drop..)  Make sense?
1988                 
1989                 if (!(Game_mode & GM_HOARD))
1990                         maybe_drop_secondary_weapon_egg(playerobj, PROXIMITY_INDEX, (Players[playerobj->id].secondary_ammo[PROXIMITY_INDEX])/4);
1991
1992                 maybe_drop_secondary_weapon_egg(playerobj, SMART_INDEX, Players[playerobj->id].secondary_ammo[SMART_INDEX]);
1993                 maybe_drop_secondary_weapon_egg(playerobj, MEGA_INDEX, Players[playerobj->id].secondary_ammo[MEGA_INDEX]);
1994
1995                 maybe_drop_secondary_weapon_egg(playerobj, SMART_MINE_INDEX,(Players[playerobj->id].secondary_ammo[SMART_MINE_INDEX])/4);
1996                 maybe_drop_secondary_weapon_egg(playerobj, SMISSILE5_INDEX, Players[playerobj->id].secondary_ammo[SMISSILE5_INDEX]);
1997
1998                 //      Drop the player's missiles in packs of 1 and/or 4
1999                 drop_missile_1_or_4(playerobj,HOMING_INDEX);
2000                 drop_missile_1_or_4(playerobj,GUIDED_INDEX);
2001                 drop_missile_1_or_4(playerobj,CONCUSSION_INDEX);
2002                 drop_missile_1_or_4(playerobj,SMISSILE1_INDEX);
2003                 drop_missile_1_or_4(playerobj,SMISSILE4_INDEX);
2004
2005                 //      If player has vulcan ammo, but no vulcan cannon, drop the ammo.
2006                 if (!(Players[playerobj->id].primary_weapon_flags & HAS_VULCAN_FLAG)) {
2007                         int     amount = Players[playerobj->id].primary_ammo[VULCAN_INDEX];
2008                         if (amount > 200) {
2009                                 mprintf((0, "Surprising amount of vulcan ammo: %i bullets.\n", amount));
2010                                 amount = 200;
2011                         }
2012                         while (amount > 0) {
2013                                 call_object_create_egg(playerobj, 1, OBJ_POWERUP, POW_VULCAN_AMMO);
2014                                 amount -= VULCAN_AMMO_AMOUNT;
2015                         }
2016                 }
2017
2018                 //      Always drop a shield and energy powerup.
2019                 if (Game_mode & GM_MULTI) {
2020                         call_object_create_egg(playerobj, 1, OBJ_POWERUP, POW_SHIELD_BOOST);
2021                         call_object_create_egg(playerobj, 1, OBJ_POWERUP, POW_ENERGY);
2022                 }
2023
2024 //--            //      Drop all the keys.
2025 //--            if (Players[Player_num].flags & PLAYER_FLAGS_BLUE_KEY) {
2026 //--                    playerobj->contains_count = 1;
2027 //--                    playerobj->contains_type = OBJ_POWERUP;
2028 //--                    playerobj->contains_id = POW_KEY_BLUE;
2029 //--                    object_create_egg(playerobj);
2030 //--            }
2031 //--            if (Players[Player_num].flags & PLAYER_FLAGS_RED_KEY) {
2032 //--                    playerobj->contains_count = 1;
2033 //--                    playerobj->contains_type = OBJ_POWERUP;
2034 //--                    playerobj->contains_id = POW_KEY_RED;
2035 //--                    object_create_egg(playerobj);
2036 //--            }
2037 //--            if (Players[Player_num].flags & PLAYER_FLAGS_GOLD_KEY) {
2038 //--                    playerobj->contains_count = 1;
2039 //--                    playerobj->contains_type = OBJ_POWERUP;
2040 //--                    playerobj->contains_id = POW_KEY_GOLD;
2041 //--                    object_create_egg(playerobj);
2042 //--            }
2043
2044 // --           if (Items_destroyed) {
2045 // --                   if (Items_destroyed == 1)
2046 // --                           HUD_init_message("%i item was destroyed.", Items_destroyed);
2047 // --                   else
2048 // --                           HUD_init_message("%i items were destroyed.", Items_destroyed);
2049 // --                   Items_destroyed = 0;
2050 // --           }
2051         }
2052
2053 }
2054
2055 // -- removed, 09/06/95, MK -- void destroy_primary_weapon(int weapon_index)
2056 // -- removed, 09/06/95, MK -- {
2057 // -- removed, 09/06/95, MK --  if (weapon_index == MAX_PRIMARY_WEAPONS) {
2058 // -- removed, 09/06/95, MK --          HUD_init_message("Quad lasers destroyed!");
2059 // -- removed, 09/06/95, MK --          Players[Player_num].flags &= ~PLAYER_FLAGS_QUAD_LASERS;
2060 // -- removed, 09/06/95, MK --          update_laser_weapon_info();
2061 // -- removed, 09/06/95, MK --  } else if (weapon_index == 0) {
2062 // -- removed, 09/06/95, MK --          Assert(Players[Player_num].laser_level > 0);
2063 // -- removed, 09/06/95, MK --          HUD_init_message("%s degraded!", Text_string[104+weapon_index]);                //      Danger! Danger! Use of literal!  Danger!
2064 // -- removed, 09/06/95, MK --          Players[Player_num].laser_level--;
2065 // -- removed, 09/06/95, MK --          update_laser_weapon_info();
2066 // -- removed, 09/06/95, MK --  } else {
2067 // -- removed, 09/06/95, MK --          HUD_init_message("%s destroyed!", Text_string[104+weapon_index]);               //      Danger! Danger! Use of literal!  Danger!
2068 // -- removed, 09/06/95, MK --          Players[Player_num].primary_weapon_flags &= ~(1 << weapon_index);
2069 // -- removed, 09/06/95, MK --          auto_select_weapon(0);
2070 // -- removed, 09/06/95, MK --  }
2071 // -- removed, 09/06/95, MK -- 
2072 // -- removed, 09/06/95, MK -- }
2073 // -- removed, 09/06/95, MK -- 
2074 // -- removed, 09/06/95, MK -- void destroy_secondary_weapon(int weapon_index)
2075 // -- removed, 09/06/95, MK -- {
2076 // -- removed, 09/06/95, MK --  if (Players[Player_num].secondary_ammo <= 0)
2077 // -- removed, 09/06/95, MK --          return;
2078 // -- removed, 09/06/95, MK -- 
2079 // -- removed, 09/06/95, MK --  HUD_init_message("%s destroyed!", Text_string[114+weapon_index]);               //      Danger! Danger! Use of literal!  Danger!
2080 // -- removed, 09/06/95, MK --  if (--Players[Player_num].secondary_ammo[weapon_index] == 0)
2081 // -- removed, 09/06/95, MK --          auto_select_weapon(1);
2082 // -- removed, 09/06/95, MK -- 
2083 // -- removed, 09/06/95, MK -- }
2084 // -- removed, 09/06/95, MK -- 
2085 // -- removed, 09/06/95, MK -- #define  LOSE_WEAPON_THRESHOLD   (F1_0*30)
2086
2087 extern fix Buddy_sorry_time;
2088
2089 void apply_damage_to_player(object *playerobj, object *killer, fix damage)
2090 {
2091         if (Player_is_dead)
2092                 return;
2093
2094         if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)
2095                 return;
2096
2097         if (Endlevel_sequence)
2098                 return;
2099
2100         //for the player, the 'real' shields are maintained in the Players[]
2101         //array.  The shields value in the player's object are, I think, not
2102         //used anywhere.  This routine, however, sets the objects shields to
2103         //be a mirror of the value in the Player structure. 
2104
2105         if (playerobj->id == Player_num) {              //is this the local player?
2106
2107                 //      MK: 08/14/95: This code can never be reached.  See the return about 12 lines up.
2108 // --           if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE) {
2109 // -- 
2110 // --                   //invincible, so just do blue flash
2111 // -- 
2112 // --                   PALETTE_FLASH_ADD(0,0,f2i(damage)*4);   //flash blue
2113 // -- 
2114 // --           } 
2115 // --           else {          //take damage, do red flash
2116
2117                         Players[Player_num].shields -= damage;
2118
2119                         PALETTE_FLASH_ADD(f2i(damage)*4,-f2i(damage/2),-f2i(damage/2)); //flash red
2120
2121 // --           }
2122
2123                 if (Players[Player_num].shields < 0)    {
2124
2125                         Players[Player_num].killer_objnum = killer-Objects;
2126                         
2127 //                      if ( killer && (killer->type == OBJ_PLAYER))
2128 //                              Players[Player_num].killer_objnum = killer-Objects;
2129
2130                         playerobj->flags |= OF_SHOULD_BE_DEAD;
2131
2132                         if (Buddy_objnum != -1)
2133                                 if (killer && (killer->type == OBJ_ROBOT) && (Robot_info[killer->id].companion))
2134                                         Buddy_sorry_time = GameTime;
2135                 }
2136 // -- removed, 09/06/95, MK --  else if (Players[Player_num].shields < LOSE_WEAPON_THRESHOLD) {
2137 // -- removed, 09/06/95, MK --                  int     randnum = d_rand();
2138 // -- removed, 09/06/95, MK -- 
2139 // -- removed, 09/06/95, MK --                  if (fixmul(Players[Player_num].shields, randnum) < damage/4) {
2140 // -- removed, 09/06/95, MK --                          if (d_rand() > 20000) {
2141 // -- removed, 09/06/95, MK --                                  destroy_secondary_weapon(Secondary_weapon);
2142 // -- removed, 09/06/95, MK --                          } else if (Primary_weapon == 0) {
2143 // -- removed, 09/06/95, MK --                                  if (Players[Player_num].flags & PLAYER_FLAGS_QUAD_LASERS)
2144 // -- removed, 09/06/95, MK --                                          destroy_primary_weapon(MAX_PRIMARY_WEAPONS);    //      This means to destroy quad laser.
2145 // -- removed, 09/06/95, MK --                                  else if (Players[Player_num].laser_level > 0)
2146 // -- removed, 09/06/95, MK --                                          destroy_primary_weapon(Primary_weapon);
2147 // -- removed, 09/06/95, MK --                          } else
2148 // -- removed, 09/06/95, MK --                                  destroy_primary_weapon(Primary_weapon);
2149 // -- removed, 09/06/95, MK --                  } else
2150 // -- removed, 09/06/95, MK --                          ; // mprintf((0, "%8x > %8x, so don't lose weapon.\n", fixmul(Players[Player_num].shields, randnum), damage/4));
2151 // -- removed, 09/06/95, MK --          }
2152
2153                 playerobj->shields = Players[Player_num].shields;               //mirror
2154
2155         }
2156 }
2157
2158 void collide_player_and_weapon( object * playerobj, object * weapon, vms_vector *collision_point )
2159 {
2160         fix             damage = weapon->shields;
2161         object * killer=NULL;
2162
2163         //      In multiplayer games, only do damage to another player if in first frame.
2164         //      This is necessary because in multiplayer, due to varying framerates, omega blobs actually
2165         //      have a bit of a lifetime.  But they start out with a lifetime of ONE_FRAME_TIME, and this
2166         //      gets bashed to 1/4 second in laser_do_weapon_sequence.  This bashing occurs for visual purposes only.
2167         if (weapon->id == OMEGA_ID)
2168                 if (!ok_to_do_omega_damage(weapon))
2169                         return;
2170
2171         //      Don't collide own smart mines unless direct hit.
2172         if (weapon->id == SUPERPROX_ID)
2173                 if (playerobj-Objects == weapon->ctype.laser_info.parent_num)
2174                         if (vm_vec_dist_quick(collision_point, &playerobj->pos) > playerobj->size)
2175                                 return;
2176
2177         if (weapon->id == EARTHSHAKER_ID)
2178                 smega_rock_stuff();
2179
2180         damage = fixmul(damage, weapon->ctype.laser_info.multiplier);
2181         if (Game_mode & GM_MULTI)
2182                 damage = fixmul(damage, Weapon_info[weapon->id].multi_damage_scale);
2183
2184         if (weapon->mtype.phys_info.flags & PF_PERSISTENT)
2185         {
2186                 if (weapon->ctype.laser_info.last_hitobj == playerobj-Objects)
2187                         return;
2188                 else
2189                         weapon->ctype.laser_info.last_hitobj = playerobj-Objects;
2190         }
2191
2192         if (playerobj->id == Player_num)
2193         {
2194                 if (!(Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
2195                 {
2196                         digi_link_sound_to_pos( SOUND_PLAYER_GOT_HIT, playerobj->segnum, 0, collision_point, 0, F1_0 );
2197                         #ifdef NETWORK
2198                         if (Game_mode & GM_MULTI)
2199                                 multi_send_play_sound(SOUND_PLAYER_GOT_HIT, F1_0);
2200                         #endif
2201                 }
2202                 else
2203                 {
2204                         digi_link_sound_to_pos( SOUND_WEAPON_HIT_DOOR, playerobj->segnum, 0, collision_point, 0, F1_0);
2205                         #ifdef NETWORK
2206                         if (Game_mode & GM_MULTI)
2207                                 multi_send_play_sound(SOUND_WEAPON_HIT_DOOR, F1_0);
2208                         #endif
2209                 }
2210         }
2211
2212         object_create_explosion( playerobj->segnum, collision_point, i2f(10)/2, VCLIP_PLAYER_HIT );
2213         if ( Weapon_info[weapon->id].damage_radius )
2214                 explode_badass_weapon(weapon,collision_point);
2215
2216         maybe_kill_weapon(weapon,playerobj);
2217
2218         bump_two_objects(playerobj, weapon, 0); //no damage from bump
2219
2220         if ( !Weapon_info[weapon->id].damage_radius ) {
2221                 if ( weapon->ctype.laser_info.parent_num > -1 )
2222                         killer = &Objects[weapon->ctype.laser_info.parent_num];
2223
2224 //              if (weapon->id == SMART_HOMING_ID)
2225 //                      damage /= 4;
2226
2227                 if (!(weapon->flags & OF_HARMLESS))
2228                         apply_damage_to_player( playerobj, killer, damage);
2229         }
2230
2231         //      Robots become aware of you if you get hit.
2232         ai_do_cloak_stuff();
2233
2234         return; 
2235 }
2236
2237 //      Nasty robots are the ones that attack you by running into you and doing lots of damage.
2238 void collide_player_and_nasty_robot( object * playerobj, object * robot, vms_vector *collision_point )
2239 {
2240 //      if (!(Robot_info[robot->id].energy_drain && Players[playerobj->id].energy))
2241                 digi_link_sound_to_pos( Robot_info[robot->id].claw_sound, playerobj->segnum, 0, collision_point, 0, F1_0 );
2242
2243         object_create_explosion( playerobj->segnum, collision_point, i2f(10)/2, VCLIP_PLAYER_HIT );
2244
2245         bump_two_objects(playerobj, robot, 0);  //no damage from bump
2246
2247         apply_damage_to_player( playerobj, robot, F1_0*(Difficulty_level+1));
2248
2249         return; 
2250 }
2251
2252 void collide_player_and_materialization_center(object *objp)
2253 {
2254         int     side;
2255         vms_vector      exit_dir;
2256         segment *segp = &Segments[objp->segnum];
2257
2258         digi_link_sound_to_pos(SOUND_PLAYER_GOT_HIT, objp->segnum, 0, &objp->pos, 0, F1_0);
2259 //      digi_play_sample( SOUND_PLAYER_GOT_HIT, F1_0 );
2260
2261         object_create_explosion( objp->segnum, &objp->pos, i2f(10)/2, VCLIP_PLAYER_HIT );
2262
2263         if (objp->id != Player_num)
2264                 return;
2265
2266         for (side=0; side<MAX_SIDES_PER_SEGMENT; side++)
2267                 if (WALL_IS_DOORWAY(segp, side) & WID_FLY_FLAG) {
2268                         vms_vector      exit_point, rand_vec;
2269
2270                         compute_center_point_on_side(&exit_point, segp, side);
2271                         vm_vec_sub(&exit_dir, &exit_point, &objp->pos);
2272                         vm_vec_normalize_quick(&exit_dir);
2273                         make_random_vector(&rand_vec);
2274                         rand_vec.x /= 4;        rand_vec.y /= 4;        rand_vec.z /= 4;
2275                         vm_vec_add2(&exit_dir, &rand_vec);
2276                         vm_vec_normalize_quick(&exit_dir);
2277                 }
2278
2279         bump_one_object(objp, &exit_dir, 64*F1_0);
2280
2281         apply_damage_to_player( objp, objp, 4*F1_0);    //      Changed, MK, 2/19/96, make killer the player, so if you die in matcen, will say you killed yourself
2282
2283         return; 
2284
2285 }
2286
2287 void collide_robot_and_materialization_center(object *objp)
2288 {
2289         int     side;
2290         vms_vector      exit_dir;
2291         segment *segp=&Segments[objp->segnum];
2292
2293         digi_link_sound_to_pos(SOUND_ROBOT_HIT, objp->segnum, 0, &objp->pos, 0, F1_0);
2294 //      digi_play_sample( SOUND_ROBOT_HIT, F1_0 );
2295
2296         if ( Robot_info[objp->id].exp1_vclip_num > -1 )
2297                 object_create_explosion( objp->segnum, &objp->pos, (objp->size/2*3)/4, Robot_info[objp->id].exp1_vclip_num );
2298
2299         for (side=0; side<MAX_SIDES_PER_SEGMENT; side++)
2300                 if (WALL_IS_DOORWAY(segp, side) & WID_FLY_FLAG) {
2301                         vms_vector      exit_point;
2302
2303                         compute_center_point_on_side(&exit_point, segp, side);
2304                         vm_vec_sub(&exit_dir, &exit_point, &objp->pos);
2305                         vm_vec_normalize_quick(&exit_dir);
2306                 }
2307
2308         bump_one_object(objp, &exit_dir, 8*F1_0);
2309
2310         apply_damage_to_robot( objp, F1_0, -1);
2311
2312         return; 
2313
2314 }
2315
2316 //##void collide_player_and_camera( object * playerobj, object * camera, vms_vector *collision_point ) { 
2317 //##    return; 
2318 //##}
2319
2320 extern int Network_got_powerup; // HACK!!!
2321
2322 void collide_player_and_powerup( object * playerobj, object * powerup, vms_vector *collision_point ) { 
2323         if (!Endlevel_sequence && !Player_is_dead && (playerobj->id == Player_num )) {
2324                 int powerup_used;
2325
2326                 powerup_used = do_powerup(powerup);
2327                 
2328                 if (powerup_used)       {
2329                         powerup->flags |= OF_SHOULD_BE_DEAD;
2330                         #ifdef NETWORK
2331                         if (Game_mode & GM_MULTI)
2332                                 multi_send_remobj(powerup-Objects);
2333                         #endif
2334                 }
2335         }
2336 #ifndef SHAREWARE
2337         else if ((Game_mode & GM_MULTI_COOP) && (playerobj->id != Player_num))
2338         {
2339                 switch (powerup->id) {
2340                         case POW_KEY_BLUE:      
2341                                 Players[playerobj->id].flags |= PLAYER_FLAGS_BLUE_KEY;
2342                                 break;
2343                         case POW_KEY_RED:       
2344                                 Players[playerobj->id].flags |= PLAYER_FLAGS_RED_KEY;
2345                                 break;
2346                         case POW_KEY_GOLD:      
2347                                 Players[playerobj->id].flags |= PLAYER_FLAGS_GOLD_KEY;
2348                                 break;
2349                         default:
2350                                 break;
2351                 }
2352         }
2353 #endif
2354         return; 
2355 }
2356
2357 //##void collide_player_and_debris( object * playerobj, object * debris, vms_vector *collision_point ) { 
2358 //##    return; 
2359 //##}
2360
2361 void collide_player_and_clutter( object * playerobj, object * clutter, vms_vector *collision_point ) { 
2362         digi_link_sound_to_pos( SOUND_ROBOT_HIT_PLAYER, playerobj->segnum, 0, collision_point, 0, F1_0 );
2363         bump_two_objects(clutter, playerobj, 1);
2364         return; 
2365 }
2366
2367 //      See if weapon1 creates a badass explosion.  If so, create the explosion
2368 //      Return true if weapon does proximity (as opposed to only contact) damage when it explodes.
2369 int maybe_detonate_weapon(object *weapon1, object *weapon2, vms_vector *collision_point)
2370 {
2371         if ( Weapon_info[weapon1->id].damage_radius ) {
2372                 fix     dist;
2373
2374                 dist = vm_vec_dist_quick(&weapon1->pos, &weapon2->pos);
2375                 if (dist < F1_0*5) {
2376                         maybe_kill_weapon(weapon1,weapon2);
2377                         if (weapon1->flags & OF_SHOULD_BE_DEAD) {
2378                                 explode_badass_weapon(weapon1,collision_point);
2379                                 digi_link_sound_to_pos( Weapon_info[weapon1->id].robot_hit_sound, weapon1->segnum , 0, collision_point, 0, F1_0 );
2380                         }
2381                         return 1;
2382                 } else {
2383                         weapon1->lifeleft = min(dist/64, F1_0);
2384                         return 1;
2385                 }
2386         } else
2387                 return 0;
2388 }
2389
2390 void collide_weapon_and_weapon( object * weapon1, object * weapon2, vms_vector *collision_point )
2391
2392         // -- Does this look buggy??:  if (weapon1->id == PMINE_ID && weapon1->id == PMINE_ID)
2393         if (weapon1->id == PMINE_ID && weapon2->id == PMINE_ID)
2394                 return;         //these can't blow each other up  
2395
2396         if (weapon1->id == OMEGA_ID) {
2397                 if (!ok_to_do_omega_damage(weapon1))
2398                         return;
2399         } else if (weapon2->id == OMEGA_ID) {
2400                 if (!ok_to_do_omega_damage(weapon2))
2401                         return;
2402         }
2403
2404         if ((Weapon_info[weapon1->id].destroyable) || (Weapon_info[weapon2->id].destroyable)) {
2405
2406                 //      Bug reported by Adam Q. Pletcher on September 9, 1994, smart bomb homing missiles were toasting each other.
2407                 if ((weapon1->id == weapon2->id) && (weapon1->ctype.laser_info.parent_num == weapon2->ctype.laser_info.parent_num))
2408                         return;
2409
2410                 if (Weapon_info[weapon1->id].destroyable)
2411                         if (maybe_detonate_weapon(weapon1, weapon2, collision_point))
2412                                 maybe_detonate_weapon(weapon2,weapon1, collision_point);
2413
2414                 if (Weapon_info[weapon2->id].destroyable)
2415                         if (maybe_detonate_weapon(weapon2, weapon1, collision_point))
2416                                 maybe_detonate_weapon(weapon1,weapon2, collision_point);
2417
2418         }
2419
2420 }
2421
2422 //##void collide_weapon_and_camera( object * weapon, object * camera, vms_vector *collision_point ) { 
2423 //##    return; 
2424 //##}
2425
2426 //##void collide_weapon_and_powerup( object * weapon, object * powerup, vms_vector *collision_point ) { 
2427 //##    return; 
2428 //##}
2429
2430 void collide_weapon_and_debris( object * weapon, object * debris, vms_vector *collision_point ) { 
2431
2432         //      Hack!  Prevent debris from causing bombs spewed at player death to detonate!
2433         if ((weapon->id == PROXIMITY_ID) || (weapon->id == SUPERPROX_ID)) {
2434                 if (weapon->ctype.laser_info.creation_time + F1_0/2 > GameTime)
2435                         return;
2436         }
2437
2438         if ( (weapon->ctype.laser_info.parent_type==OBJ_PLAYER) && !(debris->flags & OF_EXPLODING) )    {       
2439                 digi_link_sound_to_pos( SOUND_ROBOT_HIT, weapon->segnum , 0, collision_point, 0, F1_0 );
2440
2441                 explode_object(debris,0);
2442                 if ( Weapon_info[weapon->id].damage_radius )
2443                         explode_badass_weapon(weapon,collision_point);
2444                 maybe_kill_weapon(weapon,debris);
2445                 weapon->flags |= OF_SHOULD_BE_DEAD;
2446         }
2447         return; 
2448 }
2449
2450 //##void collide_camera_and_camera( object * camera1, object * camera2, vms_vector *collision_point ) { 
2451 //##    return; 
2452 //##}
2453
2454 //##void collide_camera_and_powerup( object * camera, object * powerup, vms_vector *collision_point ) { 
2455 //##    return; 
2456 //##}
2457
2458 //##void collide_camera_and_debris( object * camera, object * debris, vms_vector *collision_point ) { 
2459 //##    return; 
2460 //##}
2461
2462 //##void collide_powerup_and_powerup( object * powerup1, object * powerup2, vms_vector *collision_point ) { 
2463 //##    return; 
2464 //##}
2465
2466 //##void collide_powerup_and_debris( object * powerup, object * debris, vms_vector *collision_point ) { 
2467 //##    return; 
2468 //##}
2469
2470 //##void collide_debris_and_debris( object * debris1, object * debris2, vms_vector *collision_point ) { 
2471 //##    return; 
2472 //##}
2473
2474
2475 /* DPH: Put these macros on one long line to avoid CR/LF problems on linux */
2476 #define COLLISION_OF(a,b) (((a)<<8) + (b))
2477
2478 #define DO_COLLISION(type1,type2,collision_function)    case COLLISION_OF( (type1), (type2) ):  (collision_function)( (A), (B), collision_point ); break;   case COLLISION_OF( (type2), (type1) ):  (collision_function)( (B), (A), collision_point );  break;
2479
2480 #define DO_SAME_COLLISION(type1,type2,collision_function)    case COLLISION_OF( (type1), (type1) ):  (collision_function)( (A), (B), collision_point ); break;
2481
2482 //these next two macros define a case that does nothing
2483 #define NO_COLLISION(type1,type2,collision_function)    case COLLISION_OF( (type1), (type2) ):  break;  case COLLISION_OF( (type2), (type1) ):  break;
2484
2485 #define NO_SAME_COLLISION(type1,type2,collision_function)    case COLLISION_OF( (type1), (type1) ):    break;
2486
2487 /* DPH: These ones are never used so I'm not going to bother */
2488 #ifndef __GNUC__
2489 #define IGNORE_COLLISION(type1,type2,collision_function)                                        \
2490         case COLLISION_OF( (type1), (type2) ):                                                                          \
2491                 break;                                                                                                                                                  \
2492         case COLLISION_OF( (type2), (type1) ):                                                                          \
2493                 break;
2494
2495 #define ERROR_COLLISION(type1,type2,collision_function)                                 \
2496         case COLLISION_OF( (type1), (type2) ):                                                                          \
2497                 Error( "Error in collision type!" );                                                                    \
2498                 break;                                                                                                                                                  \
2499         case COLLISION_OF( (type2), (type1) ):                                                                          \
2500                 Error( "Error in collision type!" );                                                                    \
2501                 break;
2502 #endif
2503
2504 void collide_two_objects( object * A, object * B, vms_vector *collision_point )
2505 {
2506         int collision_type;     
2507                 
2508         collision_type = COLLISION_OF(A->type,B->type);
2509
2510         //mprintf( (0, "Object %d of type %d collided with object %d of type %d\n", A-Objects,A->type, B-Objects, B->type ));
2511
2512         switch( collision_type )        {
2513         NO_SAME_COLLISION( OBJ_FIREBALL, OBJ_FIREBALL,   collide_fireball_and_fireball )
2514         DO_SAME_COLLISION( OBJ_ROBOT, OBJ_ROBOT, collide_robot_and_robot )
2515         NO_SAME_COLLISION( OBJ_HOSTAGE, OBJ_HOSTAGE,  collide_hostage_and_hostage )
2516         DO_SAME_COLLISION( OBJ_PLAYER, OBJ_PLAYER,  collide_player_and_player )
2517         DO_SAME_COLLISION( OBJ_WEAPON, OBJ_WEAPON,  collide_weapon_and_weapon )
2518         NO_SAME_COLLISION( OBJ_CAMERA, OBJ_CAMERA, collide_camera_and_camera )
2519         NO_SAME_COLLISION( OBJ_POWERUP, OBJ_POWERUP,  collide_powerup_and_powerup )
2520         NO_SAME_COLLISION( OBJ_DEBRIS, OBJ_DEBRIS,  collide_debris_and_debris )
2521         NO_SAME_COLLISION( OBJ_MARKER, OBJ_MARKER,  NULL )
2522         NO_COLLISION( OBJ_FIREBALL, OBJ_ROBOT,   collide_fireball_and_robot )
2523         NO_COLLISION( OBJ_FIREBALL, OBJ_HOSTAGE, collide_fireball_and_hostage )
2524         NO_COLLISION( OBJ_FIREBALL, OBJ_PLAYER,  collide_fireball_and_player )
2525         NO_COLLISION( OBJ_FIREBALL, OBJ_WEAPON,  collide_fireball_and_weapon )
2526         NO_COLLISION( OBJ_FIREBALL, OBJ_CAMERA,  collide_fireball_and_camera )
2527         NO_COLLISION( OBJ_FIREBALL, OBJ_POWERUP, collide_fireball_and_powerup )
2528         NO_COLLISION( OBJ_FIREBALL, OBJ_DEBRIS,  collide_fireball_and_debris )
2529         NO_COLLISION( OBJ_ROBOT, OBJ_HOSTAGE, collide_robot_and_hostage )
2530         DO_COLLISION( OBJ_ROBOT, OBJ_PLAYER,  collide_robot_and_player )
2531         DO_COLLISION( OBJ_ROBOT, OBJ_WEAPON,  collide_robot_and_weapon )
2532         NO_COLLISION( OBJ_ROBOT, OBJ_CAMERA,  collide_robot_and_camera )
2533         NO_COLLISION( OBJ_ROBOT, OBJ_POWERUP, collide_robot_and_powerup )
2534         NO_COLLISION( OBJ_ROBOT, OBJ_DEBRIS,  collide_robot_and_debris )
2535         DO_COLLISION( OBJ_HOSTAGE, OBJ_PLAYER,  collide_hostage_and_player )
2536         NO_COLLISION( OBJ_HOSTAGE, OBJ_WEAPON,  collide_hostage_and_weapon )
2537         NO_COLLISION( OBJ_HOSTAGE, OBJ_CAMERA,  collide_hostage_and_camera )
2538         NO_COLLISION( OBJ_HOSTAGE, OBJ_POWERUP, collide_hostage_and_powerup )
2539         NO_COLLISION( OBJ_HOSTAGE, OBJ_DEBRIS,  collide_hostage_and_debris )
2540         DO_COLLISION( OBJ_PLAYER, OBJ_WEAPON,  collide_player_and_weapon )
2541         NO_COLLISION( OBJ_PLAYER, OBJ_CAMERA,  collide_player_and_camera )
2542         DO_COLLISION( OBJ_PLAYER, OBJ_POWERUP, collide_player_and_powerup )
2543         NO_COLLISION( OBJ_PLAYER, OBJ_DEBRIS,  collide_player_and_debris )
2544         DO_COLLISION( OBJ_PLAYER, OBJ_CNTRLCEN, collide_player_and_controlcen )
2545         DO_COLLISION( OBJ_PLAYER, OBJ_CLUTTER, collide_player_and_clutter )
2546         NO_COLLISION( OBJ_WEAPON, OBJ_CAMERA,  collide_weapon_and_camera )
2547         NO_COLLISION( OBJ_WEAPON, OBJ_POWERUP, collide_weapon_and_powerup )
2548         DO_COLLISION( OBJ_WEAPON, OBJ_DEBRIS,  collide_weapon_and_debris )
2549         NO_COLLISION( OBJ_CAMERA, OBJ_POWERUP, collide_camera_and_powerup )
2550         NO_COLLISION( OBJ_CAMERA, OBJ_DEBRIS,  collide_camera_and_debris )
2551         NO_COLLISION( OBJ_POWERUP, OBJ_DEBRIS,  collide_powerup_and_debris )
2552         DO_COLLISION( OBJ_WEAPON, OBJ_CNTRLCEN, collide_weapon_and_controlcen )
2553         DO_COLLISION( OBJ_ROBOT, OBJ_CNTRLCEN, collide_robot_and_controlcen )
2554         DO_COLLISION( OBJ_WEAPON, OBJ_CLUTTER, collide_weapon_and_clutter )
2555
2556         DO_COLLISION( OBJ_MARKER, OBJ_PLAYER,  collide_player_and_marker)
2557         NO_COLLISION( OBJ_MARKER, OBJ_ROBOT,   NULL)
2558         NO_COLLISION( OBJ_MARKER, OBJ_HOSTAGE, NULL)
2559         NO_COLLISION( OBJ_MARKER, OBJ_WEAPON,  NULL)
2560         NO_COLLISION( OBJ_MARKER, OBJ_CAMERA,  NULL)
2561         NO_COLLISION( OBJ_MARKER, OBJ_POWERUP, NULL)
2562         NO_COLLISION( OBJ_MARKER, OBJ_DEBRIS,  NULL)
2563
2564         default:
2565                 Int3(); //Error( "Unhandled collision_type in collide.c!\n" );
2566         }
2567 }
2568
2569 #define ENABLE_COLLISION(type1,type2)                                   \
2570         CollisionResult[type1][type2] = RESULT_CHECK;   \
2571         CollisionResult[type2][type1] = RESULT_CHECK;
2572
2573 #define DISABLE_COLLISION(type1,type2)                                  \
2574         CollisionResult[type1][type2] = RESULT_NOTHING; \
2575         CollisionResult[type2][type1] = RESULT_NOTHING;
2576
2577 void collide_init()     {
2578         int i, j;
2579
2580         for (i=0; i < MAX_OBJECT_TYPES; i++ )
2581                 for (j=0; j < MAX_OBJECT_TYPES; j++ )
2582                         CollisionResult[i][j] = RESULT_NOTHING;
2583
2584         ENABLE_COLLISION( OBJ_WALL, OBJ_ROBOT );
2585         ENABLE_COLLISION( OBJ_WALL, OBJ_WEAPON );
2586         ENABLE_COLLISION( OBJ_WALL, OBJ_PLAYER  );
2587         DISABLE_COLLISION( OBJ_FIREBALL, OBJ_FIREBALL );
2588
2589         ENABLE_COLLISION( OBJ_ROBOT, OBJ_ROBOT );
2590 //      DISABLE_COLLISION( OBJ_ROBOT, OBJ_ROBOT );      //      ALERT: WARNING: HACK: MK = RESPONSIBLE! TESTING!!
2591
2592         DISABLE_COLLISION( OBJ_HOSTAGE, OBJ_HOSTAGE );
2593         ENABLE_COLLISION( OBJ_PLAYER, OBJ_PLAYER );
2594         ENABLE_COLLISION( OBJ_WEAPON, OBJ_WEAPON );
2595         DISABLE_COLLISION( OBJ_CAMERA, OBJ_CAMERA );
2596         DISABLE_COLLISION( OBJ_POWERUP, OBJ_POWERUP );
2597         DISABLE_COLLISION( OBJ_DEBRIS, OBJ_DEBRIS );
2598         DISABLE_COLLISION( OBJ_FIREBALL, OBJ_ROBOT );
2599         DISABLE_COLLISION( OBJ_FIREBALL, OBJ_HOSTAGE );
2600         DISABLE_COLLISION( OBJ_FIREBALL, OBJ_PLAYER );
2601         DISABLE_COLLISION( OBJ_FIREBALL, OBJ_WEAPON );
2602         DISABLE_COLLISION( OBJ_FIREBALL, OBJ_CAMERA );
2603         DISABLE_COLLISION( OBJ_FIREBALL, OBJ_POWERUP );
2604         DISABLE_COLLISION( OBJ_FIREBALL, OBJ_DEBRIS );
2605         DISABLE_COLLISION( OBJ_ROBOT, OBJ_HOSTAGE );
2606         ENABLE_COLLISION( OBJ_ROBOT, OBJ_PLAYER );
2607         ENABLE_COLLISION( OBJ_ROBOT, OBJ_WEAPON );
2608         DISABLE_COLLISION( OBJ_ROBOT, OBJ_CAMERA );
2609         DISABLE_COLLISION( OBJ_ROBOT, OBJ_POWERUP );
2610         DISABLE_COLLISION( OBJ_ROBOT, OBJ_DEBRIS );
2611         ENABLE_COLLISION( OBJ_HOSTAGE, OBJ_PLAYER );
2612         ENABLE_COLLISION( OBJ_HOSTAGE, OBJ_WEAPON );
2613         DISABLE_COLLISION( OBJ_HOSTAGE, OBJ_CAMERA );
2614         DISABLE_COLLISION( OBJ_HOSTAGE, OBJ_POWERUP );
2615         DISABLE_COLLISION( OBJ_HOSTAGE, OBJ_DEBRIS );
2616         ENABLE_COLLISION( OBJ_PLAYER, OBJ_WEAPON );
2617         DISABLE_COLLISION( OBJ_PLAYER, OBJ_CAMERA );
2618         ENABLE_COLLISION( OBJ_PLAYER, OBJ_POWERUP );
2619         DISABLE_COLLISION( OBJ_PLAYER, OBJ_DEBRIS );
2620         DISABLE_COLLISION( OBJ_WEAPON, OBJ_CAMERA );
2621         DISABLE_COLLISION( OBJ_WEAPON, OBJ_POWERUP );
2622         ENABLE_COLLISION( OBJ_WEAPON, OBJ_DEBRIS );
2623         DISABLE_COLLISION( OBJ_CAMERA, OBJ_POWERUP );
2624         DISABLE_COLLISION( OBJ_CAMERA, OBJ_DEBRIS );
2625         DISABLE_COLLISION( OBJ_POWERUP, OBJ_DEBRIS );
2626         ENABLE_COLLISION( OBJ_POWERUP, OBJ_WALL );
2627         ENABLE_COLLISION( OBJ_WEAPON, OBJ_CNTRLCEN )
2628         ENABLE_COLLISION( OBJ_WEAPON, OBJ_CLUTTER )
2629         ENABLE_COLLISION( OBJ_PLAYER, OBJ_CNTRLCEN )
2630         ENABLE_COLLISION( OBJ_ROBOT, OBJ_CNTRLCEN )
2631         ENABLE_COLLISION( OBJ_PLAYER, OBJ_CLUTTER )
2632
2633         ENABLE_COLLISION( OBJ_PLAYER, OBJ_MARKER );
2634
2635 }
2636
2637 void collide_object_with_wall( object * A, fix hitspeed, short hitseg, short hitwall, vms_vector * hitpt )
2638 {
2639
2640         switch( A->type )       {
2641         case OBJ_NONE:
2642                 Error( "A object of type NONE hit a wall!\n");
2643                 break;
2644         case OBJ_PLAYER:                collide_player_and_wall(A,hitspeed,hitseg,hitwall,hitpt); break;
2645         case OBJ_WEAPON:                collide_weapon_and_wall(A,hitspeed,hitseg,hitwall,hitpt); break;
2646         case OBJ_DEBRIS:                collide_debris_and_wall(A,hitspeed,hitseg,hitwall,hitpt); break;
2647
2648         case OBJ_FIREBALL:      break;          //collide_fireball_and_wall(A,hitspeed,hitseg,hitwall,hitpt); 
2649         case OBJ_ROBOT:         collide_robot_and_wall(A,hitspeed,hitseg,hitwall,hitpt); break;
2650         case OBJ_HOSTAGE:               break;          //collide_hostage_and_wall(A,hitspeed,hitseg,hitwall,hitpt); 
2651         case OBJ_CAMERA:                break;          //collide_camera_and_wall(A,hitspeed,hitseg,hitwall,hitpt); 
2652         case OBJ_POWERUP:               break;          //collide_powerup_and_wall(A,hitspeed,hitseg,hitwall,hitpt); 
2653         case OBJ_GHOST:         break;  //do nothing
2654
2655         default:
2656                 Error( "Unhandled object type hit wall in collide.c\n" );
2657         }
2658 }
2659
2660