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