/* * Descent 3 * Copyright (C) 2024 Parallax Software * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * $Logfile: /DescentIII/Main/physics/Collide.cpp $ * $Revision: 239 $ * $Date: 9/20/01 5:33p $ * $Author: Matt $ * * Descent III collision code * * $Log: /DescentIII/Main/physics/Collide.cpp $ * * 239 9/20/01 5:33p Matt * Fixed weird omega cannon bugs (I hope) by only letting the omega * particles collide from inside AquireElectricalTarget(). * * 238 11/04/99 5:35p Chris * Levels with forcefields with a custom bouce sound don't cause damage * * 237 10/20/99 5:40p Chris * Added the Red Guidebot * * 236 10/08/99 4:29p Chris * Added the forcefield and glass breaking override options * * 235 5/24/99 1:26a Matt * Correctly deal with hit_info == NULL being passed to collision * routines. * * 234 5/21/99 2:55a Matt * When an object is bounced-out and needs to be killed, call * DestroyObject() if it's already dying. Before the code just set the * dead flag, which caused robots to disappear if they bumped into * something while dying. * * 233 5/20/99 9:03p Matt * Changed trigger system so that collisions with rendered portals don't * set off triggers on the portals. * * 232 5/19/99 1:23p Matt * Fixed (hopefully) yet more collision problems introduced by my recent * changes. * * 231 5/18/99 10:15p Matt * Fixed bug with weapon spawning that I introduced last night. * * 230 5/18/99 10:57a Chris * Various bug fixes * * 229 5/17/99 9:09p Matt * Made objects with lava behave like walls with lava; flares don't stick, * player catches on fire. * * 228 5/17/99 6:07p Chris * GB doesn't damage players - PF_LOCK_MASK objects always do... * * 227 5/11/99 5:50p Chris * Buildings don't collide with each other * * 226 5/08/99 4:27p Chris * Hearing code * * 225 5/08/99 3:31p Matt * Deal with weapons hitting water. * * 224 5/07/99 10:56p Chris * GB avoids forcefields, lava, and volatile surfaces... If he accidently * hits one of these, he ignores the collision (i.e. no effects). It make * him look smarter. * * 223 4/30/99 3:28p Matt * Weapons now make a special sound when colliding with lava & volatile * surfaces, instead of the standard hit-wall sound. * * 222 4/29/99 6:48p Matt * Made volatile surfaces apply damage and lava surfaces catch the player * on fire. Both now generate the steam puff effect. * * 221 4/29/99 2:17a Samir * adjusted some sound priority stuff * * 220 4/23/99 6:44p Matt * Changed weapons to keep going after breaking glass. * * 219 4/21/99 4:27p Chris * * 218 4/21/99 3:45p Chris * * 217 4/21/99 3:43p Chris * * 216 4/21/99 2:56p Chris * Made player-player collisions almost complete in-elastic * * 215 4/21/99 1:30p Matt * Make breakable glass use the new breakable flag, instead of * piggybacking on the destroyable flag. * * 214 4/21/99 11:06a Kevin * new ps_rand and ps_srand to replace rand & srand * * 213 4/21/99 10:12a Chris * Tweaked robot/player damage * * 212 4/21/99 3:33a Chris * Collisions between robots and players cause damage to both robots and * players (robots get more damage) * * 211 4/19/99 12:03p Jason * added checking for fireball collisions * * 210 4/19/99 4:02a Jeff * fixed min/max for Linux * * 209 4/18/99 10:55p Chris * Added ignore own concussive blasts * * 208 4/16/99 8:18p Jason * make impact spawning weapons work with doors * * 207 4/14/99 1:42a Jeff * fixed case mismatched #includes * * 206 4/12/99 6:15p Samir * Sound priorities pass 1 * * 205 4/06/99 6:24p Jason * various fixes for multiplayer * * 204 3/23/99 4:10p Jason * more tweaks for line sparks * * 203 3/23/99 12:50p Jason * added line sparks * * 202 3/22/99 6:41p Jason * fixed client side weapons not reacting correctly to bouncing code * * 201 3/22/99 1:47p Matt * Moved breaking glass code from collide.cpp to damage.cpp * * 200 3/16/99 4:16p Matt * Made mass driver spin the victim more. (Jason had reduced it from 0.25 * to 0.05; I've moved it back up to 0.12 at Andy's request.) * * 199 3/12/99 7:46p Jeff * record wall hit collisions to demo file * * 198 3/11/99 5:55p Jason * fixed player spin with mass driver * * 197 3/05/99 2:12p Matt * Fixed bug (in a slightly hacky way) where energy weapons would cause * glass to switch to the damaged texture. For a real fix, we need a * separate breakable glass flag for textures. * * 196 3/03/99 10:52a Chris * Energy weapons push again * * 195 3/03/99 5:44a Chris * Max secondary burn time of 10 seconds * * 194 3/01/99 3:18p Jason * made powerups work correctly in multiplayer * * 193 2/28/99 11:29p Chris * Improved robot-wall reactions * * 192 2/22/99 2:03p Jason * added different damages for players and generics * * 191 2/21/99 4:35p Chris * Improving the level goal system... Not done. * * 190 2/20/99 10:31a Kevin * OEM Beta 1 fixs * * 189 2/19/99 12:35p Chris * Player can fly through markers * * 188 2/17/99 4:50p Jason * fixed destroyable face/breakable glass bug * * 187 2/17/99 1:26p Matt * Fixed the die angle system for weapons (changing it around a little in * the process: now all non-weapons (which don't use the system) should * have dot == -1, weapons that die at any angle have dot == 0, and * setting dot == 1 means it never dies). * * 186 2/16/99 8:16a Chris * Fixed a bug - THANKS! * * 185 2/10/99 1:48p Matt * Changed object handle symbolic constants * * 184 2/09/99 2:38p Jason * added destroyable faces * * 183 2/02/99 8:44a Chris * I made buildings with AI work correctly (ie really big robots should be * buildings) * anim to and from states are now shorts instead of bytes * * 182 1/31/99 7:26p Matt * Renamed a bunch of functions to have HUD capitalized * * 181 1/29/99 5:09p Chris * Made changes for ROCKS * * 180 1/28/99 6:17p Jason * added markers * * 179 1/26/99 6:39p Jason * added wall effects code * * 178 1/25/99 5:59p Matt * Patched the masses of objects * * 177 1/24/99 8:18p Chris * Updated AI and OSIRIS. Externalized fireball constants for spew and * sparks. Added CreateRandomSparks to OSIRIS, renamed a bunch of AI * Notify names to use underscores. Fixed a memory access leak in the * matcen effect code. * * 176 1/23/99 5:16p Chris * Bounds fixed acos and asin stuff (-1.0000000000001 could cause really * bad things to happen with rot. vels) * * 175 1/20/99 10:50a Jason * added new terrain * * 174 1/20/99 2:13a Chris * It is now possible for robots to have special immunities, resistances, * and vunerabilities * * 173 1/18/99 12:12a Jeff * removed weapon collide event, combined it in general collide event. * general collide event handles more collides. Added a room collide * event * * 172 1/15/99 3:59a Jeff * added two new multiplayer DLL events for weapon collisions. Added two * new fields to DLLInfo data, point and normal, which are filled out for * collision events. * * 170 1/10/99 7:35p Matt * Added missing break to DO_COLLISION macor * * 169 1/10/99 6:49a Jeff * Some initial work to get linux version to compile * * 168 1/08/99 2:56p Samir * Ripped out OSIRIS1. * * 167 1/01/99 4:10p Chris * Added some const parameters, improved ray cast object collide/rejection * code * * 166 12/21/98 10:50a Chris * You can now push walking objects * * 165 12/17/98 12:08p Jeff * first checkin of new implementation of OSIRIS (old OSIRIS no longer * works) * * 164 11/19/98 3:54p Chris * * 163 11/19/98 3:51p Chris * Shooting dying robots makes them blow up faster (but now not as * quickly) * * 162 11/13/98 4:25p Jason * changes for better weapon effects * * 161 11/13/98 1:00p Chris * Fixed a math problem with the new collision response code. Zero rad * particles where causing i1 or i2 to be zero. c1 = c1/i1 -> it was * going to infinity. So, I made a floor threshold for i1 and i2 * * 160 11/11/98 2:46p Kevin * Demo recording system work * * 159 11/10/98 5:17p Jeff * peppered in some more forcefeedback effects * * 158 10/30/98 5:17p Chris * Players only get 1/4 the rotational velocity of an impulse * * 157 10/29/98 1:45p Chris * Checked in the better collision response code * * 156 10/22/98 12:57p Chris * Added sounds to player/player and player/robot collisions * * 155 10/20/98 1:26p Jason * took out spawning on direct hits * * 154 10/20/98 12:10p Chris * * 153 10/19/98 8:38p Matt * Don't make a sound when colliding with lava, since the lava will start * you on fire, which will make sound. * * 152 10/19/98 7:19p Matt * Added system to support different types of damage to the player and * have these different types make different sounds. * * 151 10/18/98 2:09p Matt * Made the backquote/flare thing go away in release versions, and made it * work for terrain cells. * * 150 10/17/98 12:25p Chris * Fixed attached flares * * 149 10/16/98 5:25p Chris * Fixed sticky code * * 148 10/16/98 4:33p Chris * * 147 10/16/98 4:18p Chris * Fixed the 'flare thing' * * 146 10/16/98 3:39p Chris * Improved the object linking system and AI and physics * * 145 10/15/98 4:51p Chris * Speed up dying for player too * * 144 10/15/98 4:49p Chris * Speed up dying if the robot is getting hit * * 143 10/12/98 2:27p Matt * Fixed code to print room:face when backquote/tilde held down and flare * hits the wall. * * 142 10/12/98 10:04a Chris * Wall hits cannot kill the player * * 141 10/09/98 7:57p Chris * Added the flare room/wall cheat * * 140 10/09/98 7:47p Chris * Added ObjSetDeadFlag * * 139 10/08/98 12:00p Kevin * Demo system work * * 138 10/05/98 7:23p Jason * implemented destroyable lights (first draft) * * 137 10/01/98 6:56p Jason * turned off energy weapon hit effects if the object getting hit is the * viewer - this helps cut down on the general clutter in heavy combat * * 136 9/29/98 4:48p Chris * Working on multiplayer collisions * * 135 9/23/98 5:04p Chris * Raised the collide wall damage threshold * * 134 9/23/98 3:47p Chris * Increased the speed to cause damage from wall hits * * 133 9/22/98 12:23p Chris * Further improvements to wall hitting * * 132 9/21/98 8:32p Chris * Another improvement to the forcefield stuff * * 131 9/21/98 8:19p Chris * Improved volatile and forcefield hits * * 130 9/21/98 1:08p Chris * Improved lava handling * * 129 9/21/98 1:04p Chris * * 128 9/21/98 1:03p Chris * * 127 9/21/98 1:01p Chris * Fixed more volatile problems * * 126 9/21/98 11:44a Chris * Fixed the volatile terrain crash bug * * 125 9/18/98 7:45p Chris * Lava works - needs tweaking. * * 124 8/25/98 7:27p Chris * Improved the scorch code * * 123 8/25/98 7:23p Chris * Made weapons explode even if they break glass * * 122 8/25/98 12:35p Chris * Fixed a small reversed normal bug * * 121 8/19/98 2:48p Jason * fixed some weapon firing and weapon exploding bugs * * 120 8/18/98 7:12p Chris * Improved FUSION behavior. * * 119 8/12/98 12:26p Chris * * 118 8/05/98 11:16a Matt * Added trigger checks when weapons and the player collide with walls. * * 117 8/03/98 5:56p Jason * got fusion cannon working correctly * * 116 7/31/98 11:52a Chris * Weapons can be persistent. Added ability for objects to be manually * set for no object collisions. * * 115 7/30/98 11:09a Jason * added weapons that freeze and deform terrain * * 114 7/22/98 4:36p Jason * took out bugs from observer mode * * 113 7/17/98 10:12a Chris * * 112 7/01/98 12:56p Jason * more changes for countermeasures * * 111 7/01/98 12:12p Jason * added countermeasures * * 110 6/24/98 3:56p Jason * added code for new omega cannon * * 109 6/15/98 3:23p Chris * Single point walker update * * 108 6/15/98 7:53a Chris * Fixed a comment'ed out variable * * 107 6/15/98 7:52a Chris * Removed the new "cool" physics * * 106 6/11/98 12:48p Jason * added better spewing weapons * * 105 6/04/98 12:00p Matt * Took out Napalm Rocket hack that I added for the E3 demo. * * 104 6/02/98 10:43p Chris * Multipoint collision detection enabled * * 103 6/01/98 9:23p Chris * Fixed a bunch of collision problems * * 102 5/27/98 6:18p Matt * Super hack to reduce impact damage of napalm rocket (for E3 only) * * 101 5/27/98 6:08p Chris * Added weapon to generic sound * * 100 5/26/98 4:17p Jason * made scripts/dlls not collide every single frame * * 99 5/26/98 3:58p Matt * Only matter weapons now break glass. * * 98 5/25/98 8:36p Matt * Added code to set different sizes for different weapon scorch marks. * Also, don't leave scorch marks on lights. * * 97 5/22/98 12:34p Matt * Added scorch mark/bullet hole system. * * 96 5/22/98 11:59a Chris * Fixed improper uses of FindSoundName and made the proper static sounds * * 95 5/19/98 4:42a Chris * Added shockwave's -- enjoy. :) * * 94 5/07/98 2:52p Chris * Removed a bounce mprintf * * 93 5/07/98 2:40p Chris * Added death_dot and bounce sound for weapons * * 92 5/07/98 2:22p Chris * Hit die dot * * 91 5/06/98 7:07p Samir * when player is completely dead, don't call script too. * * 90 5/05/98 3:42p Chris * Code cleanup and fixed the collide_XXX_with_wall code. The wall_normal * was know by fvi; so, it is passed to the function (instead of * generating it again) * * 89 5/04/98 3:51p Matt * Finished (for now) with breaking glass. * * 88 5/04/98 1:00p Jason * changed the way weapon explosions work * * 87 5/04/98 12:47p Matt * Improvements to the breaking glass system, though I don't consider it * anywhere near done yet. * * 86 5/03/98 10:24p Matt * Partially completed breaking glass code, checked in so Chris can fix a * bug * * 85 4/23/98 7:14p Chris * Changed non-moving objects to player collisions * * 84 4/22/98 9:43p Chris * More AI improvements * * 83 4/22/98 3:25p Jason * changes for multiplayer debugging * * 82 4/20/98 4:47p Chris * Made collision stuff more accurate * * 81 4/17/98 2:00p Jason * added cool object effects * * 80 4/15/98 12:56p Chris * Reformatted some code * * 79 4/09/98 5:18p Jason * got guided working in multiplayer * * 78 4/09/98 12:05p Chris * Added parenting for all object types. :) * * 77 4/08/98 11:16a Chris * Fixed powerup pickup bug * * 76 4/07/98 4:25p Chris * Added support for buddy bot * * 75 4/06/98 11:59a Chris * Fixed powerup/powerup hack * * 74 3/31/98 5:56p Jason * changes for multiplay * * 73 3/30/98 5:12p Jason * more changes for game/dll integration * * 72 3/20/98 9:34p Jason * added SetObjectDeadFlag inlined function * * 71 3/20/98 6:39p Jason * 100.0 to 1.0 for volume * * 70 3/20/98 3:54p Chris * Working on adding sounds to the game. :) * * 69 3/20/98 3:21p Chris * Added player hit by weapon sound. * * 68 3/20/98 2:59p Chris * Added a wall hit sound for the player and added support for a base * volume for 3d sounds * * 67 3/19/98 7:15p Jason * more changes for multiplayer * * 66 3/19/98 3:44p Chris * Multiplayer enchancement for collisions * * 65 3/19/98 3:27p Chris * Did some collision code for multiplayer. * * 64 3/18/98 5:45p Jason * more multiplayer script integration * * 63 3/17/98 6:17p Jason * added gamemode collide stuff * * 62 3/17/98 11:27a Chris * Added object bump notifies for AI * * 61 3/13/98 5:55p Chris * Added the new collision spheres * * 60 2/23/98 5:46p Chris * Removed some old code. * * 59 2/18/98 11:48a Craig * * 58 2/11/98 4:10p Jason * got spawning impact weapons working * * 57 2/05/98 2:54p Jason * changes for explosions * * 56 2/05/98 2:02p Chris * Fixed the hitseg and hitwall in collide_object_with_wall and * scrape_object_with_wall * * 55 2/04/98 6:09p Matt * Changed object room number to indicate a terrain cell via a flag. Got * rid of the object flag which used to indicate terrain. * * 54 1/30/98 2:54p Matt * Took script calls out of bump_two_objects, since they alread existed in * collide_two_objects * * 53 1/29/98 5:48p Matt * Got rid of marker objects * * 52 1/29/98 4:16p Matt * Fixed some problems from my last changes * * 51 1/29/98 3:11p Matt * Clean up a bit: deleted commented-out code, removed leftover D1/D2 * stuff, removed references to hostage objects * * 50 1/28/98 12:00p Jason * more changes for multiplayer * * 49 1/26/98 11:01a Jason * incremental checkin for multiplayer * * 48 1/23/98 11:21a Jason * incremental multiplayer checkin * * 47 1/21/98 6:09p Jason * Got player deaths working in multiplayer * * 46 1/21/98 3:05p Samir * Changed a function call to StartPlayerExplosion * * 45 1/21/98 1:11p Jason * incremental checkin for multiplayer * * 44 1/20/98 12:10p Jason * implemented vis effect system * * 43 1/19/98 10:04a Matt * Added new object handle system * * 42 1/14/98 7:57p Chris * Improving the awareness system * * 41 1/14/98 1:23p Jason * made ApplyDamageToGeneric send the weapons parent as the killer * * 40 1/09/98 4:13p Chris * Reformatted some code * * 39 1/09/98 4:11p Chris * Removed a mprintf * * 38 12/22/97 6:19p Chris * Moved weapon battery firing sound off the projectile (weapon) and into * the weapon battery. * * 37 12/15/97 11:03a Jason * make sparks only when successfully hit * * 36 12/12/97 6:13p Jason * added sparks * * 35 12/10/97 2:36p Jason * added random small explosions * * 34 12/04/97 12:15p Jason * gave designers the ability to set their own weapon-to-wall hit vclips * * 33 12/01/97 9:54a Chris * Added support for concussive forces, generalized robot collisions to * generic collisions * * 32 11/20/97 1:38p Chris * No rotation is applied to player objects * * 31 11/13/97 5:06p Chris * Added support for clutter to weapon collisions * * 30 11/05/97 6:42p Jason * took out stupid blast ring * * 29 10/29/97 1:29p Chris * Added PF_STICK for weapons * * 28 10/24/97 2:30p Jason * more changes for explosions * * 27 10/23/97 11:14a Jason * changed some fireball DEFINEs * * 26 10/22/97 7:26p Samir * Player death mostly working. Damn hangup still occurs. Freaky. * * 25 10/22/97 4:30p Chris * We can now slew between the mine and the terrain * * 24 10/22/97 11:44a Jason * changes for explosion system * * 23 10/21/97 4:15p Chris * Incremental integration of the fvi/physics/collide code * * 22 10/20/97 4:46p Jason * changes for explosions * * 21 10/20/97 11:55a Chris * Added some support for the new collide system. * * 20 10/16/97 3:46p Chris * Incremental improvements * * 19 10/03/97 3:47p Chris * Moved script calls to bump two objects (so building can have scripts) * * 18 10/03/97 3:45p Chris * Added some bumping force by weapons * * 17 10/03/97 3:18p Samir * Added script hook to bump_this_object * * 16 10/02/97 11:34a Chris * Added support for external room collisions * * 15 10/01/97 4:51p Samir * Include damage.h * * 14 9/19/97 9:37p Chris * Attempted to fix door collisions -- almost there, but not quite * * 13 9/15/97 5:17a Chris * Removed the Explosion Vclips since they do not play correctly in * Hardware * Added support for building collisons. * working on SPhere to non-moving sphere collisions * * 12 9/12/97 6:36p Chris * Added building collisions * * 11 9/05/97 12:27p Samir * Player takes damage and set death if needed. * * 10 9/04/97 12:03p Matt * Got rid of warnings * * 9 9/03/97 2:13p Chris * Working on improving the debris system and partical explosions * * 8 8/21/97 7:53p Chris * * 7 8/21/97 7:52p Chris * * 6 8/18/97 1:46a Chris * Allowed for robot damage and destoyable objects. * Also, allowed weapons to bounce * * 5 8/14/97 11:44a Samir * Don't kill powerups. Let script do it. * * 4 8/12/97 3:32p Samir * collisions call script code now too. * * 3 8/11/97 1:54p Matt * Ripped out robot & powerup pages, and added generic page * * 2 7/28/97 1:16p Chris * Allow for player and robots to be hit by weapons and correct parenting * of weapons. * * 16 5/19/97 5:39 PM Jeremy * removed "we hit a robot" mprintf * * 15 5/15/97 6:09p Chris * * 14 5/13/97 5:52p Chris * Added ability to exit and enter mine. Also did some * incremental improvements. * * 13 5/05/97 5:41a Chris * Added some better polygon collision code. It is still rough though. * * 12 4/29/97 8:03a Chris * Improved the sound code. High-level sound now * fully uses the flags in the sound page and it * resulted in a simpilier coding interface :) * * 11 4/24/97 5:42p Jason * got fireball vclips working * * 10 4/24/97 12:28p Jason * added sound when weapon hits a wall * * 9 4/24/97 3:25a Chris * // Added some more support for 3d sounds * * 8 4/23/97 6:34p Chris * // Incremental collision stuff. * * 7 4/23/97 6:06p Jason * made player ship and weapons work correctly together (first pass) * * 6 4/17/97 3:10p Chris * Added edge of world object delete. Also did some incremental * improvements. * * 5 4/11/97 3:05a Chris * Allowed robot sphere collision * * 4 3/15/97 1:29p Chris * * $NoKeywords: $ */ #include #include #include "collide.h" #include "PHYSICS.H" #include "pserror.h" #include "mono.h" #include "object.h" #include "player.h" #include "hlsoundlib.h" #include "weapon.h" #include "damage.h" #include "fireball.h" #include "sounds.h" #include "AIMain.h" #include "multi.h" #include "game.h" #include "soundload.h" #include "game2dll.h" #include "scorch.h" #include "ddio.h" #include "vecmat.h" #include "trigger.h" #include "lighting.h" #include "hud.h" #include "D3ForceFeedback.h" #include "demofile.h" #include "osiris_dll.h" #include "marker.h" #include "hud.h" #include "levelgoal.h" #include "psrand.h" #include #define PLAYER_ROTATION_BY_FORCE_SCALAR 0.12f #define NONPLAYER_ROTATION_BY_FORCE_SCALAR 1.0f ubyte CollisionResult[MAX_OBJECT_TYPES][MAX_OBJECT_TYPES]; ubyte CollisionRayResult[MAX_OBJECT_TYPES]; bool IsOKToApplyForce(object *objp) { if (Game_mode & GM_MULTI) { if (objp->type == OBJ_PLAYER) { if (objp != Player_object) return false; } else { if (objp->type != OBJ_WEAPON && objp->type != OBJ_POWERUP && Netgame.local_role != LR_SERVER) return false; } } if (objp->mtype.phys_info.mass == 0.0) return false; if (objp->movement_type != MT_PHYSICS && objp->movement_type != MT_WALKING) return false; if (objp->mtype.phys_info.flags & PF_PERSISTENT) return false; if (objp->mtype.phys_info.flags & PF_LOCK_MASK) // Not done! return false; return true; } // ----------------------------------------------------------------------------- void bump_this_object(object *objp, object *other_objp, vector *force, vector *collision_pnt, int damage_flag) { // float force_mag; if (objp->type == OBJ_PLAYER) { if ((Game_mode & GM_MULTI) && (objp != Player_object)) return; phys_apply_force(objp, force); } else { if ((Game_mode & GM_MULTI) && (objp->type != OBJ_PLAYER && objp->type != OBJ_POWERUP) && (Netgame.local_role != LR_SERVER)) return; phys_apply_force(objp, force); phys_apply_rot(objp, force); } // if (! (objp->mtype.phys_info.flags & PF_PERSISTENT)) // if (objp->type == OBJ_PLAYER) { // vector force2; // force2 = forcex/4; // phys_apply_force(objp,&force2); // if (damage_flag && ((other_objp->type != OBJ_ROBOT) || !Robot_info[other_objp->id].companion)) { // force_mag = vm_vec_mag_quick(&force2); // apply_force_damage(objp, force_mag, other_objp); // } // } else if ((objp->type == OBJ_ROBOT) || (objp->type == OBJ_CLUTTER) || (objp->type == OBJ_CNTRLCEN)) { // if (!Robot_info[objp->id].boss_flag) { // vector force2; // force2.x = force->x/(4 + Difficulty_level); // force2.y = force->y/(4 + Difficulty_level); // force2.z = force->z/(4 + Difficulty_level); // phys_apply_force(objp, force); // phys_apply_rot(objp, &force2); // if (damage_flag) { // force_mag = vm_vec_mag_quick(force); // apply_force_damage(objp, force_mag, other_objp); // } // } // } } /* void bump_one_object(object *obj0, vector *hit_dir, float damage) { vector hit_vec; hit_vec = *hit_dir; vm_vec_scale(&hit_vec, damage); phys_apply_force(obj0,&hit_vec); } */ // #define DAMAGE_SCALE 128 // Was 32 before 8:55 am on Thursday, September 15, changed by MK, // walls // were hurting me more than robots! #define DAMAGE_THRESHOLD (F1_0/3) #define WALL_LOUDNESS_SCALE (20) // float force_force = 50.0; typedef struct v { float i, j; } vec2d; #define cross(v0, v1) (((v0)->i * (v1)->j) - ((v0)->j * (v1)->i)) // finds the uv coords of the given point on the given seg & side // fills in u & v. if l is non-NULL fills it in also void FindHitpointUV(float *u, float *v, vector *point, room *rp, int facenum) { face *fp = &rp->faces[facenum]; int ii, jj; vec2d pnt[3], checkp, vec0, vec1; float *t; float k0, k1; int i; // 1. find what plane to project this wall onto to make it a 2d case GetIJ(&fp->normal, &ii, &jj); // 2. compute u,v of intersection point // Copy face points into 2d verts array for (i = 0; i < 3; i++) { t = &rp->verts[fp->face_verts[i]].x; pnt[i].i = t[ii]; pnt[i].j = t[jj]; } t = &point->x; checkp.i = t[ii]; checkp.j = t[jj]; // vec from 1 -> 0 vec0.i = pnt[0].i - pnt[1].i; vec0.j = pnt[0].j - pnt[1].j; // vec from 1 -> 2 vec1.i = pnt[2].i - pnt[1].i; vec1.j = pnt[2].j - pnt[1].j; k1 = -((cross(&checkp, &vec0) + cross(&vec0, &pnt[1])) / cross(&vec0, &vec1)); if (fabsf(vec0.i) > fabsf(vec0.j)) k0 = ((-k1 * vec1.i) + checkp.i - pnt[1].i) / vec0.i; else k0 = ((-k1 * vec1.j) + checkp.j - pnt[1].j) / vec0.j; // mprintf(0," k0,k1 = %x,%x\n",k0,k1); *u = fp->face_uvls[1].u + (k0 * (fp->face_uvls[0].u - fp->face_uvls[1].u)) + (k1 * (fp->face_uvls[2].u - fp->face_uvls[1].u)); *v = fp->face_uvls[1].v + (k0 * (fp->face_uvls[0].v - fp->face_uvls[1].v)) + (k1 * (fp->face_uvls[2].v - fp->face_uvls[1].v)); // mprintf(0," u,v = %x,%x\n",*u,*v); } // Creates some effects where a weapon has collided with a wall void DoWallEffects(object *weapon, int surface_tmap) { texture *texp = &GameTextures[surface_tmap]; if (texp->flags & (TF_VOLATILE + TF_LAVA + TF_WATER)) { // Create some lava steam if ((texp->flags & TF_WATER) || ((ps_rand() % 4) == 0)) { int visnum = VisEffectCreate(VIS_FIREBALL, MED_SMOKE_INDEX, weapon->roomnum, &weapon->pos); if (visnum >= 0) { vis_effect *vis = &VisEffects[visnum]; vis->lifetime = .9f; vis->lifeleft = .9f; vis->movement_type = MT_PHYSICS; vis->size = 3.0; vm_MakeZero(&vis->velocity); vis->velocity.y = 10; } } } else if (texp->flags & TF_RUBBLE) { if ((ps_rand() % 4) == 0) { int num_rubble = (ps_rand() % 3) + 1; int bm_handle = GetTextureBitmap(texp - GameTextures, 0); ushort *data = bm_data(bm_handle, 0); ushort color = data[(bm_w(bm_handle, 0) * (bm_h(bm_handle, 0) / 2)) + (bm_w(bm_handle, 0) / 2)]; for (int i = 0; i < num_rubble; i++) { int visnum; visnum = VisEffectCreate(VIS_FIREBALL, RUBBLE1_INDEX + (ps_rand() % 2), weapon->roomnum, &weapon->pos); if (visnum >= 0) { vis_effect *vis = &VisEffects[visnum]; vis->movement_type = MT_PHYSICS; vis->mass = 100; vis->drag = .1f; vis->phys_flags |= PF_GRAVITY | PF_NO_COLLIDE; if ((ps_rand() % 3) == 0) { vis->velocity.x = (ps_rand() % 100) - 50; vis->velocity.y = -((ps_rand() % 200) - 30); vis->velocity.z = (ps_rand() % 100) - 50; } else { vis->velocity.x = (ps_rand() % 300) - 50; vis->velocity.y = -((ps_rand() % 200) - 30); vis->velocity.z = (ps_rand() % 300) - 50; } vm_NormalizeVectorFast(&vis->velocity); vis->velocity *= 4 + (ps_rand() % 20); vis->size = .5 + (((ps_rand() % 11) - 5) * .05); vis->flags |= VF_USES_LIFELEFT; float lifetime = 1.0 + (((ps_rand() % 11) - 5) * .1); vis->lifeleft = lifetime; vis->lifetime = lifetime; vis->lighting_color = color; } } } } } #define FORCEFIELD_DAMAGE 5.0f void DeformTerrain(vector *pos, int depth, float size); // Check for lava, volatile, or water surface. If contact, make special sound & kill the weapon void check_for_special_surface(object *weapon, int surface_tmap, vector *surface_normal, float hit_dot) { bool f_forcefield, f_volatile, f_lava, f_water; f_forcefield = (GameTextures[surface_tmap].flags & TF_FORCEFIELD) != 0; f_volatile = (GameTextures[surface_tmap].flags & TF_VOLATILE) != 0; f_lava = (GameTextures[surface_tmap].flags & TF_LAVA) != 0; f_water = (GameTextures[surface_tmap].flags & TF_WATER) != 0; // Kill the weapon if the surface is volatile, lava, or water if (f_volatile || f_lava || f_water) { int snd; ain_hear hear; hear.f_directly_player = false; if (f_water) { snd = SOUND_WEAPON_HIT_WATER; hear.hostile_level = 0.2f; hear.curiosity_level = 0.3f; } else if (f_volatile || f_lava) { snd = SOUND_WEAPON_HIT_LAVA; hear.hostile_level = 0.1f; hear.curiosity_level = 0.5f; } else Int3(); hear.max_dist = Sounds[snd].max_distance; Sound_system.Play3dSound(snd, SND_PRIORITY_NORMAL, weapon); AINotify(weapon, AIN_HEAR_NOISE, (void *)&hear); if (!f_water) DoWeaponExploded(weapon, surface_normal); SetObjectDeadFlag(weapon); } } // Process a collision between a weapon and a wall // Returns true if the weapon hits the wall, and false if should keep going though the wall (for breakable glass) bool collide_weapon_and_wall(object *weapon, fix hitspeed, int hitseg, int hitwall, vector *hitpnt, vector *wall_normal, float hit_dot) { bool f_forcefield; bool f_volatile, f_lava, f_water; // mprintf((0, "Weapon hit wall, how nice.\n")); // #ifndef RELEASE if ((stricmp(Weapons[weapon->id].name, "Yellow flare") == 0) && (weapon->parent_handle == Player_object->handle) && (KEY_STATE(KEY_LAPOSTRO))) { if (ROOMNUM_OUTSIDE(hitseg)) { AddHUDMessage("Terrain cell %d", CELLNUM(hitseg)); } else { AddHUDMessage("Room %d face %d", hitseg, hitwall); } } // #endif // Check if forcefield int tmap; if (!ROOMNUM_OUTSIDE(hitseg)) // Make sure we've hit a wall, and not terrain { tmap = Rooms[hitseg].faces[hitwall].tmap; } else { tmap = Terrain_tex_seg[Terrain_seg[CELLNUM(hitseg)].texseg_index].tex_index; } f_forcefield = (GameTextures[tmap].flags & TF_FORCEFIELD) != 0; f_volatile = (GameTextures[tmap].flags & TF_VOLATILE) != 0; f_lava = (GameTextures[tmap].flags & TF_LAVA) != 0; f_water = (GameTextures[tmap].flags & TF_WATER) != 0; if (f_forcefield && !(Weapons[weapon->id].flags & WF_MATTER_WEAPON)) { ain_hear hear; hear.f_directly_player = false; hear.hostile_level = 0.9f; hear.curiosity_level = 0.1f; if (sound_override_force_field == -1) hear.max_dist = Sounds[SOUND_FORCEFIELD_BOUNCE].max_distance; else hear.max_dist = Sounds[sound_override_force_field].max_distance; AINotify(weapon, AIN_HEAR_NOISE, (void *)&hear); if (sound_override_force_field == -1) Sound_system.Play3dSound(SOUND_FORCEFIELD_BOUNCE, SND_PRIORITY_HIGH, weapon); else Sound_system.Play3dSound(sound_override_force_field, SND_PRIORITY_HIGH, weapon); return true; } // Check for destroyable or breakable face if (!ROOMNUM_OUTSIDE(hitseg)) // First, make sure we've hit a wall, and not terrain { room *rp = &Rooms[hitseg]; face *fp = &rp->faces[hitwall]; // Check for a trigger on this wall CheckTrigger(hitseg, hitwall, weapon, TT_COLLIDE); // Check for a destroyable face if ((GameTextures[fp->tmap].flags & TF_DESTROYABLE) && !(fp->flags & FF_DESTROYED)) { int visnum = CreateFireball(hitpnt, SHATTER_INDEX + (ps_rand() % 2), hitseg, VISUAL_FIREBALL); if (visnum >= 0) { // Alter the size of this explosion based on the face size vector verts[MAX_VERTS_PER_FACE]; vector center; for (int t = 0; t < fp->num_verts; t++) verts[t] = rp->verts[fp->face_verts[t]]; float size = sqrt(vm_GetCentroid(¢er, verts, fp->num_verts)); VisEffects[visnum].size = (size / 2); } CreateRandomSparks(20, hitpnt, hitseg); if (sound_override_glass_breaking == -1) Sound_system.Play3dSound(SOUND_BREAKING_GLASS, SND_PRIORITY_HIGH, weapon); else Sound_system.Play3dSound(sound_override_glass_breaking, SND_PRIORITY_HIGH, weapon); fp->flags |= FF_DESTROYED; } // Check for a breakable face: If the texture is breakable and it's on a portal // and hit with a matter weapon, break it if ((fp->portal_num != -1) && (GameTextures[fp->tmap].flags & TF_BREAKABLE) && (Weapons[weapon->id].flags & WF_MATTER_WEAPON)) { // Do the breaking glass stuff if (!((Game_mode & GM_MULTI) && Netgame.local_role == LR_CLIENT)) BreakGlassFace(rp, hitwall, hitpnt, &weapon->mtype.phys_info.velocity); ain_hear hear; hear.f_directly_player = false; hear.hostile_level = 0.9f; hear.curiosity_level = 1.0f; if (sound_override_glass_breaking == -1) hear.max_dist = Sounds[SOUND_BREAKING_GLASS].max_distance; else hear.max_dist = Sounds[sound_override_glass_breaking].max_distance; AINotify(weapon, AIN_HEAR_NOISE, (void *)&hear); return false; } } // Add a scorch if ((Weapons[weapon->id].scorch_handle != -1) && !f_water && !f_volatile && !f_lava) AddScorch(hitseg, hitwall, hitpnt, Weapons[weapon->id].scorch_handle, Weapons[weapon->id].scorch_size); if (ROOMNUM_OUTSIDE(hitseg)) { if (Weapons[weapon->id].terrain_damage_size > 0 && Weapons[weapon->id].terrain_damage_depth > 0) { DeformTerrain(&weapon->pos, Weapons[weapon->id].terrain_damage_depth, Weapons[weapon->id].terrain_damage_size); } } // Do special smoke, etc. DoWallEffects(weapon, tmap); // look for lava, volatile, water check_for_special_surface(weapon, tmap, wall_normal, hit_dot); // If dead, we're done if (weapon->flags & OF_DEAD) return true; // If done bouncing, kill the weapon if ((weapon->mtype.phys_info.num_bounces <= 0) && !(weapon->mtype.phys_info.flags & PF_STICK) && (hit_dot > weapon->mtype.phys_info.hit_die_dot)) { int snd; ain_hear hear; hear.f_directly_player = false; snd = Weapons[weapon->id].sounds[WSI_IMPACT_WALL]; hear.hostile_level = 0.9f; hear.curiosity_level = 0.1f; if (snd != SOUND_NONE_INDEX) { hear.max_dist = Sounds[snd].max_distance; Sound_system.Play3dSound(snd, SND_PRIORITY_NORMAL, weapon); AINotify(weapon, AIN_HEAR_NOISE, (void *)&hear); } // Check to see if we should spawn if ((Weapons[weapon->id].flags & WF_SPAWNS_IMPACT) && Weapons[weapon->id].spawn_count > 0 && Weapons[weapon->id].spawn_handle >= 0) CreateImpactSpawnFromWeapon(weapon, wall_normal); if ((Weapons[weapon->id].flags & WF_SPAWNS_ROBOT) && (Weapons[weapon->id].flags & WF_COUNTERMEASURE) && Weapons[weapon->id].robot_spawn_handle >= 0) CreateRobotSpawnFromWeapon(weapon); DoWeaponExploded(weapon, wall_normal); SetObjectDeadFlag(weapon); } else { // weapon is bouncing if (Weapons[weapon->id].sounds[WSI_BOUNCE] != SOUND_NONE_INDEX) { Sound_system.Play3dSound(Weapons[weapon->id].sounds[WSI_BOUNCE], SND_PRIORITY_HIGH, weapon); if (Weapons[weapon->id].sounds[WSI_BOUNCE] > -1) { ain_hear hear; hear.f_directly_player = false; hear.hostile_level = 0.20f; hear.curiosity_level = 0.6f; hear.max_dist = Sounds[Weapons[weapon->id].sounds[WSI_BOUNCE]].max_distance; AINotify(weapon, AIN_HEAR_NOISE, (void *)&hear); } } } return true; } // Prints out a marker hud message if needed void collide_player_and_marker(object *playerobj, object *marker_obj, vector *collision_point, vector *collision_normal, bool f_reverse_normal, fvi_info *hit_info) { if (playerobj->id == Player_num) { char str[100]; snprintf(str, sizeof(str), "Marker: %s", MarkerMessages[marker_obj->id]); AddHUDMessage(str); } } #define MIN_WALL_HIT_SOUND_VEL 40.0f #define MAX_WALL_HIT_SOUND_VEL 120.0f #define MIN_PLAYER_WALL_SOUND_TIME .1f #define WALL_DAMAGE 0.5f #define MIN_WALL_HIT_DAMAGE_SHIELDS 5 #define MIN_WALL_DAMAGE_SPEED 65.0 #define VOLATILE_DAMAGE 7.0f // damage per hit void collide_player_and_wall(object *playerobj, float hitspeed, int hitseg, int hitwall, vector *hitpt, vector *wall_normal, float hit_dot) { float volume = MAX_GAME_VOLUME; bool f_volatile, f_lava; bool f_forcefield; int tmap; if (playerobj->flags & OF_DYING) playerobj->ctype.dying_info.delay_time *= 0.9f; // Check for a trigger on this wall if (!ROOMNUM_OUTSIDE(hitseg)) // Make sure we've hit a wall, and not terrain { CheckTrigger(hitseg, hitwall, playerobj, TT_COLLIDE); tmap = Rooms[hitseg].faces[hitwall].tmap; } else { tmap = Terrain_tex_seg[Terrain_seg[CELLNUM(hitseg)].texseg_index].tex_index; } f_lava = (GameTextures[tmap].flags & TF_LAVA) != 0; f_volatile = (GameTextures[tmap].flags & TF_VOLATILE) != 0; f_forcefield = (GameTextures[tmap].flags & TF_FORCEFIELD) != 0; if (f_lava) { if (!((Game_mode & GM_MULTI) && (Netgame.local_role == LR_CLIENT))) { int id = FindWeaponName("NapalmBlob"); if (id >= 0) SetNapalmDamageEffect(playerobj, NULL, id); else Int3(); } } // If volatile, make the sound & apply damage if (f_volatile) { if (playerobj == Player_object) { if ((!(Game_mode & GM_MULTI)) || Netgame.local_role == LR_SERVER) ApplyDamageToPlayer(playerobj, playerobj, PD_VOLATILE_HISS, VOLATILE_DAMAGE); else { Multi_requested_damage_type = PD_VOLATILE_HISS; Multi_requested_damage_amount += (VOLATILE_DAMAGE); } } } if (f_forcefield) { if (playerobj == Player_object && sound_override_force_field == -1) { if ((!(Game_mode & GM_MULTI)) || Netgame.local_role == LR_SERVER) ApplyDamageToPlayer(playerobj, playerobj, PD_ENERGY_WEAPON, FORCEFIELD_DAMAGE); else { Multi_requested_damage_type = PD_ENERGY_WEAPON; Multi_requested_damage_amount += (FORCEFIELD_DAMAGE); } } if (sound_override_force_field == -1) Sound_system.Play3dSound(SOUND_FORCEFIELD_BOUNCE, SND_PRIORITY_HIGH, playerobj, MAX_GAME_VOLUME); else Sound_system.Play3dSound(sound_override_force_field, SND_PRIORITY_HIGH, playerobj, MAX_GAME_VOLUME); } if (f_forcefield || f_lava || f_volatile) { ain_hear hear; hear.f_directly_player = true; hear.hostile_level = 0.90f; hear.curiosity_level = 0.6f; if (sound_override_force_field == -1) hear.max_dist = Sounds[SOUND_FORCEFIELD_BOUNCE].max_distance; else hear.max_dist = Sounds[sound_override_force_field].max_distance; AINotify(playerobj, AIN_HEAR_NOISE, (void *)&hear); } // Do sound stuff when a player hits a wall if (!f_forcefield && !f_volatile && !f_lava && hitspeed > MIN_WALL_HIT_SOUND_VEL && Players[playerobj->id].last_hit_wall_sound_time + MIN_PLAYER_WALL_SOUND_TIME < Gametime) { if (hitspeed < MAX_WALL_HIT_SOUND_VEL) { volume = MAX_GAME_VOLUME * (hitspeed - MIN_WALL_HIT_SOUND_VEL) / (MAX_WALL_HIT_SOUND_VEL - MIN_WALL_HIT_SOUND_VEL); } if (hitspeed > MIN_WALL_DAMAGE_SPEED) { if (playerobj == Player_object && playerobj->shields >= MIN_WALL_HIT_DAMAGE_SHIELDS) { if ((!(Game_mode & GM_MULTI)) || Netgame.local_role == LR_SERVER) ApplyDamageToPlayer(playerobj, playerobj, PD_WALL_HIT, WALL_DAMAGE); else { Multi_requested_damage_type = PD_WALL_HIT; Multi_requested_damage_amount += (WALL_DAMAGE); } } } Sound_system.Play3dSound(SOUND_PLAYER_HIT_WALL, SND_PRIORITY_NORMAL, playerobj, volume); if (Demo_flags == DF_RECORDING) DemoWrite3DSound(SOUND_PLAYER_HIT_WALL, OBJNUM(playerobj), 1, volume); ain_hear hear; hear.f_directly_player = true; hear.hostile_level = 0.10f; hear.curiosity_level = 0.5f; hear.max_dist = Sounds[SOUND_PLAYER_HIT_WALL].max_distance * volume; AINotify(playerobj, AIN_HEAR_NOISE, (void *)&hear); Players[playerobj->id].last_hit_wall_sound_time = Gametime; } if (Players[playerobj->id].flags & PLAYER_FLAGS_DYING) StartPlayerExplosion(playerobj->id); // Do ForceFeedback //---------------- if (playerobj->id == Player_num && ForceIsEnabled()) { DoForceForWall(playerobj, hitspeed, hitseg, hitwall, wall_normal); } return; } void collide_generic_and_wall(object *genericobj, float hitspeed, int hitseg, int hitwall, vector *hitpt, vector *wall_normal, float hit_dot) { bool f_volatile, f_lava; bool f_forcefield; float volume = MAX_GAME_VOLUME; // Check for a trigger on this wall int tmap; if (!ROOMNUM_OUTSIDE(hitseg)) // Make sure we've hit a wall, and not terrain { CheckTrigger(hitseg, hitwall, genericobj, TT_COLLIDE); tmap = Rooms[hitseg].faces[hitwall].tmap; } else { tmap = Terrain_tex_seg[Terrain_seg[CELLNUM(hitseg)].texseg_index].tex_index; } f_forcefield = (GameTextures[tmap].flags & TF_FORCEFIELD) != 0; f_volatile = (GameTextures[tmap].flags & TF_VOLATILE) != 0; f_lava = (GameTextures[tmap].flags & TF_LAVA) != 0; if (IS_GUIDEBOT(genericobj)) { f_forcefield = false; f_volatile = false; f_lava = false; } if (f_lava) { if (!((Game_mode & GM_MULTI) && (Netgame.local_role == LR_CLIENT))) { int id = FindWeaponName("NapalmBlob"); if (id >= 0) SetNapalmDamageEffect(genericobj, NULL, id); else Int3(); } } // If volatile, make the sound & apply damage if (f_volatile && !((Game_mode & GM_MULTI) && (Netgame.local_role == LR_CLIENT))) { ApplyDamageToGeneric(genericobj, genericobj, GD_VOLATILE_HISS, VOLATILE_DAMAGE); Sound_system.Play3dSound(SOUND_VOLATILE_HISS, SND_PRIORITY_HIGHEST, genericobj, MAX_GAME_VOLUME); } if (f_forcefield && !((Game_mode & GM_MULTI) && (Netgame.local_role == LR_CLIENT))) { if (sound_override_force_field == -1) { Sound_system.Play3dSound(SOUND_FORCEFIELD_BOUNCE, SND_PRIORITY_LOW, genericobj, MAX_GAME_VOLUME); ApplyDamageToGeneric(genericobj, genericobj, GD_ENERGY, FORCEFIELD_DAMAGE); } else { Sound_system.Play3dSound(sound_override_force_field, SND_PRIORITY_LOW, genericobj, MAX_GAME_VOLUME); } } if (genericobj->control_type != CT_AI) { if (!f_forcefield && !f_volatile && !f_lava && hitspeed > MIN_WALL_HIT_SOUND_VEL) { if (hitspeed < MAX_WALL_HIT_SOUND_VEL) { volume = MAX_GAME_VOLUME * (hitspeed - MIN_WALL_HIT_SOUND_VEL) / (MAX_WALL_HIT_SOUND_VEL - MIN_WALL_HIT_SOUND_VEL); } if (hitspeed > MIN_WALL_DAMAGE_SPEED) { if ((!(Game_mode & GM_MULTI)) || Netgame.local_role == LR_SERVER) { ApplyDamageToGeneric(genericobj, genericobj, GD_PHYSICS, WALL_DAMAGE); } } Sound_system.Play3dSound(SOUND_PLAYER_HIT_WALL, SND_PRIORITY_LOW, genericobj, volume); if (Demo_flags == DF_RECORDING) DemoWrite3DSound(SOUND_PLAYER_HIT_WALL, OBJNUM(genericobj), 1, volume); } } return; } float Last_volatile_scrape_sound_time = 0; // this gets called when an object is scraping along the wall void scrape_object_on_wall(object *obj, int hitseg, int hitwall, vector *hitpt, vector *wall_normal) { /* switch (obj->type) { case OBJ_PLAYER: if (obj->id==Player_num) { int type; //mprintf((0, "Scraped segment #%3i, side #%i\n", hitseg, hitside)); if ((type=check_volatile_wall(obj,hitseg,hitside,hitpt))!=0) { vector hit_dir, rand_vec; if ((GameTime > Last_volatile_scrape_sound_time + F1_0/4) || (GameTime < Last_volatile_scrape_sound_time)) { int sound = (type==1)?SOUND_VOLATILE_WALL_HISS:SOUND_SHIP_IN_WATER; Last_volatile_scrape_sound_time = GameTime; digi_link_sound_to_pos( sound, hitseg, 0, hitpt, 0, F1_0 ); if (Game_mode & GM_MULTI) multi_send_play_sound(sound, F1_0); } #ifdef COMPACT_SEGS get_side_normal(&Segments[hitseg], higside, 0, &hit_dir ); #else hit_dir = Segments[hitseg].sides[hitside].normals[0]; #endif make_random_vector(&rand_vec); vm_vec_scale_add2(&hit_dir, &rand_vec, F1_0/8); vm_vec_normalize_quick(&hit_dir); bump_one_object(obj, &hit_dir, F1_0*8); } //@@} else { //@@ //what scrape sound //@@ //PLAY_SOUND( SOUND_PLAYER_SCRAPE_WALL ); //@@} } break; //these two kinds of objects below shouldn't really slide, so //if this scrape routine gets called (which it might if the //object (such as a fusion blob) was created already poking //through the wall) call the collide routine. case OBJ_WEAPON: collide_weapon_and_wall(obj,0,hitseg,hitside,hitpt); break; case OBJ_DEBRIS: collide_debris_and_wall(obj,0,hitseg,hitside,hitpt); break; } */ } /* void apply_damage_to_player(object *playerobj, object *killer, float damage) { if (Player_is_dead) return; if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE) return; if (Endlevel_sequence) return; //for the player, the 'real' shields are maintained in the Players[] //array. The shields value in the player's object are, I think, not //used anywhere. This routine, however, sets the objects shields to //be a mirror of the value in the Player structure. if (playerobj->id == Player_num) { //is this the local player? // MK: 08/14/95: This code can never be reached. See the return about 12 lines up. // -- if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE) { // -- // -- //invincible, so just do blue flash // -- // -- PALETTE_FLASH_ADD(0,0,f2i(damage)*4); //flash blue // -- // -- } // -- else { //take damage, do red flash Players[Player_num].shields -= damage; PALETTE_FLASH_ADD(f2i(damage)*4,-f2i(damage/2),-f2i(damage/2)); //flash red // -- } if (Players[Player_num].shields < 0) { Players[Player_num].killer_objnum = killer-Objects; // if ( killer && (killer->type == OBJ_PLAYER)) // Players[Player_num].killer_objnum = killer-Objects; playerobj->flags |= OF_DEAD; if (Buddy_objnum != -1) if ((killer->type == OBJ_ROBOT) && (Robot_info[killer->id].companion)) Buddy_sorry_time = GameTime; } // -- removed, 09/06/95, MK -- else if (Players[Player_num].shields < LOSE_WEAPON_THRESHOLD) { // -- removed, 09/06/95, MK -- int randnum = ps_rand(); // -- removed, 09/06/95, MK -- // -- removed, 09/06/95, MK -- if (floatmul(Players[Player_num].shields, randnum) < damage/4) { // -- removed, 09/06/95, MK -- if (ps_rand() > 20000) { // -- removed, 09/06/95, MK -- destroy_secondary_weapon(Secondary_weapon); // -- removed, 09/06/95, MK -- } else if (Primary_weapon == 0) { // -- removed, 09/06/95, MK -- if (Players[Player_num].flags & PLAYER_FLAGS_QUAD_LASERS) // -- removed, 09/06/95, MK -- destroy_primary_weapon(MAX_PRIMARY_WEAPONS); // This means to destroy quad laser. // -- removed, 09/06/95, MK -- else if (Players[Player_num].laser_level > 0) // -- removed, 09/06/95, MK -- destroy_primary_weapon(Primary_weapon); // -- removed, 09/06/95, MK -- } else // -- removed, 09/06/95, MK -- destroy_primary_weapon(Primary_weapon); // -- removed, 09/06/95, MK -- } else // -- removed, 09/06/95, MK -- ; // mprintf((0, "%8x > %8x, so don't lose weapon.\n", floatmul(Players[Player_num].shields, randnum), damage/4)); // -- removed, 09/06/95, MK -- } playerobj->shields = Players[Player_num].shields; //mirror } } */ void CollideAnglesToMatrix(matrix *m, float p, float h, float b) { float sinp, cosp, sinb, cosb, sinh, cosh; sinp = sin(p); cosp = cos(p); sinb = sin(b); cosb = cos(b); sinh = sin(h); cosh = cos(h); m->rvec.x = cosb * cosh; m->rvec.y = cosb * sinp * sinh - cosp * sinb; m->rvec.z = cosb * cosp * sinh + sinb * sinp; m->uvec.x = cosh * sinb; m->uvec.y = sinb * sinp * sinh + cosp * cosb; m->uvec.z = sinb * cosp * sinh - cosb * sinp; m->fvec.x = -sinh; m->fvec.y = sinp * cosh; m->fvec.z = cosp * cosh; } vector *CollideExtractAnglesFromMatrix(vector *a, matrix *m) { float sinh, cosh, sinp, sinb; sinh = -m->fvec.x; if (sinh < -1.0f) sinh = -1.0f; else if (sinh > 1.0f) sinh = 1.0f; a->y = asin(sinh); cosh = cos(a->y); ASSERT(cosh != 0.0); sinp = m->fvec.y / cosh; if (sinp < -1.0f) sinp = -1.0f; else if (sinp > 1.0f) sinp = 1.0f; sinb = m->uvec.x / cosh; if (sinb < -1.0f) sinb = -1.0f; else if (sinb > 1.0f) sinb = 1.0f; a->x = asin(sinp); a->z = asin(sinb); return a; } void ConvertEulerToAxisAmount(vector *e, vector *n, float *w) { float rotspeed = vm_GetMagnitude(e); matrix rotmat; vector e_n; float scale = rotspeed / .0001f; // If there isn't a rotation, return something valid if (rotspeed == 0.0f || scale == 0.0f) { *n = Zero_vector; n->y = 1.0f; *w = 0.0f; return; } e_n = *e / scale; // vector f; CollideAnglesToMatrix(&rotmat, e_n.x, e_n.y, e_n.z); // mprintf((0, "F %f, %f, %f\n", XYZ(&rotmat.fvec))); // mprintf((0, "R %f, %f, %f\n", XYZ(&rotmat.rvec))); // mprintf((0, "U %f, %f, %f\n", XYZ(&rotmat.uvec))); // CollideExtractAnglesFromMatrix(&f, &rotmat); // mprintf((0, "Before %f, %f, %f\n", XYZ(&e_n))); // mprintf((0, "After %f, %f, %f\n", XYZ(&f))); // This is from Graphics Gems 1 p.467 I am converting from a angle vector // to the normal of that rotation (you can also get the angle about that normal, but // we don't need it) n->x = rotmat.uvec.z - rotmat.fvec.y; n->y = rotmat.fvec.x - rotmat.rvec.z; n->z = rotmat.rvec.y - rotmat.uvec.x; if (*n != Zero_vector) { vm_NormalizeVector(n); float ct = (rotmat.rvec.x + rotmat.uvec.y + rotmat.fvec.z - 1.0f) / 2.0f; if (ct < -1.0f) ct = -1.0f; else if (ct > 1.0f) ct = 1.0f; float v = acos(ct); float z = sin(v); // if(v < 0.0) // *w = -rotspeed; // else *w = rotspeed * ((2.0f * PI) / (65535.0f)); if (z >= 0.0f) *n *= -1.0f; } else { *w = 0.0f; } } void ConvertAxisAmountToEuler(vector *n, float *w, vector *e) { float s; float c; float t; float scale = *w / .0001f; float w_n = .0001f; vector s_result; if (*w == 0.0f) { *e = Zero_vector; return; } s = sin(.0001); c = cos(.0001); t = 1.0f - c; matrix rotmat; const float sx = s * n->x; const float sy = s * n->y; const float sz = s * n->z; const float txy = t * n->x * n->y; const float txz = t * n->x * n->z; const float tyz = t * n->y * n->z; const float txx = t * n->x * n->x; const float tyy = t * n->y * n->y; const float tzz = t * n->z * n->z; rotmat.rvec.x = txx + c; rotmat.rvec.y = txy - sz; rotmat.rvec.z = txz + sy; rotmat.uvec.x = txy + sz; rotmat.uvec.y = tyy + c; rotmat.uvec.z = tyz - sx; rotmat.fvec.x = txz - sy; rotmat.fvec.y = tyz + sx; rotmat.fvec.z = tzz + c; CollideExtractAnglesFromMatrix(&s_result, &rotmat); e->x = (s_result.x) * scale * (65535.0f / (2.0 * PI)); e->y = (s_result.y) * scale * (65535.0f / (2.0 * PI)); e->z = (s_result.z) * scale * (65535.0f / (2.0 * PI)); } void bump_obj_against_fixed(object *obj, vector *collision_point, vector *collision_normal) { ASSERT(_finite(obj->mtype.phys_info.rotvel.x) != 0); ASSERT(_finite(obj->mtype.phys_info.rotvel.y) != 0); ASSERT(_finite(obj->mtype.phys_info.rotvel.z) != 0); ASSERT(_finite(obj->mtype.phys_info.velocity.x) != 0); ASSERT(_finite(obj->mtype.phys_info.velocity.y) != 0); ASSERT(_finite(obj->mtype.phys_info.velocity.z) != 0); if (!IsOKToApplyForce(obj)) return; vector r1 = *collision_point - obj->pos; vector w1; vector n1; float temp1; float j; matrix o_t1 = obj->orient; vm_TransposeMatrix(&o_t1); vector cmp1 = obj->mtype.phys_info.rotvel * o_t1; ConvertEulerToAxisAmount(&cmp1, &n1, &temp1); n1 *= temp1; if (temp1 != 0.0f) { vm_CrossProduct(&w1, &n1, &r1); } else { w1 = Zero_vector; } vector p1 = obj->mtype.phys_info.velocity + w1; float v_rel; float m1 = obj->mtype.phys_info.mass; ASSERT(m1 != 0.0f); if (m1 <= 0.0f) m1 = 0.00000001f; v_rel = *collision_normal * (p1); float e = obj->mtype.phys_info.coeff_restitution; vector c1; vector cc1; float cv1; // matrix i1; // matrix i2; float i1 = (2.0f / 5.0f) * m1 * obj->size * obj->size; if (i1 < .0000001) i1 = .0000001f; vm_CrossProduct(&c1, &r1, collision_normal); c1 = c1 / i1; vm_CrossProduct(&cc1, &c1, &r1); cv1 = (*collision_normal) * c1; j = (-(1.0f + e)) * v_rel; j /= (1 / m1 + cv1); obj->mtype.phys_info.velocity += ((j * (*collision_normal)) / m1); vector jcn = j * (*collision_normal); vm_CrossProduct(&c1, &r1, &jcn); n1 = (c1) / i1; temp1 = vm_NormalizeVector(&n1); vector txx1; ConvertAxisAmountToEuler(&n1, &temp1, &txx1); obj->mtype.phys_info.rotvel += (txx1 * obj->orient); ASSERT(_finite(obj->mtype.phys_info.rotvel.x) != 0); ASSERT(_finite(obj->mtype.phys_info.rotvel.y) != 0); ASSERT(_finite(obj->mtype.phys_info.rotvel.z) != 0); ASSERT(_finite(obj->mtype.phys_info.velocity.x) != 0); ASSERT(_finite(obj->mtype.phys_info.velocity.y) != 0); ASSERT(_finite(obj->mtype.phys_info.velocity.z) != 0); // hack rotvel /* vector v = obj->mtype.phys_info.velocity; vector c = *collision_normal; if(v != Zero_vector) { vm_NormalizeVector(&v); if(v != c) { float rad = vm_VectorDistance(&obj->pos, collision_point); float rotvel = vm_GetMagnitude(&obj->mtype.phys_info.velocity)*((2*PI*rad)); vector r; vector e; vm_CrossProduct(&r, &v, &c); vm_NormalizeVector(&r); ConvertAxisAmountToEuler(&r, &rotvel, &e); matrix rrr = obj->orient; // vm_TransposeMatrix(&rrr); obj->mtype.phys_info.rotvel = rrr * r; } else { obj->mtype.phys_info.rotvel = Zero_vector; } } else { obj->mtype.phys_info.rotvel = Zero_vector; }*/ } void bump_two_objects(object *object0, object *object1, vector *collision_point, vector *collision_normal, int damage_flag) { // vector force; //dv, object *t = NULL; object *other = NULL; // Determine if a moving object hits a non-moving object if ((object0->movement_type != MT_PHYSICS && object0->movement_type != MT_WALKING) || (object0->movement_type == MT_PHYSICS && object0->mtype.phys_info.velocity == Zero_vector && (object0->mtype.phys_info.flags & PF_LOCK_MASK) && (object0->mtype.phys_info.flags & PF_POINT_COLLIDE_WALLS))) { t = object1; other = object0; *collision_normal *= -1.0f; } if ((object1->movement_type != MT_PHYSICS && object1->movement_type != MT_WALKING) || (object1->movement_type == MT_PHYSICS && object1->mtype.phys_info.velocity == Zero_vector && (object1->mtype.phys_info.flags & PF_LOCK_MASK) && (object1->mtype.phys_info.flags & PF_POINT_COLLIDE_WALLS))) { t = object0; other = object1; } // If we hit a non-moving object... if (t) { // chrishack -- walker hack if (t->movement_type != MT_PHYSICS && t->movement_type != MT_WALKING) { t->mtype.phys_info.velocity = Zero_vector; return; } if (t->mtype.phys_info.flags & PF_PERSISTENT) return; vector moved_v; float wall_part; float luke_test; if (t->type == OBJ_PLAYER) { luke_test = vm_GetMagnitude(&t->mtype.phys_info.velocity); } if (!(t->flags & OF_DEAD)) { // bump_obj_against_fixed(t, collision_point, collision_normal); // } ///* // Find hit speed moved_v = t->pos - t->last_pos; wall_part = *collision_normal * t->mtype.phys_info.velocity; if (t->mtype.phys_info.flags & PF_BOUNCE) { wall_part *= 2.0; // Subtract out wall part twice to achieve bounce // New bounceness code if ((t->mtype.phys_info.flags & PF_BOUNCE) && (t->mtype.phys_info.num_bounces != PHYSICS_UNLIMITED_BOUNCE)) { if (t->mtype.phys_info.num_bounces == 0) { ASSERT(t->type != OBJ_PLAYER); if (t->flags & OF_DYING) { ASSERT((t->control_type == CT_DYING) || (t->control_type == CT_DYING_AND_AI)); DestroyObject(t, 50.0, t->ctype.dying_info.death_flags); } else SetObjectDeadFlag(t); } } t->mtype.phys_info.num_bounces--; t->mtype.phys_info.velocity += *collision_normal * (wall_part * -1.0f); if (t->mtype.phys_info.coeff_restitution != 1.0f) t->mtype.phys_info.velocity -= (t->mtype.phys_info.velocity * (1.0f - t->mtype.phys_info.coeff_restitution)); } else { float wall_force; wall_force = t->mtype.phys_info.thrust * *collision_normal; t->mtype.phys_info.thrust += *collision_normal * (wall_force * -1.001); // 1.001 so that we are not quite tangential // Update velocity from wall hit. t->mtype.phys_info.velocity += *collision_normal * (wall_part * -1.001); // 1.001 so that we are not quite tangential if (t->type == OBJ_PLAYER) { float real_vel; real_vel = vm_NormalizeVector(&t->mtype.phys_info.velocity); t->mtype.phys_info.velocity *= ((real_vel + luke_test) / 2.0f); // obj->mtype.phys_info.velocity *= (luke_test); } } // Weapons should face their new heading. This is so missiles are pointing in the correct direct. if (t->type == OBJ_WEAPON && (t->mtype.phys_info.flags & (PF_BOUNCE | PF_GRAVITY | PF_WIND))) vm_VectorToMatrix(&t->orient, &t->mtype.phys_info.velocity, &t->orient.uvec, NULL); } // Return it to the original direction if (object0->movement_type != MT_PHYSICS && object0->movement_type != MT_WALKING) { *collision_normal *= -1.0f; } // Do ForceFeedback stuff for non-moving objects //----------------------------------------- if (ForceIsEnabled()) { if (object0->type == OBJ_PLAYER && object0->id == Player_num) { } } //*/ return; } // force = object0->mtype.phys_info.velocity - object1->mtype.phys_info.velocity; // force *= 2*(object0->mtype.phys_info.mass * object1->mtype.phys_info.mass)/(object0->mtype.phys_info.mass + // object1->mtype.phys_info.mass); // if(!(object1->mtype.phys_info.flags & PF_PERSISTENT)) // bump_this_object(object1, object0, &force, collision_point, damage_flag); // // force = -force; // // if(!(object0->mtype.phys_info.flags & PF_PERSISTENT)) // bump_this_object(object0, object1, &force, collision_point, damage_flag); // vector r_vel = object0->mtype.phys_info.velocity - object1->mtype.phys_info.velocity; // Add this back ASSERT(_finite(object1->mtype.phys_info.rotvel.x) != 0); ASSERT(_finite(object1->mtype.phys_info.rotvel.y) != 0); ASSERT(_finite(object1->mtype.phys_info.rotvel.z) != 0); ASSERT(_finite(object0->mtype.phys_info.rotvel.x) != 0); ASSERT(_finite(object0->mtype.phys_info.rotvel.y) != 0); ASSERT(_finite(object0->mtype.phys_info.rotvel.z) != 0); ASSERT(_finite(object1->mtype.phys_info.velocity.x) != 0); ASSERT(_finite(object1->mtype.phys_info.velocity.y) != 0); ASSERT(_finite(object1->mtype.phys_info.velocity.z) != 0); ASSERT(_finite(object0->mtype.phys_info.velocity.x) != 0); ASSERT(_finite(object0->mtype.phys_info.velocity.y) != 0); ASSERT(_finite(object0->mtype.phys_info.velocity.z) != 0); vector r1 = *collision_point - object0->pos; vector r2 = *collision_point - object1->pos; vector w1; vector w2; vector n1; vector n2; float temp1; float temp2; float j; matrix o_t1 = object0->orient; matrix o_t2 = object1->orient; vm_TransposeMatrix(&o_t1); vm_TransposeMatrix(&o_t2); vector cmp1 = object0->mtype.phys_info.rotvel * o_t1; vector cmp2 = object1->mtype.phys_info.rotvel * o_t2; ConvertEulerToAxisAmount(&cmp1, &n1, &temp1); ConvertEulerToAxisAmount(&cmp2, &n2, &temp2); n1 *= temp1; n2 *= temp2; if (temp1 != 0.0f) { vm_CrossProduct(&w1, &n1, &r1); } else { w1 = Zero_vector; } if (temp2 != 0.0f) { vm_CrossProduct(&w2, &n2, &r2); } else { w2 = Zero_vector; } vector p1 = object0->mtype.phys_info.velocity + w1; vector p2 = object1->mtype.phys_info.velocity + w2; float v_rel; float m1 = object0->mtype.phys_info.mass; float m2 = object1->mtype.phys_info.mass; bool f_force_1 = IsOKToApplyForce(object0); bool f_force_2 = IsOKToApplyForce(object1); ASSERT(m1 != 0.0f && m2 != 0.0f); if (m1 <= 0.0f) m1 = 0.00000001f; if (m2 <= 0.0f) m2 = 0.00000001f; v_rel = *collision_normal * (p1 - p2); float e; if (object0->type == OBJ_PLAYER && object1->type == OBJ_PLAYER) e = 0.015f; else if ((object0->type == OBJ_WEAPON || object1->type == OBJ_WEAPON) && (object0->type != OBJ_PLAYER && object1->type != OBJ_PLAYER)) e = 1.0f; else if ((object0->type == OBJ_CLUTTER && object1->type == OBJ_PLAYER) || (object0->type == OBJ_PLAYER && object1->type == OBJ_CLUTTER)) e = 0.5f; else e = 0.1f; vector c1; vector c2; vector cc1; vector cc2; float cv1; float cv2; // matrix i1; // matrix i2; float i1 = (2.0f / 5.0f) * m1 * object0->size * object0->size; float i2 = (2.0f / 5.0f) * m2 * object1->size * object1->size; if (i1 < .0000001) i1 = .0000001f; if (i2 < .0000001) i2 = .0000001f; vm_CrossProduct(&c1, &r1, collision_normal); vm_CrossProduct(&c2, &r2, collision_normal); c1 = c1 / i1; c2 = c2 / i2; vm_CrossProduct(&cc1, &c1, &r1); vm_CrossProduct(&cc2, &c2, &r2); cv1 = (*collision_normal) * c1; cv2 = (*collision_normal) * c2; j = (-(1.0f + e)) * v_rel; j /= (1 / m1 + 1 / m2 + cv1 + cv2); if (f_force_1) object0->mtype.phys_info.velocity += ((j * (*collision_normal)) / m1); if (f_force_2) object1->mtype.phys_info.velocity -= ((j * (*collision_normal)) / m2); vector jcn = j * (*collision_normal); vm_CrossProduct(&c1, &r1, &jcn); vm_CrossProduct(&c2, &r2, &jcn); n1 = (c1) / i1; n2 = (c2) / i2; temp1 = vm_NormalizeVector(&n1); temp2 = vm_NormalizeVector(&n2); vector txx1; vector txx2; ConvertAxisAmountToEuler(&n1, &temp1, &txx1); ConvertAxisAmountToEuler(&n2, &temp2, &txx2); float rotscale1, rotscale2; if (object0->type == OBJ_PLAYER) rotscale1 = PLAYER_ROTATION_BY_FORCE_SCALAR; else rotscale1 = NONPLAYER_ROTATION_BY_FORCE_SCALAR; if (object1->type == OBJ_PLAYER) rotscale2 = PLAYER_ROTATION_BY_FORCE_SCALAR; else rotscale2 = NONPLAYER_ROTATION_BY_FORCE_SCALAR; if (f_force_1) object0->mtype.phys_info.rotvel += (txx1 * object0->orient) * rotscale1; if (f_force_2) object1->mtype.phys_info.rotvel += (txx2 * object1->orient) * rotscale2; ASSERT(_finite(object1->mtype.phys_info.rotvel.x) != 0); ASSERT(_finite(object1->mtype.phys_info.rotvel.y) != 0); ASSERT(_finite(object1->mtype.phys_info.rotvel.z) != 0); ASSERT(_finite(object0->mtype.phys_info.rotvel.x) != 0); ASSERT(_finite(object0->mtype.phys_info.rotvel.y) != 0); ASSERT(_finite(object0->mtype.phys_info.rotvel.z) != 0); ASSERT(_finite(object1->mtype.phys_info.velocity.x) != 0); ASSERT(_finite(object1->mtype.phys_info.velocity.y) != 0); ASSERT(_finite(object1->mtype.phys_info.velocity.z) != 0); ASSERT(_finite(object0->mtype.phys_info.velocity.x) != 0); ASSERT(_finite(object0->mtype.phys_info.velocity.y) != 0); ASSERT(_finite(object0->mtype.phys_info.velocity.z) != 0); // Do ForceFeedback stuff for moving objects //----------------------------------------- if (ForceIsEnabled()) { if (object0->type == OBJ_PLAYER && object0->id == Player_num) { // v is the force vector vector v; v = -1.0 * v_rel * (*collision_normal); // Was it weapon->player collide switch (object1->type) { case OBJ_WEAPON: // Do force effect for player/weapon collision DoForceForWeapon(object0, object1, &v); break; default: break; } } } // Catch things on fire if possible if (object0->effect_info && object1->effect_info) { // One of these objects must be burning if ((object0->effect_info->type_flags & EF_NAPALMED) || (object1->effect_info->type_flags & EF_NAPALMED)) { // Both cannot be burning if (!(object0->effect_info->type_flags & EF_NAPALMED) || !(object1->effect_info->type_flags & EF_NAPALMED)) { object *src_obj, *dest_obj; if (object0->effect_info->type_flags & EF_NAPALMED) { src_obj = object0; dest_obj = object1; } else { src_obj = object1; dest_obj = object0; } dest_obj->effect_info->type_flags |= EF_NAPALMED; dest_obj->effect_info->damage_time = std::max(1.0, src_obj->effect_info->damage_time / 3.0); dest_obj->effect_info->damage_per_second = src_obj->effect_info->damage_per_second; // We need this cap (as the gb burns forever if (dest_obj->effect_info->damage_time > 10.0f) { dest_obj->effect_info->damage_time = 10.0f; } dest_obj->effect_info->last_damage_time = 0; dest_obj->effect_info->damage_handle = src_obj->handle; if (dest_obj->effect_info->sound_handle == SOUND_NONE_INDEX) dest_obj->effect_info->sound_handle = Sound_system.Play3dSound(SOUND_PLAYER_BURNING, SND_PRIORITY_HIGHEST, dest_obj); } } } } void collide_player_and_player(object *p1, object *p2, vector *collision_point, vector *collision_normal, bool f_reverse_normal, fvi_info *hit_info) { if (f_reverse_normal) *collision_normal *= -1.0f; vector rvel = p1->mtype.phys_info.velocity - p2->mtype.phys_info.velocity; float speed = vm_NormalizeVector(&rvel); float scalar; if (speed <= MIN_WALL_HIT_SOUND_VEL) { scalar = 0.0f; } else if (speed >= MAX_WALL_HIT_SOUND_VEL) { scalar = 1.0f; } else { scalar = (speed - MIN_WALL_HIT_SOUND_VEL) / (MAX_WALL_HIT_SOUND_VEL - MIN_WALL_HIT_SOUND_VEL); } if (scalar > .01) { pos_state cur_pos; cur_pos.position = collision_point; cur_pos.orient = &p1->orient; cur_pos.roomnum = p1->roomnum; Sound_system.Play3dSound(SOUND_PLAYER_HIT_WALL, SND_PRIORITY_HIGHEST, &cur_pos, MAX_GAME_VOLUME * scalar); } bump_two_objects(p1, p2, collision_point, collision_normal, 1); } #include "polymodel.h" void collide_generic_and_player(object *robotobj, object *playerobj, vector *collision_point, vector *collision_normal, bool f_reverse_normal, fvi_info *hit_info) { if (f_reverse_normal) *collision_normal *= -1.0f; vector rvel; if (robotobj->movement_type == MT_PHYSICS || robotobj->movement_type == MT_WALKING) { rvel = robotobj->mtype.phys_info.velocity; } else { rvel = Zero_vector; } rvel -= playerobj->mtype.phys_info.velocity; float speed = vm_NormalizeVector(&rvel); float scalar; if (speed <= MIN_WALL_HIT_SOUND_VEL) { scalar = 0.0f; } else if (speed >= MAX_WALL_HIT_SOUND_VEL) { scalar = 1.0f; } else { scalar = (speed - MIN_WALL_HIT_SOUND_VEL) / (MAX_WALL_HIT_SOUND_VEL - MIN_WALL_HIT_SOUND_VEL); } // Check for lava surface on an object if ((robotobj->type == OBJ_BUILDING) && hit_info) { poly_model *pm = GetPolymodelPointer(robotobj->rtype.pobj_info.model_num); int tmap = pm->textures[pm->submodel[hit_info->hit_subobject[0]].faces[hit_info->hit_face[0]].texnum]; if (GameTextures[tmap].flags & TF_LAVA) { if (!((Game_mode & GM_MULTI) && (Netgame.local_role == LR_CLIENT))) { int id = FindWeaponName("NapalmBlob"); if (id >= 0) SetNapalmDamageEffect(playerobj, NULL, id); else Int3(); } scalar = 0; // don't make collide sound } } if (scalar > .01 || (robotobj->mtype.phys_info.flags & PF_LOCK_MASK)) { pos_state cur_pos; cur_pos.position = collision_point; cur_pos.orient = &playerobj->orient; cur_pos.roomnum = playerobj->roomnum; Sound_system.Play3dSound(SOUND_PLAYER_HIT_WALL, SND_PRIORITY_HIGHEST, &cur_pos, MAX_GAME_VOLUME * scalar); ain_hear hear; hear.f_directly_player = true; hear.hostile_level = 0.10f; hear.curiosity_level = 0.5f; hear.max_dist = Sounds[SOUND_PLAYER_HIT_WALL].max_distance * scalar; AINotify(playerobj, AIN_HEAR_NOISE, (void *)&hear); if ((scalar > .25f && (robotobj->movement_type == MT_WALKING || robotobj->movement_type == MT_PHYSICS)) || ((robotobj->mtype.phys_info.flags & PF_LOCK_MASK) && (robotobj->mtype.phys_info.flags & PF_POINT_COLLIDE_WALLS))) { if (!(IS_GUIDEBOT(robotobj))) { if (robotobj->shields <= 1.0f) { ApplyDamageToGeneric(robotobj, playerobj, GD_PHYSICS, 2.0f); } else { ApplyDamageToGeneric(robotobj, playerobj, GD_PHYSICS, 5.0f * Frametime * scalar); } if (scalar < 1.0f && (robotobj->mtype.phys_info.flags & PF_LOCK_MASK) && (robotobj->mtype.phys_info.flags & PF_POINT_COLLIDE_WALLS)) scalar = 1.0f; ApplyDamageToPlayer(playerobj, playerobj, PD_WALL_HIT, 2.0f * Frametime * scalar); } } } // mprintf((0, "We hit a robot\n")); if (robotobj->control_type == CT_AI) { AINotify(robotobj, AIN_BUMPED_OBJ, (void *)playerobj); } /* if((GameTextures[Rooms[hitseg].faces[hitwall].tmap].flags & TF_VOLATILE) && !((Game_mode & GM_MULTI) && (Netgame.local_role==LR_CLIENT))) { int id = FindWeaponName("NapalmBlob"); if(id >= 0) { int objnum = ObjCreate(OBJ_WEAPON, id, playerobj->roomnum, &playerobj->pos, NULL, playerobj->handle); if(objnum >= 0) { object *weapon = &Objects[objnum]; float damage_to_apply = Weapons[weapon->id].damage; // Factor in multiplier damage_to_apply *= weapon->ctype.laser_info.multiplier; ApplyDamageToPlayer(playerobj, weapon, 0); SetObjectDeadFlag(objnum); } } } */ bump_two_objects(robotobj, playerobj, collision_point, collision_normal, 1); } void MakeWeaponStick(object *weapon, object *parent, fvi_info *hit_info) { weapon->mtype.obj_link_info.parent_handle = parent->handle; weapon->mtype.obj_link_info.fvec = hit_info->hit_subobj_fvec; weapon->mtype.obj_link_info.uvec = hit_info->hit_subobj_uvec; weapon->mtype.obj_link_info.pos = hit_info->hit_subobj_pos; weapon->mtype.obj_link_info.sobj_index = hit_info->hit_subobject[0]; weapon->movement_type = MT_OBJ_LINKED; } void collide_generic_and_weapon(object *robotobj, object *weapon, vector *collision_point, vector *collision_normal, bool f_reverse_normal, fvi_info *hit_info) { object *parent_obj; float damage_to_apply; ubyte electrical = (Weapons[weapon->id].flags & WF_ELECTRICAL) ? 1 : 0; bool f_stick = ((weapon->mtype.phys_info.flags & PF_STICK) != 0); bool f_energy = ((Weapons[weapon->id].flags & WF_MATTER_WEAPON) == 0); int damage_type; // Check for lava & volatile surfaces on an object if ((robotobj->type == OBJ_BUILDING) && hit_info) { poly_model *pm = GetPolymodelPointer(robotobj->rtype.pobj_info.model_num); int tmap = pm->textures[pm->submodel[hit_info->hit_subobject[0]].faces[hit_info->hit_face[0]].texnum]; vector *normal = &hit_info->hit_wallnorm[0]; DoWallEffects(weapon, tmap); check_for_special_surface(weapon, tmap, normal, 1.0); // If object died, stop processing if (weapon->flags & OF_DEAD) return; } if (Weapons[weapon->id].flags & WF_NAPALM) damage_type = GD_FIRE; else if (Weapons[weapon->id].flags & WF_MATTER_WEAPON) damage_type = GD_MATTER; else if (Weapons[weapon->id].flags & WF_ELECTRICAL) damage_type = GD_ELECTRIC; else damage_type = GD_ENERGY; if (f_reverse_normal) *collision_normal *= -1.0f; if (robotobj->flags & OF_DYING) robotobj->ctype.dying_info.delay_time *= 0.975f; if (Weapons[weapon->id].sounds[WSI_IMPACT_WALL] != SOUND_NONE_INDEX) { Sound_system.Play3dSound(Weapons[weapon->id].sounds[WSI_IMPACT_WALL], SND_PRIORITY_HIGH, weapon); ain_hear hear; hear.f_directly_player = false; if (robotobj->control_type == CT_AI) { hear.hostile_level = 1.0f; hear.curiosity_level = 0.1f; } else if (robotobj->type == OBJ_DOOR) { hear.hostile_level = 0.5f; hear.curiosity_level = 1.0f; } else { hear.hostile_level = .9f; hear.curiosity_level = 0.5f; } hear.max_dist = Sounds[Weapons[weapon->id].sounds[WSI_IMPACT_WALL]].max_distance; AINotify(weapon, AIN_HEAR_NOISE, (void *)&hear); } // Do weapon explosion stuff DoWeaponExploded(weapon, collision_normal, collision_point); // Check to see if we should spawn if (robotobj->type == OBJ_DOOR && !(Game_mode & GM_MULTI)) { if ((Weapons[weapon->id].flags & WF_SPAWNS_IMPACT) && Weapons[weapon->id].spawn_count > 0 && Weapons[weapon->id].spawn_handle >= 0) CreateImpactSpawnFromWeapon(weapon, collision_normal); } if ((Weapons[weapon->id].flags & WF_SPAWNS_ROBOT) && (Weapons[weapon->id].flags & WF_COUNTERMEASURE) && Weapons[weapon->id].robot_spawn_handle >= 0) CreateRobotSpawnFromWeapon(weapon); parent_obj = ObjGet(weapon->parent_handle); if ((parent_obj) && (parent_obj->control_type == CT_AI)) { AINotify(parent_obj, AIN_WHIT_OBJECT, (void *)robotobj); } if (robotobj->control_type == CT_AI) { AINotify(robotobj, AIN_HIT_BY_WEAPON, (void *)weapon); } if (electrical) { damage_to_apply = Weapons[weapon->id].generic_damage * Frametime; } else damage_to_apply = Weapons[weapon->id].generic_damage; // Factor in multiplier damage_to_apply *= weapon->ctype.laser_info.multiplier; if (ApplyDamageToGeneric(robotobj, weapon, damage_type, damage_to_apply)) { if (Weapons[weapon->id].sounds[WSI_IMPACT_ROBOT] != SOUND_NONE_INDEX) { Sound_system.Play3dSound(Weapons[weapon->id].sounds[WSI_IMPACT_ROBOT], SND_PRIORITY_HIGHEST, weapon); } if (!electrical) { light_info *li = &Weapons[weapon->id].lighting_info; ushort color = GR_RGB16(li->red_light2 * 255, li->green_light2 * 255, li->blue_light2 * 255); CreateRandomLineSparks(3 + ps_rand() % 6, &weapon->pos, weapon->roomnum, color); } } #if 0 if(Demo_flags == DF_RECORDING) { DemoWriteCollideGenericWeapon( robotobj, weapon, collision_point, collision_normal, f_reverse_normal, hit_info ); } else if(Demo_flags == DF_PLAYBACK) { //During playback we don't need the code below (actually it won't work) return; } #endif if (!electrical) { bump_two_objects(robotobj, weapon, collision_point, collision_normal, 0); if (!f_stick || (hit_info == NULL)) { if ((robotobj->lighting_render_type == LRT_LIGHTMAPS) || !(weapon->mtype.phys_info.flags & PF_PERSISTENT)) SetObjectDeadFlag(weapon); } else { MakeWeaponStick(weapon, robotobj, hit_info); } } } void collide_player_and_weapon(object *playerobj, object *weapon, vector *collision_point, vector *collision_normal, bool f_reverse_normal, fvi_info *hit_info) { object *parent_obj; float damage_to_apply; ubyte electrical = Weapons[weapon->id].flags & WF_ELECTRICAL ? 1 : 0; bool f_stick = ((weapon->mtype.phys_info.flags & PF_STICK) != 0); if (f_reverse_normal) *collision_normal *= -1.0f; parent_obj = ObjGet(weapon->parent_handle); if ((parent_obj) && (parent_obj->control_type == CT_AI)) { AINotify(parent_obj, AIN_WHIT_OBJECT, (void *)playerobj); } // Do weapon explosion stuff DoWeaponExploded(weapon, collision_normal, collision_point, playerobj); // Check to see if we should spawn /*if ((Weapons[weapon->id].flags & WF_SPAWNS_IMPACT) && Weapons[weapon->id].spawn_count>0 && Weapons[weapon->id].spawn_handle>=0) CreateImpactSpawnFromWeapon (weapon,collision_normal);*/ if ((Weapons[weapon->id].flags & WF_SPAWNS_ROBOT) && (Weapons[weapon->id].flags & WF_COUNTERMEASURE) && Weapons[weapon->id].robot_spawn_handle >= 0) CreateRobotSpawnFromWeapon(weapon); if (electrical) damage_to_apply = Weapons[weapon->id].player_damage * Frametime; else damage_to_apply = Weapons[weapon->id].player_damage; // Factor in multiplier damage_to_apply *= weapon->ctype.laser_info.multiplier; int damage_type = electrical ? PD_ENERGY_WEAPON : PD_MATTER_WEAPON; if (ApplyDamageToPlayer(playerobj, weapon, damage_type, damage_to_apply)) { // we were damaged! if (Weapons[weapon->id].sounds[WSI_IMPACT_ROBOT] != SOUND_NONE_INDEX) Sound_system.Play3dSound(Weapons[weapon->id].sounds[WSI_IMPACT_ROBOT], SND_PRIORITY_NORMAL, weapon); } #if 0 if(Demo_flags == DF_RECORDING) { DemoWriteCollidePlayerWeapon( playerobj, weapon, collision_point, collision_normal, f_reverse_normal, hit_info ); } else if(Demo_flags == DF_PLAYBACK) { //During playback we don't need the code below (actually it won't work) return; } #endif if (!electrical) { bump_two_objects(playerobj, weapon, collision_point, collision_normal, 0); if (!f_stick || (hit_info == NULL)) { if (!(weapon->mtype.phys_info.flags & PF_PERSISTENT)) SetObjectDeadFlag(weapon); } else { MakeWeaponStick(weapon, playerobj, hit_info); } } } #define COLLISION_OF(a, b) (((a) << 8) + (b)) #define DO_COLLISION(type1, type2, collision_function) \ case COLLISION_OF((type1), (type2)): \ (collision_function)((A), (B), collision_point, collision_normal, false, hit_info); \ break; \ case COLLISION_OF((type2), (type1)): \ (collision_function)((B), (A), collision_point, collision_normal, true, hit_info); \ break; #define DO_SAME_COLLISION(type1, type2, collision_function) \ case COLLISION_OF((type1), (type1)): \ (collision_function)((A), (B), collision_point, collision_normal, false, hit_info); \ break; #define NO_COLLISION(type1, type2) \ case COLLISION_OF((type1), (type2)): \ case COLLISION_OF((type2), (type1)): \ break; void check_lg_inform(object *A, object *B) { if (A->flags & (OF_INFORM_PLAYER_COLLIDE_TO_LG | OF_INFORM_PLAYER_WEAPON_COLLIDE_TO_LG)) { if (B->type == OBJ_PLAYER) { bool f_pwc = (A->flags & OF_INFORM_PLAYER_WEAPON_COLLIDE_TO_LG) != 0; int type; if (f_pwc) { type = OF_INFORM_PLAYER_WEAPON_COLLIDE_TO_LG; } else { type = OF_INFORM_PLAYER_COLLIDE_TO_LG; } Level_goals.Inform(LIT_OBJECT, type, A->handle); } } if (A->flags & OF_INFORM_PLAYER_WEAPON_COLLIDE_TO_LG) { if (B->type == OBJ_WEAPON) { object *parent = ObjGetUltimateParent(B); if (parent && parent->type == OBJ_PLAYER) { Level_goals.Inform(LIT_OBJECT, OF_INFORM_PLAYER_WEAPON_COLLIDE_TO_LG, A->handle); } } } } void collide_two_objects(object *A, object *B, vector *collision_point, vector *collision_normal, fvi_info *hit_info) { int collision_type; int a_num = A - Objects; int b_num = B - Objects; ubyte a_good = 0, b_good = 0; ubyte a_hittime_good = 1, b_hittime_good = 1; // Only do omega particle collisions if specifically allowed extern bool Enable_omega_collions; if (((A->type == OBJ_WEAPON) && (A->id == OMEGA_INDEX)) || ((B->type == OBJ_WEAPON) && (B->id == OMEGA_INDEX))) if (!Enable_omega_collions) return; ASSERT(CollisionResult[A->type][B->type] != RESULT_NOTHING); collision_type = COLLISION_OF(A->type, B->type); check_lg_inform(A, B); check_lg_inform(B, A); // mprintf( (0, "Object %d of type %d collided with object %d of type %d\n", A-Objects,A->type, B-Objects, B->type )); // COLLISION SCRIPT HOOK if (Game_mode & GM_MULTI) { ASSERT(!(A->flags & OF_DEAD)); ASSERT(!(B->flags & OF_DEAD)); // Make sure we have good scripts to call if (A->type == OBJ_PLAYER || A->type == OBJ_ROBOT || A->type == OBJ_POWERUP || A->type == OBJ_WEAPON || A->type == OBJ_BUILDING || A->type == OBJ_DOOR || A->type == OBJ_CLUTTER) a_good = 1; if (B->type == OBJ_PLAYER || B->type == OBJ_ROBOT || B->type == OBJ_POWERUP || B->type == OBJ_WEAPON || B->type == OBJ_BUILDING || B->type == OBJ_DOOR || B->type == OBJ_CLUTTER) b_good = 1; if (A->type == OBJ_PLAYER && (Players[A->id].flags & (PLAYER_FLAGS_DYING | PLAYER_FLAGS_DEAD))) a_good = 0; if (B->type == OBJ_PLAYER && (Players[B->id].flags & (PLAYER_FLAGS_DYING | PLAYER_FLAGS_DEAD))) b_good = 0; if (A->type == OBJ_POWERUP && A->effect_info->last_object_hit == b_num && (Gametime - A->effect_info->last_object_hit_time) < 1) { a_good = 0; a_hittime_good = 0; } if (B->type == OBJ_POWERUP && B->effect_info->last_object_hit == a_num && (Gametime - B->effect_info->last_object_hit_time) < 1) { b_good = 0; b_hittime_good = 0; } if (a_good && b_good) { DLLInfo.me_handle = A->handle; DLLInfo.it_handle = B->handle; DLLInfo.collide_info.point.x = collision_point->x; DLLInfo.collide_info.point.y = collision_point->y; DLLInfo.collide_info.point.z = collision_point->z; DLLInfo.collide_info.normal.x = collision_normal->x; DLLInfo.collide_info.normal.y = collision_normal->y; DLLInfo.collide_info.normal.z = collision_normal->z; CallGameDLL(EVT_GAMECOLLIDE, &DLLInfo); DLLInfo.me_handle = B->handle; DLLInfo.it_handle = A->handle; CallGameDLL(EVT_GAMECOLLIDE, &DLLInfo); // Update static variables if (A->type == OBJ_POWERUP) { A->effect_info->last_object_hit = b_num; A->effect_info->last_object_hit_time = Gametime; } if (B->type == OBJ_POWERUP) { B->effect_info->last_object_hit = a_num; B->effect_info->last_object_hit_time = Gametime; } } } // Call script only if its ok to int ok_to_call_script = 1; if (A->type == OBJ_PLAYER && (Players[A->id].flags & (PLAYER_FLAGS_DYING | PLAYER_FLAGS_DEAD))) ok_to_call_script = 0; if (B->type == OBJ_PLAYER && (Players[B->id].flags & (PLAYER_FLAGS_DYING | PLAYER_FLAGS_DEAD))) ok_to_call_script = 0; // Check to see if we should call the script if ((Game_mode & GM_MULTI)) { if ((A->type == OBJ_POWERUP || B->type == OBJ_POWERUP) && (a_hittime_good == 0 || b_hittime_good == 0)) ok_to_call_script = 0; } if (ok_to_call_script) { if (!(Game_mode & GM_MULTI)) { ASSERT(!(A->flags & OF_DEAD)); ASSERT(!(B->flags & OF_DEAD)); } tOSIRISEventInfo ei; ei.evt_collide.it_handle = B->handle; Osiris_CallEvent(A, EVT_COLLIDE, &ei); ei.evt_collide.it_handle = A->handle; Osiris_CallEvent(B, EVT_COLLIDE, &ei); } switch (collision_type) { DO_COLLISION(OBJ_PLAYER, OBJ_WEAPON, collide_player_and_weapon) DO_COLLISION(OBJ_PLAYER, OBJ_MARKER, collide_player_and_marker) DO_COLLISION(OBJ_ROBOT, OBJ_PLAYER, collide_generic_and_player) DO_COLLISION(OBJ_BUILDING, OBJ_PLAYER, collide_generic_and_player) DO_COLLISION(OBJ_DOOR, OBJ_PLAYER, collide_generic_and_player) DO_COLLISION(OBJ_ROOM, OBJ_VIEWER, collide_generic_and_player) DO_COLLISION(OBJ_ROOM, OBJ_PLAYER, collide_generic_and_player) DO_COLLISION(OBJ_CLUTTER, OBJ_PLAYER, collide_generic_and_player) DO_COLLISION(OBJ_ROBOT, OBJ_WEAPON, collide_generic_and_weapon) DO_COLLISION(OBJ_CLUTTER, OBJ_WEAPON, collide_generic_and_weapon) DO_COLLISION(OBJ_BUILDING, OBJ_WEAPON, collide_generic_and_weapon) DO_COLLISION(OBJ_ROOM, OBJ_WEAPON, collide_generic_and_weapon) DO_COLLISION(OBJ_DOOR, OBJ_WEAPON, collide_generic_and_weapon) DO_SAME_COLLISION(OBJ_PLAYER, OBJ_PLAYER, collide_player_and_player) // Handled by the script, so no code NO_COLLISION(OBJ_PLAYER, OBJ_POWERUP) NO_COLLISION(OBJ_POWERUP, OBJ_WEAPON) default: bump_two_objects(A, B, collision_point, collision_normal, 1); } if ((A->type == OBJ_PLAYER && B->type == OBJ_POWERUP && (B->flags & OF_DEAD)) || (B->type == OBJ_PLAYER && A->type == OBJ_POWERUP && (A->flags & OF_DEAD))) { ain_hear hear; hear.f_directly_player = true; hear.hostile_level = 0.05f; hear.curiosity_level = 1.0f; hear.max_dist = Sounds[SOUND_POWERUP_PICKUP].max_distance; if (A->type == OBJ_PLAYER) AINotify(A, AIN_HEAR_NOISE, (void *)&hear); else AINotify(B, AIN_HEAR_NOISE, (void *)&hear); } } #define ENABLE_COLLISION_SPHERE_SPHERE(type1, type2) \ CollisionResult[type1][type2] = RESULT_CHECK_SPHERE_SPHERE; \ CollisionResult[type2][type1] = RESULT_CHECK_SPHERE_SPHERE; #define ENABLE_COLLISION_SPHERE_POLY(type1, type2) \ CollisionResult[type1][type2] = RESULT_CHECK_SPHERE_POLY; \ CollisionResult[type2][type1] = RESULT_CHECK_POLY_SPHERE; #define ENABLE_COLLISION_POLY_SPHERE(type1, type2) \ CollisionResult[type1][type2] = RESULT_CHECK_POLY_SPHERE; \ CollisionResult[type2][type1] = RESULT_CHECK_SPHERE_POLY; #define ENABLE_COLLISION_BBOX_POLY(type1, type2) \ CollisionResult[type1][type2] = RESULT_CHECK_BBOX_POLY; \ CollisionResult[type2][type1] = RESULT_CHECK_POLY_BBOX; #define ENABLE_COLLISION_POLY_BBOX(type1, type2) \ CollisionResult[type1][type2] = RESULT_CHECK_POLY_BBOX; \ CollisionResult[type2][type1] = RESULT_CHECK_BBOX_POLY; #define ENABLE_COLLISION_BBOX_BBOX(type1, type2) \ CollisionResult[type1][type2] = RESULT_CHECK_BBOX_BBOX; \ CollisionResult[type2][type1] = RESULT_CHECK_BBOX_BBOX; #define ENABLE_COLLISION_BBOX_SPHERE(type1, type2) \ CollisionResult[type1][type2] = RESULT_CHECK_BBOX_SPHERE; \ CollisionResult[type2][type1] = RESULT_CHECK_SPHERE_BBOX; #define ENABLE_COLLISION_SPHERE_BBOX(type1, type2) \ CollisionResult[type1][type2] = RESULT_CHECK_SPHERE_BBOX; \ CollisionResult[type2][type1] = RESULT_CHECK_BBOX_SPHERE; #define ENABLE_COLLISION_SPHERE_ROOM(type1, type2) \ CollisionResult[type1][type2] = RESULT_CHECK_SPHERE_ROOM; \ CollisionResult[type2][type1] = RESULT_CHECK_SPHERE_ROOM; #define ENABLE_COLLISION_BBOX_ROOM(type1, type2) \ CollisionResult[type1][type2] = RESULT_CHECK_BBOX_ROOM; \ CollisionResult[type2][type1] = RESULT_CHECK_BBOX_ROOM; #define DISABLE_COLLISION(type1, type2) \ CollisionResult[type1][type2] = RESULT_NOTHING; \ CollisionResult[type2][type1] = RESULT_NOTHING; void CollideInit() { int i, j; for (i = 0; i < MAX_OBJECT_TYPES; i++) for (j = 0; j < MAX_OBJECT_TYPES; j++) { CollisionResult[i][j] = RESULT_NOTHING; } for (i = 0; i < MAX_OBJECT_TYPES; i++) { CollisionRayResult[i] = RESULT_NOTHING; } CollisionRayResult[OBJ_ROBOT] = RESULT_CHECK_SPHERE_POLY; CollisionRayResult[OBJ_PLAYER] = RESULT_CHECK_SPHERE_POLY; CollisionRayResult[OBJ_WEAPON] = RESULT_CHECK_SPHERE_POLY; CollisionRayResult[OBJ_POWERUP] = RESULT_CHECK_SPHERE_POLY; CollisionRayResult[OBJ_CLUTTER] = RESULT_CHECK_SPHERE_POLY; CollisionRayResult[OBJ_BUILDING] = RESULT_CHECK_SPHERE_POLY; CollisionRayResult[OBJ_DOOR] = RESULT_CHECK_SPHERE_POLY; CollisionRayResult[OBJ_ROOM] = RESULT_CHECK_SPHERE_POLY; for (i = 0; i < MAX_OBJECT_TYPES; i++) { ENABLE_COLLISION_SPHERE_ROOM(i, OBJ_ROOM) } ENABLE_COLLISION_POLY_SPHERE(OBJ_WALL, OBJ_ROBOT) ENABLE_COLLISION_POLY_SPHERE(OBJ_WALL, OBJ_WEAPON) ENABLE_COLLISION_POLY_SPHERE(OBJ_WALL, OBJ_PLAYER) ENABLE_COLLISION_SPHERE_SPHERE(OBJ_ROBOT, OBJ_ROBOT) // ENABLE_COLLISION_SPHERE_SPHERE( OBJ_BUILDING, OBJ_BUILDING ) ENABLE_COLLISION_POLY_SPHERE(OBJ_PLAYER, OBJ_FIREBALL) ENABLE_COLLISION_SPHERE_SPHERE(OBJ_PLAYER, OBJ_PLAYER) ENABLE_COLLISION_SPHERE_SPHERE(OBJ_PLAYER, OBJ_MARKER) ENABLE_COLLISION_SPHERE_SPHERE(OBJ_MARKER, OBJ_PLAYER) ENABLE_COLLISION_SPHERE_SPHERE(OBJ_WEAPON, OBJ_WEAPON) ENABLE_COLLISION_POLY_SPHERE(OBJ_ROBOT, OBJ_PLAYER) // ENABLE_COLLISION_SPHERE_SPHERE( OBJ_ROBOT, OBJ_PLAYER ) ENABLE_COLLISION_POLY_SPHERE(OBJ_ROBOT, OBJ_WEAPON) ENABLE_COLLISION_POLY_SPHERE(OBJ_PLAYER, OBJ_WEAPON) ENABLE_COLLISION_SPHERE_SPHERE(OBJ_PLAYER, OBJ_POWERUP) ENABLE_COLLISION_SPHERE_SPHERE(OBJ_POWERUP, OBJ_WALL) ENABLE_COLLISION_SPHERE_POLY(OBJ_WEAPON, OBJ_CLUTTER) ENABLE_COLLISION_SPHERE_POLY(OBJ_PLAYER, OBJ_CLUTTER) ENABLE_COLLISION_SPHERE_SPHERE(OBJ_CLUTTER, OBJ_CLUTTER) ENABLE_COLLISION_SPHERE_POLY(OBJ_ROBOT, OBJ_CLUTTER) ENABLE_COLLISION_SPHERE_POLY(OBJ_PLAYER, OBJ_BUILDING) ENABLE_COLLISION_SPHERE_POLY(OBJ_ROBOT, OBJ_BUILDING) ENABLE_COLLISION_SPHERE_POLY(OBJ_WEAPON, OBJ_BUILDING) ENABLE_COLLISION_SPHERE_POLY(OBJ_CLUTTER, OBJ_BUILDING) ENABLE_COLLISION_SPHERE_POLY(OBJ_CLUTTER, OBJ_DOOR) ENABLE_COLLISION_SPHERE_POLY(OBJ_BUILDING, OBJ_DOOR) ENABLE_COLLISION_SPHERE_ROOM(OBJ_PLAYER, OBJ_ROOM) ENABLE_COLLISION_SPHERE_ROOM(OBJ_ROBOT, OBJ_ROOM) ENABLE_COLLISION_SPHERE_ROOM(OBJ_WEAPON, OBJ_ROOM) ENABLE_COLLISION_SPHERE_ROOM(OBJ_VIEWER, OBJ_ROOM) ENABLE_COLLISION_SPHERE_POLY(OBJ_PLAYER, OBJ_DOOR) ENABLE_COLLISION_SPHERE_POLY(OBJ_ROBOT, OBJ_DOOR) ENABLE_COLLISION_SPHERE_POLY(OBJ_WEAPON, OBJ_DOOR) DISABLE_COLLISION(OBJ_POWERUP, OBJ_POWERUP) } // Process a collision between an object and a wall // Returns true if the object hits the wall, and false if should keep going though the wall (for breakable glass) bool collide_object_with_wall(object *A, float hitspeed, int hitseg, int hitwall, vector *hitpt, vector *wall_normal, float hit_dot) { ubyte do_event = 0; bool ret = true; switch (A->type) { case OBJ_NONE: Error("A object of type NONE hit a wall!\n"); break; case OBJ_PLAYER: collide_player_and_wall(A, hitspeed, hitseg, hitwall, hitpt, wall_normal, hit_dot); do_event = 1; break; case OBJ_WEAPON: ret = collide_weapon_and_wall(A, hitspeed, hitseg, hitwall, hitpt, wall_normal, hit_dot); do_event = 1; break; case OBJ_DEBRIS: break; // chrishack -- collide_debris_and_wall(A,hitspeed,hitseg,hitwall,hitpt); break; case OBJ_FIREBALL: break; // collide_fireball_and_wall(A,hitspeed,hitseg,hitwall,hitpt); case OBJ_CLUTTER: case OBJ_BUILDING: case OBJ_ROBOT: collide_generic_and_wall(A, hitspeed, hitseg, hitwall, hitpt, wall_normal, hit_dot); do_event = 1; break; case OBJ_VIEWER: break; // collide_camera_and_wall(A,hitspeed,hitseg,hitwall,hitpt); case OBJ_POWERUP: break; // collide_powerup_and_wall(A,hitspeed,hitseg,hitwall,hitpt); case OBJ_GHOST: break; // do nothing case OBJ_OBSERVER: break; // do nothing case OBJ_SPLINTER: break; case OBJ_MARKER: break; case OBJ_SHARD: if (sound_override_glass_breaking == -1) Sound_system.Play3dSound(SOUND_BREAKING_GLASS, SND_PRIORITY_NORMAL, A, MAX_GAME_VOLUME / 10); else Sound_system.Play3dSound(sound_override_glass_breaking, SND_PRIORITY_NORMAL, A, MAX_GAME_VOLUME / 10); break; default: mprintf((0, "Unhandled collision of object type %d and wall\n", A->type)); // Error( "Unhandled object type hit wall in collide.c\n" ); } if (do_event && (Game_mode & GM_MULTI)) { // call multiplayer event DLLInfo.me_handle = A->handle; DLLInfo.it_handle = OBJECT_HANDLE_NONE; DLLInfo.collide_info.point.x = hitpt->x; DLLInfo.collide_info.point.y = hitpt->y; DLLInfo.collide_info.point.z = hitpt->z; DLLInfo.collide_info.normal.x = wall_normal->x; DLLInfo.collide_info.normal.y = wall_normal->y; DLLInfo.collide_info.normal.z = wall_normal->z; DLLInfo.collide_info.hitspeed = hitspeed; DLLInfo.collide_info.hit_dot = hit_dot; DLLInfo.collide_info.hitseg = hitseg; DLLInfo.collide_info.hitwall = hitwall; CallGameDLL(EVT_GAMEWALLCOLLIDE, &DLLInfo); } return ret; }