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