mirror of
https://github.com/kevinbentley/Descent3.git
synced 2025-01-23 12:08:56 +00:00
9416458f9c
Remove unused mono debug code.
3517 lines
96 KiB
C++
3517 lines
96 KiB
C++
/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
|
|
--- HISTORICAL COMMENTS FOLLOW ---
|
|
|
|
* $Logfile: /DescentIII/main/object.cpp $
|
|
* $Revision: 315 $
|
|
* $Date: 3/20/00 12:18p $
|
|
* $Author: Matt $
|
|
*
|
|
* Object management functions
|
|
*
|
|
* $Log: /DescentIII/main/object.cpp $
|
|
*
|
|
* 315 3/20/00 12:18p Matt
|
|
* Merge of Duane's post-1.3 changes.
|
|
* Memset ai_frame to zeros after malloc.
|
|
*
|
|
* 314 1/26/00 9:20p Jeff
|
|
* added support for IntelliVIBE DLL
|
|
*
|
|
* 313 10/25/99 9:52a Matt
|
|
* Undid a Mac change (Duane says it's ok)
|
|
*
|
|
* 312 10/23/99 12:51a Matt
|
|
* Fixed compile warning
|
|
*
|
|
* 311 10/22/99 10:44p Jeff
|
|
* mac merge bug fix
|
|
*
|
|
* 310 10/22/99 3:43p Matt
|
|
* Mac merge
|
|
*
|
|
* 309 10/20/99 5:40p Chris
|
|
* Added the Red Guidebot
|
|
*
|
|
* 308 10/08/99 4:24p 3dsmax
|
|
* some ambient sound fix done by MattT
|
|
*
|
|
* 307 9/18/99 9:24p Jeff
|
|
* object position history code (needed for motion blur effect)
|
|
*
|
|
* 306 5/22/99 1:54a Matt
|
|
* Got rid of debug code mistakenly checked in.
|
|
*
|
|
* 305 5/21/99 3:43a Matt
|
|
*
|
|
* 304 5/20/99 9:03p Matt
|
|
* Changed trigger system so that collisions with rendered portals don't
|
|
* set off triggers on the portals.
|
|
*
|
|
* 303 5/20/99 3:00p Jason
|
|
* added support for negative respawn scalars
|
|
*
|
|
* 302 5/13/99 7:18p Jeff
|
|
* fixed unattach bug in delete dead
|
|
*
|
|
* 301 5/10/99 8:29p Jason
|
|
* robots no longer burn on terrain that causes damage
|
|
*
|
|
* 300 5/08/99 4:12p Chris
|
|
* Added AI hearing noises... version 1
|
|
*
|
|
* 299 5/06/99 12:52p Jason
|
|
* fixed set on fire bug
|
|
*
|
|
* 298 5/05/99 5:18p Jason
|
|
* Invul generic objects don't burn on damaging terrain
|
|
*
|
|
* 297 5/05/99 5:10a Matt
|
|
* Fixed a bug with dying & animating objects.
|
|
*
|
|
* 296 4/29/99 4:50p Chris
|
|
* Fixed matcen and Osiris created objects so thier ambient sounds play
|
|
*
|
|
* 295 4/29/99 3:09p Jason
|
|
* fixed a potential bug with object deaths
|
|
*
|
|
* 294 4/27/99 10:25p Jason
|
|
* slowed down afterburner outside just a bit
|
|
*
|
|
* 293 4/27/99 9:13a Matt
|
|
* When clearing a room's secret flag, clear the flag from all the
|
|
* connected rooms.
|
|
*
|
|
* 292 4/26/99 2:30p Jason
|
|
* fixed bug with secret messages
|
|
*
|
|
* 291 4/26/99 2:28p Jason
|
|
* added persistant hud messages
|
|
*
|
|
* 290 4/25/99 5:20p Jason
|
|
* fixed some more coop bugs
|
|
*
|
|
* 289 4/24/99 2:00p Chris
|
|
* Removed the slow terrain cell verify check from ObjMoveOne when Del+Y
|
|
* is in effect
|
|
*
|
|
* 288 4/23/99 7:18p Jason
|
|
* fixed some multiplayer issues with observer mode
|
|
*
|
|
* 287 4/22/99 7:13p Jeff
|
|
* fixed bug deleting ghost object in multiplayer
|
|
*
|
|
* 286 4/22/99 4:19p Chris
|
|
* Checked in with the extended DEL+Y terrain node check stuff
|
|
*
|
|
* 285 4/21/99 3:01p Matt
|
|
* Added a new type for dying objects that have AI, instead of keeping a
|
|
* flag in the dying info.
|
|
*
|
|
* 284 4/21/99 11:05a Kevin
|
|
* new ps_rand and ps_srand to replace rand & srand
|
|
*
|
|
* 283 4/20/99 12:24p Matt
|
|
* Re-revised the ObjInit() system. ObjInit() again does all the
|
|
* initialization stuff, but now it's passed a lot more information so
|
|
* those fields can be set before the rest of the initialization takes
|
|
* place.
|
|
*
|
|
* 282 4/18/99 8:13p Chris
|
|
* Fixed the floating flare problems (where windows where broken out and
|
|
* the flare remained)
|
|
*
|
|
* 281 4/18/99 5:33p Jason
|
|
* fixed object multiplayer problem
|
|
*
|
|
* 280 4/16/99 12:33a Matt
|
|
* Disable Soar on non-Windows systems.
|
|
*
|
|
* 279 4/15/99 1:41a Jeff
|
|
* changes for linux compile
|
|
*
|
|
* 278 4/14/99 3:56a Jeff
|
|
* fixed case mismatch in #includes
|
|
*
|
|
* 277 4/12/99 6:15p Samir
|
|
* Sound priorities pass 1
|
|
*
|
|
* 276 4/12/99 8:18a Chris
|
|
*
|
|
* 275 4/12/99 8:12a Chris
|
|
*
|
|
* 274 4/12/99 8:06a Chris
|
|
* Added an ASSERT to check init code that doesn't call
|
|
* ComputeDefaultSize() for polygon objects
|
|
*
|
|
* 273 4/10/99 5:56p Matt
|
|
* Cleaned up object initialization code, including create object & load
|
|
* object.
|
|
*
|
|
* 272 4/09/99 12:06p Jason
|
|
* made model setup code faster
|
|
*
|
|
* 271 4/05/99 10:54a Matt
|
|
* Added auto-waypoint system
|
|
*
|
|
* 270 4/03/99 4:24p Jason
|
|
* sped up attached vis effects by a large amount
|
|
*
|
|
* 269 3/28/99 5:56p Matt
|
|
* Added sparking effect for objects
|
|
*
|
|
* 268 3/16/99 9:01a Kevin
|
|
* Fixed generic check to also check for doors.
|
|
*
|
|
* 267 3/12/99 7:47p Jeff
|
|
* only call applydamagetogeneric for generic objects
|
|
*
|
|
* 266 3/04/99 6:12p Jason
|
|
* fixed bug with terrain LOD
|
|
*
|
|
* 265 3/01/99 8:34p Matt
|
|
* Made room damage system set the player on fire instead of applying
|
|
* damage directly. This system still needs work, but is ok for now.
|
|
*
|
|
* 264 3/01/99 6:27p Matt
|
|
* Cleaned up handling of the outside-mine flag
|
|
*
|
|
* 263 3/01/99 3:15p Jason
|
|
* fixed multiplayer object deletion bug
|
|
*
|
|
* 262 2/28/99 6:36p Kevin
|
|
* More progress on load/save
|
|
*
|
|
* 261 2/27/99 5:40p Jason
|
|
* fixed bug where clients weren't getting remove messages for timed out
|
|
* powerups
|
|
*
|
|
* 260 2/25/99 11:01a Matt
|
|
* Added new explosion system.
|
|
*
|
|
* 259 2/25/99 10:29a Jason
|
|
* fixed powerup stalling bug in netgames
|
|
*
|
|
* 258 2/24/99 1:02p Jason
|
|
* various fixes for multiplayer
|
|
*
|
|
* 257 2/23/99 7:35p Jeff
|
|
* removed assert/return in unghost if the item is in a player's inventory
|
|
* (needs to be called when sending over objects in a multiplayer game,
|
|
* causes an assert)
|
|
*
|
|
* 256 2/23/99 5:13p Kevin
|
|
* Fixed it so objects with lightmaps will retain their lightmaps when a
|
|
* game is saved & loaded.
|
|
*
|
|
* 255 2/23/99 12:39p Jason
|
|
* added more options for generic objects
|
|
*
|
|
* 254 2/22/99 3:26a Matt
|
|
* Deleted obsolete code
|
|
*
|
|
* 253 2/22/99 1:20a Jeff
|
|
* added support for inventory (simple) in dallas. Moved end-level
|
|
* sequence to use IGC. Add position clipboard stuff for dallas. Fixed
|
|
* some inventory bug with storing object handles
|
|
*
|
|
* 252 2/21/99 4:31p Chris
|
|
* Improving the level goal system... Not done.
|
|
*
|
|
* 251 2/21/99 4:20p Matt
|
|
* Added SoundSource objects (and reformatted parts of the object header
|
|
* files).
|
|
*
|
|
* 250 2/19/99 5:16p Chris
|
|
* Added the child died event
|
|
*
|
|
* 249 2/15/99 11:22a Jason
|
|
* took out client-side prediction
|
|
*
|
|
* 248 2/13/99 12:36a Jeff
|
|
* new object flag. set for when an object is currently in a player's
|
|
* inventory
|
|
*
|
|
* 247 2/12/99 5:59p Jason
|
|
* more permissable server stuff
|
|
*
|
|
* 246 2/12/99 3:38p Jason
|
|
* added client side interpolation
|
|
*
|
|
* 245 2/10/99 2:49p Matt
|
|
* Renamed OBJECT_HANDLE_INVALID to OBJECT_HANDLE_BAD
|
|
*
|
|
* 244 2/10/99 1:47p Matt
|
|
* Changed object handle symbolic constants
|
|
*
|
|
* 243 2/08/99 5:25p Jeff
|
|
* removed all calls to MultiSendRemoveObject, incorportated into
|
|
* SetObjectDeadFlag. Fixes sequencing issues in multiplayer
|
|
*
|
|
* 242 2/07/99 1:20a Jeff
|
|
* added new multiplayer game events EVT_GAMEOBJKILLED and
|
|
* EVT_GAMEOBJDESTROYED
|
|
*
|
|
* 241 2/05/99 1:27p Matt
|
|
* Only do cycled animations for generic objects
|
|
*
|
|
* 240 2/03/99 6:32p Jason
|
|
* more changes for world states
|
|
*
|
|
* 239 2/03/99 5:49p Matt
|
|
* Added room damage system
|
|
*
|
|
* 238 2/02/99 8:43a 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
|
|
*
|
|
* 237 2/01/99 4:17p Jason
|
|
* more changes for multisafe
|
|
*
|
|
* 236 1/31/99 7:26p Matt
|
|
* Renamed a bunch of functions to have HUD capitalized
|
|
*
|
|
* 235 1/29/99 5:22p Jeff
|
|
* localization
|
|
*
|
|
* 234 1/27/99 6:48p Jeff
|
|
* better error handling for ghost/unghost (release build)
|
|
*
|
|
* 233 1/24/99 1:24a Jason
|
|
* fixed chase camera being deleted wrong
|
|
*
|
|
* 232 1/23/99 2:49p Kevin
|
|
* Fixed player movement masking
|
|
*
|
|
* 231 1/22/99 8:53p Jeff
|
|
* added custom-default script overrides
|
|
*
|
|
* 230 1/22/99 7:12p Jason
|
|
* changed terrain speed
|
|
*
|
|
* 229 1/22/99 4:23p Jason
|
|
* sped up afterburner temporarily
|
|
*
|
|
* 228 1/21/99 11:15p Jeff
|
|
* pulled out some structs and defines from header files and moved them
|
|
* into seperate header files so that multiplayer dlls don't require major
|
|
* game headers, just those new headers. Side effect is a shorter build
|
|
* time. Also cleaned up some header file #includes that weren't needed.
|
|
* This affected polymodel.h, object.h, player.h, vecmat.h, room.h,
|
|
* manage.h and multi.h
|
|
*
|
|
* 227 1/21/99 6:50p Jason
|
|
* added test impact mortar
|
|
*
|
|
* 226 1/21/99 3:34p Jason
|
|
* added liquid code
|
|
*
|
|
* 225 1/20/99 8:06p Jeff
|
|
* added members into DLLinfo struct for game change segment events, pass
|
|
* them over on execute dll packets
|
|
*
|
|
* 224 1/20/99 2:13a Chris
|
|
* It is now possible for robots to have special immunities, resistances,
|
|
* and vunerabilities
|
|
*
|
|
* 223 1/19/99 4:22p Matt
|
|
* Added the ability for objects to have their own lighting info,
|
|
* different from the default lighting for that type of object.
|
|
*
|
|
* 222 1/19/99 9:20a Kevin
|
|
* Added afterburner masking
|
|
*
|
|
* 221 1/18/99 6:18p Kevin
|
|
* Added controller masking to DALLAS
|
|
*
|
|
* 220 1/18/99 2:46p Matt
|
|
* Combined flags & flags2 fields in object struct
|
|
*
|
|
* 219 1/18/99 12:01a Jeff
|
|
* fixed EVT_GAMEPLAYERCHANGESEG event (would only get called if the
|
|
* server changed seg) and added EVT_GAMEOBJCHANGESEG
|
|
*
|
|
* 218 1/15/99 7:52p Chris
|
|
* Updated ObjSetPos() to include a f_update_attach_children flag
|
|
*
|
|
* 217 1/15/99 2:53p Jason
|
|
* fixed multiplayer ping bug
|
|
*
|
|
* 216 1/13/99 6:31p Jason
|
|
* added punch to the afterburner
|
|
*
|
|
* 215 1/13/99 2:27p Jeff
|
|
* added an is_dying flag for evt_destroy to determine whether the event
|
|
* is due to level end or because it really is dying
|
|
*
|
|
* 214 1/12/99 11:44p Jason
|
|
* Fixed bug with accelerated ping objects
|
|
*
|
|
* 213 1/08/99 2:56p Samir
|
|
* Ripped out OSIRIS1.
|
|
*
|
|
* 212 1/06/99 7:02p Jeff
|
|
* added a multiplayer event for game controls
|
|
*
|
|
* 211 1/05/99 5:09p Jason
|
|
* added permissable server networking (ala Quake/Unreal) to Descent3
|
|
*
|
|
* 210 12/31/98 7:35p Jeff
|
|
* don't allow players to be Ghosted (turned into OBJ_DUMMY). Also, for
|
|
* ObjDelete[Dead] unghost a ghosted object before killing it
|
|
*
|
|
* 209 12/30/98 12:31p Jeff
|
|
* when freeing object scripts, the type is changed before it's freed in
|
|
* ObjDelete. Also, when destroyed, OBJ_ROBOT becomes OBJ_DEBRIS and
|
|
* Osiris was not handling this.
|
|
*
|
|
* 208 12/28/98 10:16a Jason
|
|
* fixed code that was causing some weapons to disappear
|
|
*
|
|
* 207 12/21/98 11:30p Matt
|
|
* Added names for objects
|
|
*
|
|
* 206 12/17/98 12:08p Jeff
|
|
* first checkin of new implementation of OSIRIS (old OSIRIS no longer
|
|
* works)
|
|
*
|
|
* 205 12/10/98 7:09p Jason
|
|
* added cloak fade
|
|
*
|
|
* 204 12/08/98 4:27p Chris
|
|
* Removed MT_WALKING hack
|
|
*
|
|
* 203 12/01/98 3:35p Matt
|
|
* Got rear view working.
|
|
*
|
|
* 202 12/01/98 3:15p Jason
|
|
* play refueling sound in 3d for multiplayer
|
|
*
|
|
* 201 11/17/98 4:16p Kevin
|
|
* Demo recording system
|
|
*
|
|
* 200 11/13/98 4:25p Jason
|
|
* changes for better weapon effects
|
|
*
|
|
* 199 11/13/98 2:27p Samir
|
|
* took out music code.
|
|
*
|
|
* 198 11/11/98 2:46p Kevin
|
|
* Demo recording system work
|
|
*
|
|
* 197 11/10/98 4:48p Jason
|
|
* sped up afterburner recharge time
|
|
*
|
|
* 196 11/10/98 11:16a Jason
|
|
* fixed some multiplayer bugs with powerups disappearing
|
|
*
|
|
*
|
|
* 195 11/06/98 11:05a Kevin
|
|
* Added code for demo play back
|
|
*
|
|
* 194 11/02/98 6:49p Jason
|
|
* fixed ifdef release problem
|
|
*
|
|
* 193 11/02/98 4:04p Luke
|
|
* Temp. disabled walkers inside
|
|
*
|
|
* 192 10/30/98 4:24p Jason
|
|
* changes for multiplayer
|
|
*
|
|
* 191 10/29/98 5:20p Chris
|
|
* Player ships collide smaller now
|
|
*
|
|
* 190 10/28/98 11:51a Jason
|
|
* fixed some multiplayer problems, plus redid coop a bit to make it
|
|
* cleaner
|
|
*
|
|
* 189 10/21/98 12:13p Jason
|
|
* took out guided in multi
|
|
*
|
|
* 188 10/20/98 5:47p Kevin
|
|
* Gunboy and other fixes
|
|
*
|
|
* 187 10/20/98 1:19p Jason
|
|
* fixed afterburner and Of2_SERVER_OBJECT bug
|
|
*
|
|
* 186 10/20/98 10:50a Sean
|
|
*
|
|
* 185 10/19/98 11:46p Jason
|
|
* changes for multiplayer debug layer
|
|
*
|
|
* 184 10/19/98 10:45p Jason
|
|
* toned down afterburner outside
|
|
*
|
|
* 183 10/19/98 7:51p Kevin
|
|
* performance testing
|
|
*
|
|
* 182 10/19/98 7:18p Matt
|
|
* Added system to support different types of damage to the player and
|
|
* have these different types make different sounds.
|
|
*
|
|
* 181 10/19/98 1:21p Jason
|
|
* made afterburner faster
|
|
*
|
|
* 180 10/18/98 11:07p Matt
|
|
* Made ClearTransientObjects() work.
|
|
*
|
|
* 179 10/17/98 5:50p Jeff
|
|
* hooked in rtperformance (run time performance) library
|
|
*
|
|
* 178 10/16/98 4:18p Chris
|
|
* Fixed the 'flare thing'
|
|
*
|
|
* 177 10/16/98 3:39p Chris
|
|
* Improved the object linking system and AI and physics
|
|
*
|
|
* 176 10/15/98 8:47a Matt
|
|
* Changed _DEBUG/RELEASE inconsistancy with slew
|
|
*
|
|
* 175 10/14/98 1:02p Jason
|
|
* fixed FindSoundName issues
|
|
*
|
|
* 174 10/14/98 12:43a Matt
|
|
* Fixed error handling for when an object can't be initialized.
|
|
*
|
|
* 173 10/13/98 1:08p Chris
|
|
* Greatly improved the AI's use of paths. Improved visability checking
|
|
* algorithm. Probably needs a second pass for further cleanup.
|
|
*
|
|
* 172 10/11/98 10:53p Matt
|
|
* Fixed some zero-length malloc()s
|
|
*
|
|
* 171 10/11/98 2:39p Matt
|
|
* If ObjInit() fails, ObjCreate() now returns an error.
|
|
*
|
|
* 170 10/08/98 4:23p Kevin
|
|
* Changed code to comply with memory library usage. Always use mem_malloc
|
|
* , mem_free and mem_strdup
|
|
*
|
|
* 169 10/06/98 5:46p Kevin
|
|
* Added new configuration for demo
|
|
*
|
|
* 168 10/06/98 2:59p Jason
|
|
* added timedemo function
|
|
*
|
|
* 167 10/06/98 11:27a Jason
|
|
* added new death type for robots
|
|
*
|
|
* 166 10/03/98 8:05p Matt
|
|
* Added asserts
|
|
*
|
|
* 165 9/28/98 6:23p Chris
|
|
* Changed multi_anim to custom_anim
|
|
*
|
|
* 164 9/23/98 2:55p Chris
|
|
* Improved auto-leveling and added newbie leveling
|
|
*
|
|
* 163 9/22/98 6:48p Jason
|
|
* fixed dedicated server problems
|
|
*
|
|
* 162 9/22/98 6:04p Samir
|
|
* ifdef DoAI to 1 if in release version.
|
|
*
|
|
* 161 9/22/98 1:02p Matt
|
|
* Don't set previous room info for an object if the object was moved in
|
|
* the editor.
|
|
*
|
|
* 160 9/22/98 12:40p Matt
|
|
* Cleaned up the object per-frame processing code.
|
|
*
|
|
* 159 9/17/98 3:03p Jason
|
|
* added lightning and invul hit effects
|
|
*
|
|
* 158 9/10/98 12:18p Jason
|
|
* more changes to afterburner/thrust effect
|
|
*
|
|
* 157 9/09/98 7:09p Jason
|
|
* changed afterburner effect for ships
|
|
*
|
|
* 156 9/09/98 4:30p Jason
|
|
* fixed a remaining (and insidious) bug with gunpoint instancing
|
|
*
|
|
* 155 8/31/98 6:12p Luke
|
|
* Moved the multi turret deletion code to somewhere where I can debug
|
|
* things
|
|
*
|
|
* 154 8/24/98 4:48p Jason
|
|
* undid my last rev
|
|
*
|
|
* 153 8/24/98 2:52p Jason
|
|
* fixed instance problem
|
|
*
|
|
* 152 8/19/98 11:30a Jason
|
|
* fixed afterburner problem with low framerates
|
|
*
|
|
* 151 8/17/98 12:10p Chris
|
|
* Fixed MAJOR bug in getting gunpoint positions
|
|
*
|
|
* 150 8/14/98 6:21p Chris
|
|
* Added another safety check for docycledanim
|
|
*
|
|
* 149 8/12/98 6:37p Jeff
|
|
* added functions to ghost an object (make it's type to OBJ_DUMMY) and
|
|
* unghost
|
|
*
|
|
* 148 8/12/98 12:04p Chris
|
|
* Attach system version .5 (still needs more work to be multiplayer
|
|
* friendly)
|
|
*
|
|
* 147 8/07/98 8:25p Chris
|
|
* Improved the attach system
|
|
*
|
|
* 146 8/07/98 6:54p Jeff
|
|
* afterburner powerup effect added
|
|
*
|
|
* 145 8/03/98 4:29p Chris
|
|
* Added additional support for the attach system
|
|
*
|
|
* 144 7/30/98 11:09a Jason
|
|
* added weapons that freeze and deform terrain
|
|
*
|
|
* 143 7/28/98 5:43p Samir
|
|
* added room change hook for music system,
|
|
*
|
|
* 142 7/22/98 3:16p Jason
|
|
* added observer mode
|
|
*
|
|
* 141 7/20/98 10:42a Jason
|
|
* added more player scalars, plus afterburner changes
|
|
*
|
|
* 140 7/16/98 12:51p Mark
|
|
* Fixed a potential bug
|
|
*
|
|
* 139 7/15/98 2:33p Jason
|
|
* added scalar variables for various player skills
|
|
*
|
|
* 138 7/15/98 12:48p Jeff
|
|
* put in calls to handle new multiplayer event...when an object's shields
|
|
* change
|
|
*
|
|
* 137 7/14/98 5:52p Kevin
|
|
* Packet loss measurements and auto pps adjusting
|
|
*
|
|
* 136 7/14/98 3:54p Sean
|
|
* CHRIS -- Fixed a bug in free'ing multi_turret_info on objects that are
|
|
* not polygon models (i.e. they don't have that info and it is union'ed
|
|
* to something else so it is not NULL)
|
|
*
|
|
* 135 7/09/98 7:50p Jeff
|
|
* added ObjGetUltimateParent to find out the original parent of an
|
|
* object, traces the family tree
|
|
*
|
|
* 134 7/09/98 3:30p Chris
|
|
* Fixed communication problem between systems
|
|
*
|
|
* 133 7/09/98 11:34a Chris
|
|
* Turrets are interpolated.
|
|
*
|
|
* 132 7/08/98 11:38a Chris
|
|
* Improved the turret info passing in multiplayer
|
|
*
|
|
* 131 7/07/98 3:26p Chris
|
|
* Added changes for turret updates
|
|
*
|
|
* 130 7/02/98 2:47p Chris
|
|
* Dynamic weapon info is now dynamically allocated
|
|
*
|
|
* 129 7/01/98 4:35p Chris
|
|
* More multiplayer sync issues
|
|
*
|
|
* 128 7/01/98 2:02p Chris
|
|
* Added the sound for animations
|
|
*
|
|
* 127 6/30/98 6:36p Chris
|
|
* Added rev .1 of multiplayer animations - BTW It is totally not done.
|
|
*
|
|
* 126 6/30/98 4:28p Chris
|
|
* Checked in some missing commas
|
|
*
|
|
* 125 5/26/98 7:07p Samir
|
|
* reset script.is_custom to 1 when calling ObjCreate.
|
|
*
|
|
* 124 5/22/98 4:45p Chris
|
|
* Powerups us the correct anim time
|
|
*
|
|
* 123 5/19/98 4:42a Chris
|
|
* Added shockwave's -- enjoy. :)
|
|
*
|
|
* 122 5/15/98 5:41p Jason
|
|
* implemented volume lighting system
|
|
*
|
|
* 121 5/11/98 4:40p Chris
|
|
* AI info is now a malloc'ed pointer
|
|
*
|
|
* 120 5/04/98 6:47p Craig
|
|
* Rooms cannot cycle through anims
|
|
*
|
|
* 119 5/04/98 12:30p Matt
|
|
* ObjCreate() now takes object id as a uint16_t
|
|
*
|
|
* 118 5/04/98 12:28p Matt
|
|
* Added shard objects
|
|
*
|
|
* 117 5/01/98 3:41p Chris
|
|
*
|
|
* 116 5/31/98 3:08p Chris
|
|
* Allowed for death animations
|
|
*
|
|
* 115 4/27/98 1:14p Jason
|
|
* cleaned up afterburner stuff
|
|
*
|
|
* 114 4/23/98 12:44p Jason
|
|
* made AI toggleable to test framerate - this is temporary
|
|
*
|
|
* 113 4/22/98 4:15p Chris
|
|
* Improved DebugBlockPrint
|
|
*
|
|
* 112 4/22/98 3:50p Chris
|
|
* Added DebugBlockPrint
|
|
*
|
|
* 111 4/22/98 2:10p Matt
|
|
* Made FreeAllObjects() free the player object (object 0), since objects
|
|
* now contain malloc'd data and must be freed.
|
|
*
|
|
* 110 4/20/98 6:24p Chris
|
|
* Bulletproofing collision code
|
|
*
|
|
* 109 4/19/98 5:00p Jason
|
|
* added cool napalm effect, plus made object effects dynamically
|
|
* allocated
|
|
*
|
|
* 108 4/17/98 1:59p Jason
|
|
* added cool object effects
|
|
*
|
|
* 107 4/16/98 2:56p Chris
|
|
* Updated buddy support and intergrated scripting and AI
|
|
*
|
|
* 106 4/15/98 8:38p Jason
|
|
* fixed some multiplayer issues
|
|
*
|
|
* 105 4/15/98 12:56p Chris
|
|
* Updated parent_handle support and added buddy bot support
|
|
*
|
|
* 104 4/13/98 2:21p Chris
|
|
* Fixed some collision problems dealing with AABBs and Polymodel paging
|
|
* in.
|
|
*
|
|
* 103 4/13/98 12:42p Jason
|
|
* changed afterburner effect
|
|
*
|
|
* 102 4/10/98 2:42p Jason
|
|
* fixed afterburner blobs to be a stream
|
|
*
|
|
* 101 4/10/98 2:16p Jason
|
|
* fixed guided missile problems
|
|
*
|
|
* 100 4/09/98 5:18p Jason
|
|
* got guided working in multiplayer
|
|
*
|
|
* 99 4/09/98 3:04p Jason
|
|
* temp fix for afterburner running out of fuel
|
|
*
|
|
* 98 4/09/98 2:23p Jason
|
|
* added guided/afterburner stuff
|
|
*
|
|
* 97 4/07/98 1:12p Jason
|
|
* make powerup respawn a variable in multiplayer games
|
|
*
|
|
* 96 4/03/98 11:04a Jason
|
|
* added a multplayer dll hook for when a player changes a room
|
|
*
|
|
* 95 4/01/98 6:23p Jason
|
|
* added a slew of stuff for multiplayer
|
|
*
|
|
* 94 3/31/98 5:56p Jason
|
|
* changes for multiplay
|
|
*
|
|
* 93 3/31/98 4:23p Chris
|
|
* Added a new AIpath system
|
|
*
|
|
* 92 3/20/98 9:34p Jason
|
|
* added SetObjectDeadFlag inlined function
|
|
*
|
|
* 91 3/13/98 5:55p Chris
|
|
* Added the new collision spheres
|
|
*
|
|
* 90 3/12/98 7:30p Chris
|
|
* Added ObjSetOrient
|
|
*
|
|
* 89 2/25/98 3:01p Jason
|
|
* added splinter objects to FreeObjects
|
|
*
|
|
* 88 2/25/98 2:05p Jason
|
|
* did FOV and object visibility changes
|
|
*
|
|
* 87 2/20/98 1:46p Chris
|
|
* JASON: Made dynamic lighting only occur if the object is rendered
|
|
*
|
|
* 86 2/19/98 11:17a Chris
|
|
* Tweaked the BIG_OBJECT system
|
|
*
|
|
* 85 2/17/98 3:46p Matt
|
|
* Revamped weapon system and got sounds for spray and fusion weapons
|
|
* working. Still need to implements getting continuous & cutoff sounds
|
|
* from player ship.
|
|
*
|
|
* 84 2/16/98 2:49p Chris
|
|
* Made the MAX_SUBOBJECTS determine the number of normalized_time values
|
|
* to be processed. No longer a 'literal' problem.
|
|
*
|
|
* 83 2/13/98 6:59p Chris
|
|
* FIxed the normalized_time array from being smaller than the number of
|
|
* subobjects. Also update newstyle_fi.cpp
|
|
*
|
|
* 82 2/12/98 8:48p Matt
|
|
* Changed controls system to keep the reading of the controls seperate
|
|
* from using the results. Got rid of the Controls global.
|
|
*
|
|
* 81 2/11/98 2:04p Jason
|
|
* got spawning weapons working
|
|
*
|
|
* 80 2/09/98 3:20p Matt
|
|
* Changed assert to allow OBJECT_HANDLE_INVALID to be pased to ObjGet()
|
|
*
|
|
* 79 2/05/98 3:00p Matt
|
|
* Made ObjSetPos() take optional orienation. Got rid of unlink & relink
|
|
* funcs from header.
|
|
*
|
|
* 78 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.
|
|
*
|
|
* 77 2/03/98 1:06p Jason
|
|
* tidied up some multiplayer loose ends
|
|
*
|
|
* 76 1/30/98 5:26p Chris
|
|
* Allow all non-AI objects to animate
|
|
*
|
|
* 75 1/29/98 5:49p Matt
|
|
* Changed old camera object type to be viewer object (for editor), and
|
|
* now camera objects will just be for game cameras.
|
|
*
|
|
* 74 1/27/98 12:26p Samir
|
|
* Added GetObjectPointInWorld.
|
|
*
|
|
* 73 1/23/98 11:21a Jason
|
|
* incremental multiplayer checkin
|
|
*
|
|
* 72 1/21/98 1:11p Jason
|
|
* incremental checkin for multiplayer
|
|
*
|
|
* 71 1/20/98 6:01p Jason
|
|
* first pass at getting multiplayer deaths working
|
|
*
|
|
* 70 1/20/98 4:12p Samir
|
|
* New script housekeeping system.
|
|
*
|
|
* 69 1/20/98 12:27p Luke
|
|
* Added an assert to make sure that we are not linking to an external
|
|
* room
|
|
*
|
|
* 68 1/20/98 12:10p Jason
|
|
* implemented vis effect system
|
|
*
|
|
* 67 1/19/98 2:44p Samir
|
|
* Use one script per object and started parameter passing support.
|
|
*
|
|
* 66 1/19/98 12:38p Jason
|
|
* took out mprintf in ObjCreate
|
|
*
|
|
* 65 1/19/98 10:04a Matt
|
|
* Added new object handle system
|
|
*
|
|
* 64 1/15/98 6:49p Jason
|
|
* incremental checkin for multiplayer
|
|
*
|
|
* 63 12/30/97 5:45p Jason
|
|
* added toggleable fog
|
|
*
|
|
* 62 12/19/97 2:46p Jason
|
|
* implemented bitmap paging routines
|
|
*
|
|
* 61 12/12/97 3:42p Jason
|
|
* free all objects on shutdown
|
|
*
|
|
* 60 12/01/97 9:56a Chris
|
|
* Removed FLARE and CNTRL_CENTER types
|
|
*
|
|
* 59 11/10/97 12:27p Jason
|
|
* added objects live mprintf
|
|
*
|
|
* 58 10/28/97 12:22p Chris
|
|
* Added a movement type of walking
|
|
*
|
|
* 57 10/23/97 5:36p Jason
|
|
* added splinter objects (probably temporary)
|
|
*
|
|
* 56 10/22/97 5:14p Chris
|
|
* Allow for non-AI objects to animate
|
|
*
|
|
* 55 10/20/97 4:46p Jason
|
|
* changes for explosions
|
|
*
|
|
* 54 10/14/97 6:53p Chris
|
|
* Added the initial SOAR support
|
|
*
|
|
* 53 10/08/97 6:35p Samir
|
|
* Dying player ships get marked as destroyed. When death sequence is
|
|
* over, they are marked as dead. This is needed to better control
|
|
* movement of the dying ship.
|
|
*
|
|
* 52 10/03/97 5:18p Chris
|
|
* Added OBJ_LINE as a new type
|
|
*
|
|
* 51 10/02/97 1:04p Jason
|
|
* implemented FreeAllObjects
|
|
*
|
|
* 50 10/01/97 7:51p Matt
|
|
* Added code for external rooms
|
|
*
|
|
* 49 9/30/97 6:40p Jason
|
|
* got lightmap stuff sort of working with objects
|
|
*
|
|
* 48 9/22/97 6:20p Matt
|
|
* Killed include of stub.h, and removed references to stuff that had been
|
|
* in stub.h
|
|
*
|
|
* 47 9/19/97 6:58p Chris
|
|
* Fixed some bugs with the big object system and removed some annoying
|
|
* mprintf's
|
|
*
|
|
* 46 9/19/97 1:01p Chris
|
|
* Better big object support
|
|
*
|
|
* 45 9/18/97 1:25p Matt
|
|
* Cleaned up code, added the text name for door objects, and other small
|
|
* changes.
|
|
*
|
|
* 44 9/17/97 11:16a Matt
|
|
* Ripped out segment code
|
|
*
|
|
* 43 9/16/97 5:04p Matt
|
|
* Changed conditional for debug code
|
|
*
|
|
* 42 9/15/97 12:42p Samir
|
|
* Added include of stdlib.
|
|
*
|
|
* 41 9/15/97 5:21a Chris
|
|
* Added sphere2poly support
|
|
*
|
|
* 40 9/12/97 6:36p Chris
|
|
* Added collision terrain segment support
|
|
* Added some LARGE OBJECT collision support
|
|
*
|
|
* 39 9/12/97 10:12a Matt
|
|
* Added trigger check
|
|
*
|
|
* 38 9/05/97 3:22p Jason
|
|
* changes for lit objects
|
|
*
|
|
* 37 9/05/97 1:29p Jason
|
|
* revamped generic object lighting
|
|
*
|
|
* 36 9/05/97 12:26p Samir
|
|
* Player initiates death sequence if killed.
|
|
*
|
|
* 35 9/04/97 5:26p Jason
|
|
* got pulsing objects to work
|
|
*
|
|
* 34 9/04/97 12:11p Jason
|
|
* added flickering generic objects
|
|
*
|
|
* 33 9/03/97 6:17p Jason
|
|
* added code to support powerups/robots/etc that cast light
|
|
*
|
|
* 32 9/03/97 3:53p Jason
|
|
* got objects to cast light
|
|
*
|
|
* 31 8/25/97 7:32p Chris
|
|
*
|
|
* 30 8/25/97 6:18p Chris
|
|
*
|
|
* 29 8/25/97 5:14p Craig
|
|
*
|
|
* 28 8/25/97 12:41p Chris
|
|
* Made sure that all object movements and mine changes result in AABB
|
|
* updates.
|
|
*
|
|
* 27 8/22/97 10:51a Samir
|
|
* call destroy script if object is being killed.
|
|
*
|
|
* 26 8/21/97 7:47p Matt
|
|
* ObjSetPos() wasn't relinking the object when the room changed
|
|
*
|
|
* 25 8/21/97 5:57p Samir
|
|
* Added code that kills scripts when object is being deleted.
|
|
*
|
|
* 24 8/20/97 5:04p Samir
|
|
* Added frame interval scripting.
|
|
*
|
|
* 23 8/18/97 1:45a Chris
|
|
* Fix some bugs.
|
|
*
|
|
* 22 8/12/97 3:56p Matt
|
|
* Added new object type: OBJ_BUILDING
|
|
*
|
|
* 21 8/12/97 1:13p Chris
|
|
* Added AABBs.
|
|
*
|
|
* 20 8/11/97 6:48p Matt
|
|
* Rewrote static object remap system
|
|
*
|
|
* 19 8/11/97 1:53p Matt
|
|
* Ripped out robot & powerup pages, and added generic page
|
|
*
|
|
* 18 8/04/97 5:29p Matt
|
|
* Removed some not-to-be-used room code
|
|
*
|
|
* 17 8/04/97 4:08p Matt
|
|
* Added ObjSetPos() that takes the new room number
|
|
*
|
|
* 16 8/04/97 12:37p Chris
|
|
* We now support fvi in Rooms :)
|
|
*
|
|
* 15 7/29/97 10:27a Jason
|
|
* added RenderOBject abstraction layer
|
|
*
|
|
* 14 7/28/97 1:14p Chris
|
|
* Added support for sub-object visability. Plus, debris.
|
|
*
|
|
* 13 7/16/97 4:09p Jason
|
|
* ripped out temporary object lighting
|
|
*
|
|
*
|
|
* 12 7/16/97 3:43p Jason
|
|
* took off lighting of objects
|
|
*
|
|
* 11 7/15/97 5:32p Jason
|
|
* got simple static lighting working with objects
|
|
*
|
|
* 10 7/15/97 4:59p Chris
|
|
*
|
|
* 9 7/15/97 4:59p Chris
|
|
* added support for AI and animations
|
|
*
|
|
* 50 6/24/97 6:54p Mark
|
|
*
|
|
* 49 6/24/97 6:07p Matt
|
|
* Use DrawPolygonModel() directly, instead of DrawPolygonObject()
|
|
*
|
|
* 48 6/06/97 4:07p Jason
|
|
* verify tmap2 textures if in editor mode
|
|
*
|
|
* 47 5/16/97 2:17p Samir
|
|
* ResetPlayerObject added.
|
|
*
|
|
* 46 5/15/97 6:09p Chris
|
|
*
|
|
* 45 5/14/97 3:41p Chris
|
|
*
|
|
* 44 5/14/97 12:32p Matt
|
|
* Took out timer stuff for refuel sound; instead, we set the execlusive
|
|
* flag for the sound itself.
|
|
*
|
|
* 43 5/13/97 10:31p Matt
|
|
* Added code for refueling centers.
|
|
* Dead objects now get deleted right after the simulation is done.
|
|
*
|
|
* 42 4/30/97 5:43p Jason
|
|
* remap polymodels when pagefile is done loading
|
|
*
|
|
* 41 4/24/97 5:42p Jason
|
|
* got fireball vclips working
|
|
*
|
|
* 40 4/17/97 2:48p Samir
|
|
* ReadFlyingControls renamed from read_flying_controls to match naming
|
|
* convention rules.
|
|
*
|
|
* 39 4/14/97 4:23p Jason
|
|
* made objects work with DrawWeaponObject
|
|
*
|
|
* 38 4/14/97 3:48p Jason
|
|
* changed RT_LASER to RT_WEAPON
|
|
*
|
|
* 37 4/04/97 4:33p Chris
|
|
*
|
|
* 36 4/04/97 2:45p Matt
|
|
* Removed prototypes for deleted functions
|
|
*
|
|
* 35 4/02/97 3:44p Matt
|
|
* Find a valid Ships[] entry, instead of assuming that Ships[0] is valid.
|
|
*
|
|
* 34 4/02/97 2:26p Jason
|
|
* made RemapEverything actually call every remap function
|
|
*
|
|
* 33 4/01/97 10:49p Matt
|
|
* Get player ship model num from Ship page
|
|
*
|
|
* 32 3/27/97 10:45a Chris
|
|
* Set player mass and drag info
|
|
*
|
|
* 31 3/26/97 3:29p Jason
|
|
* made robots work/render
|
|
*
|
|
* 30 3/26/97 3:02p Chris
|
|
* Fixed a bug in ObjMoveAll... It only moved every other object.
|
|
*
|
|
* 29 3/24/97 4:11p Chris
|
|
* Fixed a bug by the TerrainMaster... It seems that !obj->flags &
|
|
* OF_OVER_TERRAIN is not the same as !(obj->flags & OF_OVER_TERRAIN) and
|
|
* it isn't and shouldn't be. :)
|
|
*
|
|
* 28 3/21/97 5:01p Jason
|
|
* incremental terrain improvments
|
|
*
|
|
*
|
|
* 27 3/20/97 5:45p Jason
|
|
* made object work with and render on terrain
|
|
*
|
|
* 26 3/17/97 12:11p Chris
|
|
*
|
|
* 25 3/17/97 11:56a Chris
|
|
* Forced player object to have valid mass and drag. Will fix this
|
|
* correctly at a later date.
|
|
*
|
|
* 24 3/14/97 12:18p Chris
|
|
* Tweak physics and remove braking for now.
|
|
*
|
|
* 23 3/12/97 6:31p Chris
|
|
* Added player flight controls while in game
|
|
*
|
|
* 22 3/05/97 3:09p Jason
|
|
* added RemapDoors to RemapEverything
|
|
*
|
|
* 21 3/5/97 1:40 PM Jeremy
|
|
* only #include editor headers when EDITOR is defined
|
|
*
|
|
* 20 2/28/97 2:37p Matt
|
|
* Draw wireframe box around editor's current object
|
|
*
|
|
* 19 2/27/97 5:10p Samir
|
|
* Added crude trigger checking while flying.
|
|
*
|
|
* 18 2/18/97 6:31p Matt
|
|
* Changed Frame_time to Frametime, & added Gametime
|
|
*
|
|
* 17 2/13/97 12:16p Jason
|
|
* changes for powerup remapping
|
|
*
|
|
* 16 2/12/97 5:37p Jason
|
|
* changed GamePowerups to Powerups to keep from
|
|
* getting lynched
|
|
*
|
|
* 15 2/11/97 6:51p Matt
|
|
* Ripped out some old stuff & renamed a function
|
|
*
|
|
* 14 2/10/97 5:39p Matt
|
|
* Fixed to compile with EDITOR defined
|
|
*
|
|
* 13 2/07/97 5:48p Matt
|
|
* Renamed vector.h to vecmat.h to fix DevStudio problem
|
|
*
|
|
* $NoKeywords: $
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h> // for memset
|
|
#include <stdio.h>
|
|
|
|
#include "object.h"
|
|
|
|
#include "pserror.h"
|
|
#include "vecmat.h"
|
|
#include "mono.h"
|
|
|
|
#include "descent.h"
|
|
#include "player.h"
|
|
#include "slew.h"
|
|
#include "game.h"
|
|
#include "trigger.h"
|
|
#include "PHYSICS.H"
|
|
#include "collide.h"
|
|
#include "door.h"
|
|
#include "controls.h"
|
|
#include "terrain.h"
|
|
#include "polymodel.h"
|
|
#include "gametexture.h"
|
|
#include "ship.h"
|
|
#include "soundload.h"
|
|
#include "weapon.h"
|
|
#include "objinit.h"
|
|
#include "fireball.h"
|
|
#include "hlsoundlib.h"
|
|
#include "sounds.h"
|
|
#include "AIMain.h"
|
|
#include "room.h"
|
|
#include "objinfo.h"
|
|
#include "lighting.h"
|
|
#include "findintersection.h"
|
|
#include "lightmap_info.h"
|
|
#include "object_lighting.h"
|
|
#include "soar.h"
|
|
#include "splinter.h"
|
|
#include "ObjScript.h"
|
|
#include "viseffect.h"
|
|
#include "multi.h"
|
|
#include "game2dll.h"
|
|
#include "robot.h"
|
|
#include "damage.h"
|
|
#include "attach.h"
|
|
#include "dedicated_server.h"
|
|
#include "hud.h"
|
|
#include "demofile.h"
|
|
#include "rtperformance.h"
|
|
#include "osiris_dll.h"
|
|
#include "gameloop.h"
|
|
#include "mem.h"
|
|
#include "stringtable.h"
|
|
#include "levelgoal.h"
|
|
#include "psrand.h"
|
|
#include "vibeinterface.h"
|
|
|
|
#ifdef EDITOR
|
|
#include "editor\d3edit.h"
|
|
#endif
|
|
|
|
#include <algorithm>
|
|
|
|
/*
|
|
* Global variables
|
|
*/
|
|
|
|
object *Player_object = NULL; // the object that is the player
|
|
object *Viewer_object = NULL; // which object we are seeing from
|
|
|
|
static int16_t free_obj_list[MAX_OBJECTS];
|
|
|
|
// Data for objects
|
|
|
|
// -- Object stuff
|
|
|
|
// info on the various types of objects
|
|
|
|
object Objects[MAX_OBJECTS];
|
|
|
|
tPosHistory Object_position_samples[MAX_OBJECT_POS_HISTORY];
|
|
uint8_t Object_position_head;
|
|
int16_t Object_map_position_history[MAX_OBJECTS];
|
|
int16_t Object_map_position_free_slots[MAX_OBJECT_POS_HISTORY];
|
|
uint16_t Num_free_object_position_history;
|
|
|
|
int Num_objects = 0;
|
|
int Highest_object_index = 0;
|
|
int Highest_ever_object_index = 0;
|
|
|
|
int print_object_info = 0;
|
|
|
|
#ifdef EDITOR
|
|
// This array matches the object types in object.h
|
|
char *Object_type_names[MAX_OBJECT_TYPES] = {
|
|
"WALL", // OBJ_WALL 0
|
|
"FIREBALL", // OBJ_FIREBALL 1
|
|
"ROBOT", // OBJ_ROBOT 2
|
|
"SHARD", // OBJ_SHARD 3
|
|
"PLAYER", // OBJ_PLAYER 4
|
|
"WEAPON", // OBJ_WEAPON 5
|
|
"VIEWER", // OBJ_VIEWER 6
|
|
"POWERUP", // OBJ_POWERUP 7
|
|
"DEBRIS", // OBJ_DEBRIS 8
|
|
"CAMERA", // OBJ_CAMERA 9
|
|
"SHOCKWV", // OBJ_SHOCKWAVE 10
|
|
"CLUTTER", // OBJ_CLUTTER 11
|
|
"GHOST", // OBJ_GHOST 12
|
|
"LIGHT", // OBJ_LIGHT 13
|
|
"COOP", // OBJ_COOP 14
|
|
"UNUSED", // OBJ_MARKER 15
|
|
"BUILDING", // OBJ_BUILDING 16
|
|
"DOOR", // OBJ_DOOR 17
|
|
"ROOM", // OBJ_ROOM 18
|
|
"LINE", // OBJ_PARTICLE 19
|
|
"SPLINTER", // OBJ_SPLINTER 20
|
|
"DUMMY", // OBJ_DUMMY 21
|
|
"OBSERVER", // OBJ_OBSERVER 22
|
|
"DEBUG LINE", // OBJ_DEBUG_LINE 23
|
|
"SOUNDSOURCE", // OBJ_SOUNDSOURCE 24
|
|
"WAYPOINT", // OBJ_WAYPOINT 25
|
|
};
|
|
#endif
|
|
|
|
int Num_big_objects = 0;
|
|
int16_t BigObjectList[MAX_BIG_OBJECTS]; // DAJ_MR utb int
|
|
|
|
/*
|
|
* Local Function Prototypes
|
|
*/
|
|
|
|
// Do refueling centers & damage rooms
|
|
void DoSpecialPlayerStuff(object *obj);
|
|
|
|
/*
|
|
* Functions
|
|
*/
|
|
|
|
#ifndef RELEASE
|
|
// set viewer object to next object in array
|
|
void ObjGotoNextViewer() {
|
|
int i, start_obj = 0;
|
|
|
|
start_obj = Viewer_object - Objects; // get viewer object number
|
|
|
|
for (i = 0; i <= Highest_object_index; i++) {
|
|
|
|
start_obj++;
|
|
if (start_obj > Highest_object_index)
|
|
start_obj = 0;
|
|
|
|
if (Objects[start_obj].type != OBJ_NONE) {
|
|
Viewer_object = &Objects[start_obj];
|
|
return;
|
|
}
|
|
}
|
|
|
|
Error("Couldn't find a viewer object!");
|
|
}
|
|
|
|
// set viewer object to next object in array
|
|
void obj_goto_prev_viewer() {
|
|
int i, start_obj = 0;
|
|
|
|
start_obj = Viewer_object - Objects; // get viewer object number
|
|
|
|
for (i = 0; i <= Highest_object_index; i++) {
|
|
|
|
start_obj--;
|
|
if (start_obj < 0)
|
|
start_obj = Highest_object_index;
|
|
|
|
if (Objects[start_obj].type != OBJ_NONE) {
|
|
Viewer_object = &Objects[start_obj];
|
|
return;
|
|
}
|
|
}
|
|
|
|
Error("Couldn't find a viewer object!");
|
|
}
|
|
#endif
|
|
|
|
object *obj_find_first_of_type(int type) {
|
|
int i;
|
|
|
|
for (i = 0; i <= Highest_object_index; i++)
|
|
if (Objects[i].type == type)
|
|
return (&Objects[i]);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int obj_return_num_of_type(int type) {
|
|
int i, count = 0;
|
|
|
|
for (i = 0; i <= Highest_object_index; i++)
|
|
if (Objects[i].type == type)
|
|
count++;
|
|
|
|
return (count);
|
|
}
|
|
|
|
int obj_return_num_of_typeid(int type, int id) {
|
|
int i, count = 0;
|
|
|
|
for (i = 0; i <= Highest_object_index; i++)
|
|
if (Objects[i].type == type && Objects[i].id == id)
|
|
count++;
|
|
|
|
return (count);
|
|
}
|
|
|
|
int ObjAllocate(void);
|
|
|
|
// Creates the player object in the center of the given room
|
|
void CreatePlayerObject(int roomnum) {
|
|
int objnum;
|
|
vector pos;
|
|
|
|
ComputeRoomCenter(&pos, &Rooms[roomnum]);
|
|
|
|
objnum = ObjCreate(OBJ_PLAYER, 0, roomnum, &pos, NULL);
|
|
|
|
ASSERT(objnum == 0); // player must be object 0
|
|
|
|
Player_object = Viewer_object = &Objects[objnum];
|
|
}
|
|
|
|
void InitBigObjects() { Num_big_objects = 0; }
|
|
|
|
void BigObjAdd(int objnum) {
|
|
ASSERT(Num_big_objects < MAX_BIG_OBJECTS);
|
|
if (Num_big_objects >= MAX_BIG_OBJECTS) {
|
|
Int3();
|
|
return;
|
|
}
|
|
|
|
Objects[objnum].flags |= OF_BIG_OBJECT;
|
|
BigObjectList[Num_big_objects++] = objnum;
|
|
}
|
|
|
|
void BigObjRemove(int objnum) {
|
|
int i;
|
|
|
|
Objects[objnum].flags &= (~OF_BIG_OBJECT);
|
|
|
|
for (i = 0; i < Num_big_objects; i++) {
|
|
if (BigObjectList[i] == objnum) {
|
|
Num_big_objects--;
|
|
break;
|
|
}
|
|
}
|
|
|
|
while (i < Num_big_objects) {
|
|
BigObjectList[i] = BigObjectList[i + 1];
|
|
i++;
|
|
}
|
|
|
|
ASSERT(Num_big_objects < MAX_BIG_OBJECTS);
|
|
ASSERT(Num_big_objects >= 0);
|
|
}
|
|
|
|
// Resets the object list: sets all objects to unused, intializes handles, & sets roomnums to -1
|
|
// Called by the editor to init a new level.
|
|
void ResetObjectList() {
|
|
int i;
|
|
|
|
// Init data for each object
|
|
for (i = 0; i < MAX_OBJECTS; i++) {
|
|
Objects[i].handle = i;
|
|
Objects[i].type = OBJ_NONE;
|
|
Objects[i].roomnum = -1;
|
|
}
|
|
|
|
// Build the free object list
|
|
ResetFreeObjects();
|
|
|
|
// Say no big objects
|
|
InitBigObjects();
|
|
|
|
ObjResetPositionHistory();
|
|
}
|
|
|
|
// sets up the free list & init player & whatever else
|
|
void InitObjects() {
|
|
int i;
|
|
|
|
// Initialize the collision system
|
|
CollideInit();
|
|
|
|
// Mark all the objects as unused
|
|
ResetObjectList();
|
|
|
|
// Make sure no rooms are using any objects
|
|
for (i = 0; i < MAX_ROOMS; i++) {
|
|
Rooms[i].objects = -1;
|
|
Rooms[i].vis_effects = -1;
|
|
}
|
|
|
|
InitVisEffects();
|
|
|
|
atexit(FreeAllObjects);
|
|
}
|
|
|
|
// link the object into the list for its room
|
|
void ObjLink(int objnum, int roomnum) {
|
|
object *obj = &Objects[objnum];
|
|
|
|
ASSERT(objnum != -1);
|
|
|
|
ASSERT(obj->roomnum == -1);
|
|
ASSERT(!(obj->flags & OF_BIG_OBJECT));
|
|
|
|
if ((obj->size >= MIN_BIG_OBJ_RAD) && (!ROOMNUM_OUTSIDE(roomnum))) {
|
|
BigObjAdd(objnum);
|
|
}
|
|
|
|
obj->roomnum = roomnum;
|
|
|
|
if (ROOMNUM_OUTSIDE(roomnum)) {
|
|
int cellnum = CELLNUM(roomnum);
|
|
|
|
obj->next = Terrain_seg[cellnum].objects;
|
|
Terrain_seg[cellnum].objects = objnum;
|
|
|
|
ASSERT(obj->next != objnum);
|
|
} else {
|
|
ASSERT(roomnum >= 0 && roomnum <= Highest_room_index);
|
|
ASSERT(!(Rooms[roomnum].flags & RF_EXTERNAL));
|
|
|
|
obj->next = Rooms[roomnum].objects;
|
|
Rooms[roomnum].objects = objnum;
|
|
ASSERT(obj->next != objnum);
|
|
}
|
|
|
|
obj->prev = -1;
|
|
|
|
if (obj->next != -1)
|
|
Objects[obj->next].prev = objnum;
|
|
|
|
ASSERT(Objects[0].next != 0);
|
|
if (Objects[0].next == 0)
|
|
Objects[0].next = -1;
|
|
|
|
ASSERT(Objects[0].prev != 0);
|
|
if (Objects[0].prev == 0)
|
|
Objects[0].prev = -1;
|
|
}
|
|
|
|
void ObjUnlink(int objnum) {
|
|
object *obj = &Objects[objnum];
|
|
|
|
ASSERT(objnum != -1);
|
|
|
|
// If object is already unlinked, do nothing
|
|
if (obj->roomnum == -1) {
|
|
return;
|
|
}
|
|
|
|
if (obj->flags & OF_BIG_OBJECT) {
|
|
BigObjRemove(objnum);
|
|
}
|
|
|
|
if (OBJECT_OUTSIDE(obj)) {
|
|
int cellnum = CELLNUM(obj->roomnum);
|
|
|
|
terrain_segment *seg = &Terrain_seg[cellnum];
|
|
|
|
if (obj->prev == -1)
|
|
seg->objects = obj->next;
|
|
else
|
|
Objects[obj->prev].next = obj->next;
|
|
|
|
if (obj->next != -1)
|
|
Objects[obj->next].prev = obj->prev;
|
|
} else {
|
|
room *rp = &Rooms[obj->roomnum];
|
|
|
|
if (obj->prev == -1)
|
|
rp->objects = obj->next;
|
|
else
|
|
Objects[obj->prev].next = obj->next;
|
|
|
|
if (obj->next != -1)
|
|
Objects[obj->next].prev = obj->prev;
|
|
}
|
|
|
|
// Mark as not linked
|
|
obj->roomnum = -1;
|
|
|
|
ASSERT(Objects[0].next != 0);
|
|
ASSERT(Objects[0].prev != 0);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Scan the object list, freeing down to num_used objects
|
|
// Returns number of slots freed.
|
|
int FreeObjectSlots(int num_used) {
|
|
int i, olind;
|
|
int obj_list[MAX_OBJECTS];
|
|
int num_already_free, num_to_free, original_num_to_free;
|
|
|
|
olind = 0;
|
|
num_already_free = MAX_OBJECTS - Highest_object_index - 1;
|
|
|
|
if (MAX_OBJECTS - num_already_free < num_used) {
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i <= Highest_object_index; i++) {
|
|
if (Objects[i].flags & OF_DEAD) {
|
|
num_already_free++;
|
|
if (MAX_OBJECTS - num_already_free < num_used)
|
|
return num_already_free;
|
|
} else {
|
|
switch (Objects[i].type) {
|
|
case OBJ_NONE:
|
|
num_already_free++;
|
|
if (MAX_OBJECTS - num_already_free < num_used)
|
|
return 0;
|
|
break;
|
|
case OBJ_WALL:
|
|
Int3(); // This is curious. What is an object that is a wall?
|
|
break;
|
|
case OBJ_FIREBALL:
|
|
case OBJ_WEAPON:
|
|
case OBJ_DEBRIS:
|
|
case OBJ_SPLINTER:
|
|
obj_list[olind++] = i;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
num_to_free = MAX_OBJECTS - num_used - num_already_free;
|
|
original_num_to_free = num_to_free;
|
|
|
|
if (num_to_free > olind) {
|
|
mprintf(1, "Warning: Asked to free %i objects, but can only free %i.\n", num_to_free, olind);
|
|
num_to_free = olind;
|
|
}
|
|
|
|
for (i = 0; i < num_to_free; i++)
|
|
if (Objects[obj_list[i]].type == OBJ_DEBRIS) {
|
|
num_to_free--;
|
|
mprintf(0, "Freeing DEBRIS object %3i\n", obj_list[i]);
|
|
SetObjectDeadFlag(&Objects[obj_list[i]]);
|
|
}
|
|
|
|
if (!num_to_free)
|
|
return original_num_to_free;
|
|
|
|
if (!num_to_free)
|
|
return original_num_to_free;
|
|
|
|
//@@for (i=0; i<num_to_free; i++)
|
|
//@@ if ((Objects[obj_list[i]].type == OBJ_WEAPON)&& (Objects[obj_list[i]].id == FLARE_ID)) {
|
|
//@@ num_to_free--;
|
|
//@@ Objects[obj_list[i]].flags |= OF_DEAD;
|
|
//@@ }
|
|
|
|
if (!num_to_free)
|
|
return original_num_to_free;
|
|
|
|
for (i = 0; i < num_to_free; i++)
|
|
if (Objects[obj_list[i]].type == OBJ_WEAPON) {
|
|
num_to_free--;
|
|
mprintf(0, "Freeing WEAPON object %3i\n", obj_list[i]);
|
|
SetObjectDeadFlag(&Objects[obj_list[i]]);
|
|
}
|
|
|
|
return original_num_to_free - num_to_free;
|
|
}
|
|
|
|
// returns the number of a free object, updating Highest_object_index.
|
|
// Generally, ObjCreate() should be called to get an object, since it
|
|
// fills in important fields and does the linking.
|
|
// returns -1 if no free objects
|
|
int ObjAllocate(void) {
|
|
int objnum;
|
|
|
|
if (Num_objects >= MAX_OBJECTS - 2) {
|
|
int num_freed;
|
|
|
|
num_freed = FreeObjectSlots(MAX_OBJECTS - 10);
|
|
mprintf(0, " *** Freed %i objects in frame %i\n", num_freed, FrameCount);
|
|
}
|
|
|
|
if (Num_objects >= MAX_OBJECTS) {
|
|
mprintf(1, "Object creation failed - too many objects!\n");
|
|
return -1;
|
|
}
|
|
|
|
objnum = free_obj_list[Num_objects++];
|
|
|
|
if (objnum > Highest_object_index) {
|
|
Highest_object_index = objnum;
|
|
if (Highest_object_index > Highest_ever_object_index) {
|
|
Highest_ever_object_index = Highest_object_index;
|
|
mprintf(0, "Highest ever Object %d\n", Highest_ever_object_index);
|
|
}
|
|
}
|
|
|
|
return objnum;
|
|
}
|
|
|
|
// frees up an object. Generally, ObjDelete() should be called to get
|
|
// rid of an object. This function deallocates the object entry after
|
|
// the object has been unlinked
|
|
void ObjFree(int objnum) {
|
|
ObjFreePositionHistory(&Objects[objnum]);
|
|
|
|
free_obj_list[--Num_objects] = objnum;
|
|
ASSERT(Num_objects >= 0);
|
|
|
|
if (objnum == Highest_object_index)
|
|
while (Objects[--Highest_object_index].type == OBJ_NONE)
|
|
;
|
|
}
|
|
|
|
void ObjSetAABB(object *obj) {
|
|
vector object_rad;
|
|
|
|
if (obj->type == OBJ_ROOM) {
|
|
obj->min_xyz = Rooms[obj->id].min_xyz;
|
|
obj->max_xyz = Rooms[obj->id].max_xyz;
|
|
} else if (obj->flags & OF_POLYGON_OBJECT && obj->type != OBJ_WEAPON && obj->type != OBJ_DEBRIS &&
|
|
obj->type != OBJ_POWERUP && obj->type != OBJ_PLAYER) {
|
|
vector offset_pos;
|
|
|
|
object_rad.x = object_rad.y = object_rad.z = Poly_models[obj->rtype.pobj_info.model_num].anim_size;
|
|
offset_pos = obj->pos + obj->anim_sphere_offset;
|
|
|
|
obj->min_xyz = offset_pos - object_rad;
|
|
obj->max_xyz = offset_pos + object_rad;
|
|
} else {
|
|
object_rad.x = obj->size;
|
|
object_rad.y = obj->size;
|
|
object_rad.z = obj->size;
|
|
|
|
obj->min_xyz = obj->pos - object_rad;
|
|
|
|
obj->max_xyz = obj->pos + object_rad;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// initialize a new object. adds to the list for the given room
|
|
// returns the object number
|
|
int ObjCreate(uint8_t type, uint16_t id, int roomnum, vector *pos, const matrix *orient, int parent_handle) {
|
|
int objnum;
|
|
object *obj;
|
|
int handle;
|
|
|
|
ASSERT(type != OBJ_NONE);
|
|
|
|
if (ROOMNUM_OUTSIDE(roomnum)) {
|
|
ASSERT(CELLNUM(roomnum) <= TERRAIN_WIDTH * TERRAIN_DEPTH);
|
|
ASSERT(CELLNUM(roomnum) >= 0);
|
|
|
|
roomnum = GetTerrainRoomFromPos(pos);
|
|
|
|
if (roomnum == -1)
|
|
return -1;
|
|
}
|
|
|
|
// Get next free object
|
|
objnum = ObjAllocate();
|
|
if (objnum == -1) // no free objects
|
|
return -1;
|
|
obj = &Objects[objnum];
|
|
|
|
// Make sure the object is ok
|
|
ASSERT(obj->type == OBJ_NONE); // make sure unused
|
|
ASSERT(obj->roomnum == -1); // make sure unlinked
|
|
|
|
// Compute the new handle
|
|
handle = obj->handle + HANDLE_COUNT_INCREMENT;
|
|
|
|
// Check for handle wrap
|
|
if ((handle & HANDLE_COUNT_MASK) == HANDLE_COUNT_MASK) // Going to wrap!
|
|
Int3(); // Show this to Matt, or email him if he's not here
|
|
|
|
// Initialize the object
|
|
if (!ObjInit(obj, type, id, handle, pos, Gametime, parent_handle)) { // Couldn't init!
|
|
obj->type = OBJ_NONE; // mark as unused
|
|
ObjFree(objnum); // de-allocate object
|
|
return -1;
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
if (print_object_info) {
|
|
mprintf(0, "Created object %d of type %d\n", objnum, obj->type);
|
|
}
|
|
#endif
|
|
|
|
// Set the object's orietation
|
|
// THIS MUST BE DONE AFTER ObjInit (as ObjInit could load in a polymodel and set the anim and wall offsets)
|
|
ObjSetOrient(obj, orient ? orient : &Identity_matrix);
|
|
|
|
// Link object into room or terrain cell
|
|
ObjLink(objnum, roomnum);
|
|
|
|
// Type specific should have set up the size, so now we can compute the bounding box.
|
|
ObjSetAABB(obj);
|
|
|
|
// Let the demo system know about this object
|
|
DemoWriteObjCreate(type, id, roomnum, pos, orient, parent_handle, obj);
|
|
|
|
ObjInitPositionHistory(obj);
|
|
|
|
// Check bad init code that doesn't call ComputeDefaultSize()
|
|
ASSERT(!(obj->type != OBJ_ROOM && obj->type != OBJ_DEBRIS && (obj->flags & OF_POLYGON_OBJECT) &&
|
|
!(Poly_models[obj->rtype.pobj_info.model_num].flags & PMF_SIZE_COMPUTED)));
|
|
|
|
// Done!
|
|
return objnum;
|
|
}
|
|
|
|
void ObjInitPositionHistory(object *obj) {
|
|
ASSERT(Object_map_position_history[OBJNUM(obj)] == -1);
|
|
|
|
// If it is one of the types of object's that we have to sample positions for
|
|
// Initialize that
|
|
switch (obj->type) {
|
|
case OBJ_ROBOT:
|
|
case OBJ_WEAPON:
|
|
case OBJ_DEBRIS:
|
|
if (Num_free_object_position_history > 0) {
|
|
// we have a free slot
|
|
Num_free_object_position_history--;
|
|
int free_slot = Object_map_position_free_slots[Num_free_object_position_history];
|
|
Object_map_position_history[OBJNUM(obj)] = free_slot;
|
|
int i;
|
|
for (i = 0; i < MAX_POSITION_HISTORY; i++) {
|
|
Object_position_samples[free_slot].pos[i] = obj->pos;
|
|
}
|
|
} else {
|
|
// out of slots!
|
|
Object_map_position_history[OBJNUM(obj)] = -1;
|
|
}
|
|
break;
|
|
default:
|
|
// not saving positions
|
|
Object_map_position_history[OBJNUM(obj)] = -1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ObjFreePositionHistory(object *obj) {
|
|
int objnum = OBJNUM(obj);
|
|
|
|
if (Object_map_position_history[objnum] != -1) {
|
|
int slot_to_free = Object_map_position_history[objnum];
|
|
Object_map_position_history[objnum] = -1;
|
|
|
|
Object_map_position_free_slots[Num_free_object_position_history] = slot_to_free;
|
|
Num_free_object_position_history++;
|
|
ASSERT(Num_free_object_position_history <= MAX_OBJECT_POS_HISTORY);
|
|
}
|
|
}
|
|
|
|
void ObjResetPositionHistory(void) {
|
|
Num_free_object_position_history = MAX_OBJECT_POS_HISTORY;
|
|
Object_position_head = 0;
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_OBJECTS; i++) {
|
|
Object_map_position_history[i] = -1;
|
|
|
|
if (i < MAX_OBJECT_POS_HISTORY) {
|
|
// this array is guaranteed smaller than Objects[]
|
|
// so instead of looping through an array again just for
|
|
// this, I'll stick it in this loop.
|
|
Object_map_position_free_slots[i] = i;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < MAX_POSITION_HISTORY; i++) {
|
|
Last_position_history_update[i] = 0 - (0.1f * i);
|
|
}
|
|
}
|
|
|
|
void ObjReInitPositionHistory(void) {
|
|
ObjResetPositionHistory();
|
|
|
|
int i;
|
|
for (i = 0; i <= Highest_object_index; i++) {
|
|
if (Objects[i].type != OBJ_NONE) {
|
|
ObjInitPositionHistory(&Objects[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
extern void newdemo_record_guided_end();
|
|
|
|
// remove object from the world
|
|
void ObjDelete(int objnum) {
|
|
object *obj = &Objects[objnum];
|
|
|
|
if (obj->type == OBJ_DUMMY) {
|
|
// unghost the object before destroying it
|
|
ObjUnGhostObject(objnum);
|
|
}
|
|
|
|
/* if ((Game_mode & GM_MULTI) && Netgame.local_role==LR_CLIENT && Objects[objnum].type==OBJ_POWERUP)
|
|
{
|
|
mprintf(0,"Deleting object number %d type=%d id=%d\n",objnum,obj->type,obj->id);
|
|
}*/
|
|
|
|
if ((Game_mode & GM_MULTI) && (obj->flags & OF_SERVER_OBJECT)) {
|
|
if (Netgame.local_role == LR_CLIENT && NetPlayers[Player_num].sequence > NETSEQ_OBJECTS &&
|
|
NetPlayers[Player_num].sequence != NETSEQ_LEVEL_END) {
|
|
if (!(obj->flags & OF_SERVER_SAYS_DELETE)) {
|
|
mprintf(0, "Illegally deleting server object! Objnum=%d type=%d id=%d\n", objnum, obj->type, obj->id);
|
|
Int3();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (obj->flags & OF_POLYGON_OBJECT) {
|
|
polyobj_info *p_info = &obj->rtype.pobj_info;
|
|
if (p_info->multi_turret_info.keyframes != NULL) {
|
|
mem_free(p_info->multi_turret_info.keyframes);
|
|
mem_free(p_info->multi_turret_info.last_keyframes);
|
|
|
|
p_info->multi_turret_info.keyframes = NULL;
|
|
p_info->multi_turret_info.last_keyframes = NULL;
|
|
}
|
|
}
|
|
|
|
ASSERT(objnum != -1);
|
|
// ASSERT(objnum != 0 );
|
|
ASSERT(obj->type != OBJ_NONE);
|
|
// ASSERT(obj != Player_object);
|
|
|
|
//?? if (obj->type==OBJ_WEAPON && obj->id==GUIDEDMISS_ID) {
|
|
//?? int pnum=Objects[obj->ctype.laser_info.parent_num].id;
|
|
//?? mprintf(0,"Deleting a guided missile! Player %d\n\n",pnum);
|
|
//??
|
|
//?? if (pnum!=Player_num) {
|
|
//?? mprintf(0,"deleting missile that belongs to %d (%s)!\n",pnum,Players[pnum].callsign);
|
|
//?? Guided_missile[pnum]=NULL;
|
|
//?? }
|
|
//?? else if (Newdemo_state==ND_STATE_RECORDING)
|
|
//?? newdemo_record_guided_end();
|
|
//??
|
|
//?? }
|
|
|
|
if (obj == Viewer_object) // deleting the viewer?
|
|
Viewer_object = Player_object; //..make the player the viewer
|
|
|
|
// Delete this chase camera if needed
|
|
if (Player_camera_objnum == objnum)
|
|
Player_camera_objnum = -1;
|
|
|
|
//?? if (obj->flags & OF_ATTACHED) //detach this from object
|
|
//?? obj_detach_one(obj);
|
|
//??
|
|
//?? if (obj->attached_obj != -1) //detach all objects from this
|
|
//?? obj_detach_all(obj);
|
|
|
|
#ifdef _DEBUG
|
|
if (print_object_info) {
|
|
mprintf(0, "Deleting object %d of type %d\n", objnum, Objects[objnum].type);
|
|
}
|
|
#endif
|
|
|
|
if (obj->type == OBJ_WEAPON && obj->ctype.laser_info.parent_type == OBJ_PLAYER) {
|
|
int pnum = Objects[(obj->parent_handle & HANDLE_OBJNUM_MASK)].id;
|
|
|
|
if (Players[pnum].guided_obj == obj) {
|
|
mprintf(0, "Deleting a guided missile!");
|
|
Players[pnum].guided_obj = NULL;
|
|
}
|
|
}
|
|
|
|
if (obj->type == OBJ_WEAPON && obj->ctype.laser_info.parent_type == OBJ_PLAYER) {
|
|
int pnum = Objects[(obj->parent_handle & HANDLE_OBJNUM_MASK)].id;
|
|
|
|
if (Players[pnum].user_timeout_obj == obj) {
|
|
mprintf(0, "Deleting a timeout missile!");
|
|
Players[pnum].user_timeout_obj = NULL;
|
|
}
|
|
}
|
|
|
|
ObjUnlink(objnum);
|
|
|
|
ASSERT(Objects[0].next != 0);
|
|
|
|
// Update powerup respawn point
|
|
if ((Game_mode & GM_MULTI) && Netgame.local_role == LR_SERVER) {
|
|
if (obj->type == OBJ_POWERUP && Object_info[obj->id].respawn_scalar > 0) {
|
|
for (int i = 0; i < Num_powerup_respawn; i++) {
|
|
if (Powerup_respawn[i].used && Powerup_respawn[i].objnum == objnum) {
|
|
Powerup_respawn[i].used = 0;
|
|
Powerup_respawn[i].objnum = -1;
|
|
Powerup_timer[Num_powerup_timer].respawn_time =
|
|
Gametime + (Netgame.respawn_time * Object_info[obj->id].respawn_scalar);
|
|
Powerup_timer[Num_powerup_timer].id = obj->id;
|
|
Num_powerup_timer++;
|
|
mprintf(0, "Adding powerup id %d to respawn list! count=%d\n", obj->id, Num_powerup_timer);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Kill any script thread associated with this object.
|
|
FreeObjectScripts(obj, false);
|
|
|
|
if (obj->custom_default_script_name) {
|
|
mem_free(obj->custom_default_script_name);
|
|
obj->custom_default_script_name = NULL;
|
|
}
|
|
|
|
if (obj->custom_default_module_name) {
|
|
mem_free(obj->custom_default_module_name);
|
|
obj->custom_default_module_name = NULL;
|
|
}
|
|
|
|
obj->type = OBJ_NONE; // unused!
|
|
obj->roomnum = -1; // zero it!
|
|
|
|
// Free lightmap memory
|
|
if (obj->lm_object.used)
|
|
ClearObjectLightmaps(obj);
|
|
|
|
// Free up effects memory
|
|
if (obj->effect_info) {
|
|
mem_free(obj->effect_info);
|
|
obj->effect_info = NULL;
|
|
}
|
|
|
|
if (obj->ai_info != NULL) {
|
|
AIDestroyObj(obj);
|
|
mem_free(obj->ai_info);
|
|
obj->ai_info = NULL;
|
|
}
|
|
|
|
if (obj->dynamic_wb != NULL) {
|
|
mem_free(obj->dynamic_wb);
|
|
obj->dynamic_wb = NULL;
|
|
}
|
|
|
|
if (obj->attach_children != NULL) {
|
|
mem_free(obj->attach_children);
|
|
obj->attach_children = NULL;
|
|
}
|
|
|
|
if (obj->name) {
|
|
mem_free(obj->name);
|
|
obj->name = NULL;
|
|
}
|
|
|
|
if (obj->lighting_info) {
|
|
mem_free(obj->lighting_info);
|
|
obj->lighting_info = NULL;
|
|
}
|
|
|
|
ObjFree(objnum);
|
|
}
|
|
|
|
// Frees all the objects that are currently in use
|
|
void FreeAllObjects() {
|
|
int objnum;
|
|
|
|
for (objnum = 0; objnum <= Highest_object_index; objnum++)
|
|
if (Objects[objnum].type != OBJ_NONE) {
|
|
Objects[objnum].flags |= OF_SERVER_SAYS_DELETE;
|
|
Objects[objnum].flags &= ~OF_INPLAYERINVENTORY;
|
|
ObjDelete(objnum);
|
|
}
|
|
|
|
FreeAllVisEffects();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------------------------
|
|
void ObjDeleteDead() {
|
|
int i;
|
|
object *objp;
|
|
int local_dead_player_object = -1;
|
|
|
|
// Move all objects
|
|
objp = Objects;
|
|
|
|
for (i = 0; i <= Highest_object_index; i++) {
|
|
if ((objp->type != OBJ_NONE) && (objp->flags & OF_DEAD)) {
|
|
if (objp->flags & OF_INFORM_DESTROY_TO_LG) {
|
|
Level_goals.Inform(LIT_OBJECT, LGF_COMP_DESTROY, objp->handle);
|
|
}
|
|
|
|
if (objp->flags & OF_INPLAYERINVENTORY) {
|
|
// this object is in a player inventory somewhere...remove it first!
|
|
InventoryRemoveObject(objp->handle);
|
|
}
|
|
|
|
if (objp->type == OBJ_DUMMY) {
|
|
// unghost the object before deleting it
|
|
ObjUnGhostObject(i);
|
|
|
|
// tell clients to unghost
|
|
if ((Game_mode & GM_MULTI) && (Netgame.local_role != LR_CLIENT)) {
|
|
MultiSendGhostObject(&Objects[i], false);
|
|
}
|
|
}
|
|
|
|
if (objp->flags & OF_POLYGON_OBJECT) {
|
|
if (objp->flags & OF_ATTACHED) {
|
|
tOSIRISEventInfo ei;
|
|
ei.evt_child_died.it_handle = objp->handle;
|
|
|
|
Osiris_CallEvent(ObjGet(objp->attach_ultimate_handle), EVT_CHILD_DIED, &ei);
|
|
}
|
|
|
|
UnattachFromParent(objp);
|
|
UnattachChildren(objp);
|
|
}
|
|
|
|
// Execute script for death
|
|
tOSIRISEventInfo ei;
|
|
ei.evt_destroy.is_dying = 1;
|
|
Osiris_CallEvent(objp, EVT_DESTROY, &ei);
|
|
|
|
if (Game_mode & GM_MULTI) {
|
|
DLLInfo.me_handle = DLLInfo.it_handle = objp->handle;
|
|
CallGameDLL(EVT_GAMEOBJDESTROYED, &DLLInfo);
|
|
}
|
|
|
|
// detach any scripts attached
|
|
Osiris_DetachScriptsFromObject(objp);
|
|
|
|
// if it's flag to tell the clients to remove is set, tell them now
|
|
if ((Game_mode & GM_MULTI) && (Netgame.local_role != LR_CLIENT)) {
|
|
bool removed = false;
|
|
|
|
if (objp->flags & OF_SEND_MULTI_REMOVE_ON_DEATH) {
|
|
// Tell all clients to remove this object
|
|
MultiSendRemoveObject(objp, 0);
|
|
removed = true;
|
|
}
|
|
if (!removed && objp->flags & OF_SEND_MULTI_REMOVE_ON_DEATHWS) {
|
|
// Tell all clients to remove this object with sound
|
|
MultiSendRemoveObject(objp, 1);
|
|
removed = true;
|
|
}
|
|
}
|
|
|
|
if (IS_GUIDEBOT(objp)) {
|
|
int i;
|
|
int handle = objp->handle;
|
|
|
|
for (i = 0; i < MAX_PLAYERS; i++) {
|
|
if (objp->handle == Buddy_handle[i]) {
|
|
Buddy_handle[i] = OBJECT_HANDLE_NONE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// check if player being killed.
|
|
if (objp->type != OBJ_PLAYER) {
|
|
ObjDelete(i);
|
|
}
|
|
}
|
|
objp++;
|
|
}
|
|
|
|
// Delete our visual effects
|
|
VisEffectDeleteDead();
|
|
}
|
|
|
|
// when an object has moved into a new room, this function unlinks it
|
|
// from its old room and links it into the new room
|
|
void ObjRelink(int objnum, int newroomnum) {
|
|
ASSERT((objnum >= 0) && (objnum <= Highest_object_index));
|
|
|
|
if (!ROOMNUM_OUTSIDE(newroomnum)) {
|
|
ASSERT((newroomnum <= Highest_room_index) && (newroomnum >= 0));
|
|
ASSERT(Rooms[newroomnum].used);
|
|
} else
|
|
ASSERT(CELLNUM(newroomnum) >= 0 && CELLNUM(newroomnum) <= (TERRAIN_WIDTH * TERRAIN_DEPTH));
|
|
|
|
ObjUnlink(objnum);
|
|
ObjLink(objnum, newroomnum);
|
|
}
|
|
|
|
void DoCycledAnim(object *obj) {
|
|
float from;
|
|
float to;
|
|
float delta;
|
|
float spc;
|
|
|
|
float anim_time = Frametime;
|
|
|
|
if (!(obj->flags & OF_POLYGON_OBJECT))
|
|
return;
|
|
|
|
from = Object_info[obj->id].anim[MC_STANDING].elem[AS_ALERT].from;
|
|
to = Object_info[obj->id].anim[MC_STANDING].elem[AS_ALERT].to;
|
|
spc = Object_info[obj->id].anim[MC_STANDING].elem[AS_ALERT].spc;
|
|
|
|
if (spc <= 0.0)
|
|
return;
|
|
ASSERT(from <= to);
|
|
|
|
if (obj->rtype.pobj_info.anim_frame < from || obj->rtype.pobj_info.anim_frame > to) {
|
|
mprintf(0, "NonAI-Animation: Correcting for an incorrect frame number\n");
|
|
obj->rtype.pobj_info.anim_frame = from;
|
|
}
|
|
|
|
delta = to - from;
|
|
|
|
ASSERT(delta >= 0.0f);
|
|
|
|
if (delta > 0.0f) {
|
|
// Frames per second will be on the animation page
|
|
float step = (1.0f / (spc / (delta))) * anim_time;
|
|
|
|
while (step > delta) {
|
|
step -= delta;
|
|
}
|
|
|
|
ASSERT(step >= 0.0f);
|
|
|
|
obj->rtype.pobj_info.anim_frame += step;
|
|
|
|
if (obj->rtype.pobj_info.anim_frame >= to) {
|
|
|
|
// We are guarenteed that this is between 'to' and 'from' from the above asserts() :)
|
|
obj->rtype.pobj_info.anim_frame -= delta;
|
|
}
|
|
} else {
|
|
obj->rtype.pobj_info.anim_frame = from;
|
|
}
|
|
|
|
ASSERT(obj->rtype.pobj_info.anim_frame >= from && obj->rtype.pobj_info.anim_frame <= to);
|
|
}
|
|
|
|
void TimeoutObject(object *obj) {
|
|
switch (obj->type) {
|
|
case OBJ_WEAPON:
|
|
TimeoutWeapon(obj);
|
|
break;
|
|
case OBJ_SHOCKWAVE:
|
|
DoConcussiveForce(obj, obj->parent_handle);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Does afterburn effects to a player ship
|
|
void DoPlayerAfterburnControl(game_controls *controls, object *objp) {
|
|
static int afterburner_id = -1;
|
|
int slot = objp->id;
|
|
|
|
if (controls->afterburn_thrust > 0) {
|
|
Players[slot].last_afterburner_time = Gametime;
|
|
|
|
if (Players[slot].afterburn_time_left > 0) {
|
|
float punch_scalar = 1.0;
|
|
|
|
if (Players[slot].afterburn_time_left == (AFTERBURN_TIME))
|
|
AddToShakeMagnitude(objp->mtype.phys_info.mass / 2);
|
|
|
|
if (Players[slot].afterburn_time_left > (AFTERBURN_TIME * .90))
|
|
punch_scalar = 1.8f;
|
|
else if (Players[slot].afterburn_time_left > (AFTERBURN_TIME * .80) &&
|
|
Players[slot].afterburn_time_left < (AFTERBURN_TIME * .90)) {
|
|
float norm = Players[slot].afterburn_time_left - (AFTERBURN_TIME * .80);
|
|
norm /= (AFTERBURN_TIME * .1);
|
|
punch_scalar = 1.0 + (norm * .8);
|
|
}
|
|
|
|
if (OBJECT_OUTSIDE(objp))
|
|
controls->forward_thrust = controls->afterburn_thrust * 1.6 * punch_scalar;
|
|
else
|
|
controls->forward_thrust = controls->afterburn_thrust * 1.6 * punch_scalar;
|
|
|
|
Players[slot].flags |= PLAYER_FLAGS_AFTERBURN_ON | PLAYER_FLAGS_THRUSTED;
|
|
Players[slot].afterburn_time_left -= Frametime;
|
|
if (Players[slot].afterburn_time_left < 0)
|
|
Players[slot].afterburn_time_left = 0;
|
|
} else {
|
|
Players[slot].afterburn_time_left = 0;
|
|
Players[slot].flags &= ~PLAYER_FLAGS_AFTERBURN_ON;
|
|
}
|
|
} else {
|
|
Players[slot].flags &= ~PLAYER_FLAGS_AFTERBURN_ON;
|
|
|
|
if (Players[slot].afterburn_time_left < AFTERBURN_TIME) {
|
|
if (Players[slot].energy > 5.0) {
|
|
float useage;
|
|
if (afterburner_id == -1)
|
|
afterburner_id = FindObjectIDName("Afterburner");
|
|
|
|
if (afterburner_id != -1 && Players[slot].inventory.CheckItem(OBJ_POWERUP, afterburner_id))
|
|
useage = Frametime * 2;
|
|
else
|
|
useage = Frametime;
|
|
|
|
Players[slot].afterburn_time_left += useage;
|
|
Players[slot].afterburn_time_left = std::min<float>(AFTERBURN_TIME, Players[slot].afterburn_time_left);
|
|
|
|
Players[slot].energy -= (useage);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Deal with the player's rear view
|
|
void CheckRearView(int down_count, bool down_state) {
|
|
player *player = &Players[Player_num];
|
|
|
|
#define LEAVE_TIME (1.0 / 16) // How long until we decide key is down
|
|
|
|
static bool leave_mode;
|
|
static float entry_time;
|
|
|
|
if (down_count) { // key/button has gone down
|
|
|
|
if (player->flags & PLAYER_FLAGS_REARVIEW) { // rear view was active, so turn it off
|
|
player->flags &= ~PLAYER_FLAGS_REARVIEW;
|
|
} else { // rear view wasn't active, so turn it on
|
|
player->flags |= PLAYER_FLAGS_REARVIEW;
|
|
leave_mode = 0; // means wait for another key
|
|
entry_time = Gametime;
|
|
}
|
|
} else { // key didn't go down this frame; check if it used to be down
|
|
|
|
if (down_state) { // key is still down
|
|
|
|
if (!leave_mode && ((Gametime - entry_time) > LEAVE_TIME))
|
|
leave_mode = 1;
|
|
} else {
|
|
|
|
if (leave_mode && (player->flags & PLAYER_FLAGS_REARVIEW)) {
|
|
player->flags &= ~PLAYER_FLAGS_REARVIEW;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extern int Timedemo_frame;
|
|
float Timedemo_timecount;
|
|
|
|
// Do the controls for a player-type object
|
|
void DoFlyingControl(object *objp) {
|
|
game_controls controls;
|
|
|
|
if (Dedicated_server)
|
|
return;
|
|
|
|
ASSERT(objp->control_type == CT_FLYING);
|
|
|
|
if ((objp->type != OBJ_PLAYER && objp->type != OBJ_OBSERVER) || objp->id != Player_num)
|
|
return;
|
|
|
|
if (Timedemo_frame != -1) {
|
|
matrix temp_mat, rot_mat;
|
|
|
|
if (Timedemo_frame == 0) {
|
|
Timedemo_timecount = 0;
|
|
} else {
|
|
Timedemo_timecount += Frametime;
|
|
}
|
|
|
|
vm_AnglesToMatrix(&temp_mat, 0, 65536 / 360, 0);
|
|
|
|
rot_mat = objp->orient * temp_mat;
|
|
|
|
ObjSetOrient(objp, &rot_mat);
|
|
|
|
Timedemo_frame++;
|
|
if (Timedemo_frame == 360) {
|
|
// Calculate timedemo framerate
|
|
|
|
float fps = 1.0 / (Timedemo_timecount / 360.0);
|
|
AddHUDMessage(TXT_TIMEDEMO, fps);
|
|
Timedemo_frame = -1;
|
|
}
|
|
|
|
return;
|
|
}
|
|
if (Demo_flags == DF_PLAYBACK) {
|
|
// If we are playing back a demo, bail early and don't process controls
|
|
return;
|
|
}
|
|
|
|
// Read the controllers
|
|
ReadPlayerControls(&controls);
|
|
|
|
if ((objp->type == OBJ_PLAYER) && (Players[objp->id].controller_bitflags != 0xffffffff)) {
|
|
player *pp = &Players[objp->id];
|
|
if (!(pp->controller_bitflags & PCBF_FORWARD)) {
|
|
if (controls.forward_thrust > 0.0f)
|
|
controls.forward_thrust = 0.0f;
|
|
}
|
|
if (!(pp->controller_bitflags & PCBF_REVERSE)) {
|
|
if (controls.forward_thrust < 0.0f)
|
|
controls.forward_thrust = 0.0f;
|
|
}
|
|
if (!(pp->controller_bitflags & PCBF_LEFT)) {
|
|
if (controls.sideways_thrust < 0.0f)
|
|
controls.sideways_thrust = 0.0f;
|
|
}
|
|
if (!(pp->controller_bitflags & PCBF_RIGHT)) {
|
|
if (controls.sideways_thrust > 0.0f)
|
|
controls.sideways_thrust = 0.0f;
|
|
}
|
|
if (!(pp->controller_bitflags & PCBF_UP)) {
|
|
if (controls.vertical_thrust > 0.0f)
|
|
controls.vertical_thrust = 0.0f;
|
|
}
|
|
if (!(pp->controller_bitflags & PCBF_DOWN)) {
|
|
if (controls.vertical_thrust < 0.0f)
|
|
controls.vertical_thrust = 0.0f;
|
|
}
|
|
if (!(pp->controller_bitflags & PCBF_PITCHUP)) {
|
|
if (controls.pitch_thrust > 0.0f)
|
|
controls.pitch_thrust = 0.0f;
|
|
}
|
|
if (!(pp->controller_bitflags & PCBF_PITCHDOWN)) {
|
|
if (controls.pitch_thrust < 0.0f)
|
|
controls.pitch_thrust = 0.0f;
|
|
}
|
|
if (!(pp->controller_bitflags & PCBF_HEADINGLEFT)) {
|
|
if (controls.heading_thrust < 0.0f)
|
|
controls.heading_thrust = 0.0f;
|
|
}
|
|
if (!(pp->controller_bitflags & PCBF_HEADINGRIGHT)) {
|
|
if (controls.heading_thrust > 0.0f)
|
|
controls.heading_thrust = 0.0f;
|
|
}
|
|
if (!(pp->controller_bitflags & PCBF_BANKLEFT)) {
|
|
if (controls.bank_thrust < 0.0f)
|
|
controls.bank_thrust = 0.0f;
|
|
}
|
|
if (!(pp->controller_bitflags & PCBF_BANKRIGHT)) {
|
|
if (controls.bank_thrust > 0.0f)
|
|
controls.bank_thrust = 0.0f;
|
|
}
|
|
if (!(pp->controller_bitflags & PCBF_PRIMARY)) {
|
|
controls.fire_primary_down_count = 0;
|
|
controls.fire_primary_down_time = 0;
|
|
controls.fire_primary_down_state = false;
|
|
}
|
|
if (!(pp->controller_bitflags & PCBF_SECONDARY)) {
|
|
controls.fire_secondary_down_count = 0;
|
|
controls.fire_secondary_down_time = 0;
|
|
controls.fire_secondary_down_state = false;
|
|
}
|
|
if (!(pp->controller_bitflags & PCBF_AFTERBURNER)) {
|
|
controls.afterburn_thrust = 0.0f;
|
|
}
|
|
}
|
|
|
|
// Update IntelliVIBE
|
|
if (objp == Player_object) {
|
|
VIBE_DoControls(&controls);
|
|
}
|
|
|
|
// Send an event to the Game DLLs so they can do any processing of game controls
|
|
if (Game_mode & GM_MULTI) {
|
|
DLLInfo.me_handle = DLLInfo.it_handle = objp->handle;
|
|
DLLInfo.special_data = (uint8_t *)&controls;
|
|
CallGameDLL(EVT_GAMEDOCONTROLS, &DLLInfo);
|
|
}
|
|
|
|
// Deal with afterburner
|
|
if (objp->type == OBJ_PLAYER) {
|
|
ASSERT(Player_num == objp->id);
|
|
|
|
// Reset thrusting
|
|
Players[objp->id].flags &= ~PLAYER_FLAGS_THRUSTED;
|
|
|
|
DoPlayerAfterburnControl(&controls, objp);
|
|
}
|
|
|
|
// Update object's physics
|
|
if (objp->movement_type == MT_PHYSICS) {
|
|
if (objp->type == OBJ_PLAYER && Players[objp->id].guided_obj != NULL) {
|
|
ASSERT(objp->id == Player_num);
|
|
object *g_obj = Players[objp->id].guided_obj;
|
|
|
|
g_obj->mtype.phys_info.rotthrust.x = controls.pitch_thrust * g_obj->mtype.phys_info.full_rotthrust;
|
|
g_obj->mtype.phys_info.rotthrust.z = controls.bank_thrust * g_obj->mtype.phys_info.full_rotthrust;
|
|
g_obj->mtype.phys_info.rotthrust.y = controls.heading_thrust * g_obj->mtype.phys_info.full_rotthrust;
|
|
} else {
|
|
player *playp = &Players[objp->id];
|
|
|
|
objp->mtype.phys_info.rotthrust.x =
|
|
controls.pitch_thrust * objp->mtype.phys_info.full_rotthrust * playp->turn_scalar;
|
|
objp->mtype.phys_info.rotthrust.z =
|
|
controls.bank_thrust * objp->mtype.phys_info.full_rotthrust * playp->turn_scalar;
|
|
objp->mtype.phys_info.rotthrust.y =
|
|
controls.heading_thrust * objp->mtype.phys_info.full_rotthrust * playp->turn_scalar;
|
|
}
|
|
|
|
float speed_scalar = 1.0;
|
|
|
|
// If thrusting foward, show glow
|
|
if (objp->type == OBJ_PLAYER && controls.forward_thrust > 0)
|
|
Players[objp->id].flags |= PLAYER_FLAGS_THRUSTED;
|
|
|
|
if (objp->effect_info && objp->effect_info->type_flags & EF_FREEZE)
|
|
speed_scalar = objp->effect_info->freeze_scalar;
|
|
|
|
if (OBJECT_OUTSIDE(objp))
|
|
speed_scalar *= 1.3f;
|
|
|
|
objp->mtype.phys_info.thrust = speed_scalar * Players[objp->id].movement_scalar *
|
|
((objp->orient.fvec * controls.forward_thrust * objp->mtype.phys_info.full_thrust) +
|
|
(objp->orient.uvec * controls.vertical_thrust * objp->mtype.phys_info.full_thrust) +
|
|
(objp->orient.rvec * controls.sideways_thrust * objp->mtype.phys_info.full_thrust));
|
|
}
|
|
|
|
// Process player-specific stuff
|
|
if (objp->type == OBJ_PLAYER) {
|
|
// Process weapon firing
|
|
FireWeaponFromPlayer(objp, PW_PRIMARY, controls.fire_primary_down_count, controls.fire_primary_down_state,
|
|
controls.fire_primary_down_time);
|
|
FireWeaponFromPlayer(objp, PW_SECONDARY, controls.fire_secondary_down_count, controls.fire_secondary_down_state,
|
|
controls.fire_secondary_down_time);
|
|
|
|
// Deal with the flare
|
|
for (int i = 0; i < controls.fire_flare_down_count; i++)
|
|
FireFlareFromPlayer(objp);
|
|
|
|
// Do rear view
|
|
CheckRearView(controls.rearview_down_count, controls.rearview_down_state);
|
|
}
|
|
}
|
|
|
|
// Do any special effects processing for this object
|
|
void ObjDoEffects(object *obj) {
|
|
if (!obj->effect_info)
|
|
return;
|
|
|
|
static int napalm_id = -1;
|
|
|
|
if (napalm_id == -1)
|
|
napalm_id = FindWeaponName("Napalm");
|
|
|
|
if (obj->effect_info->type_flags & EF_VOLUME_CHANGING) {
|
|
obj->effect_info->volume_change_time -= Frametime;
|
|
if (obj->effect_info->volume_change_time <= 0.0f) {
|
|
obj->effect_info->type_flags &= ~EF_VOLUME_CHANGING;
|
|
}
|
|
}
|
|
|
|
if (obj->effect_info->type_flags & EF_FADING_IN) {
|
|
obj->effect_info->fade_time -= Frametime;
|
|
if (obj->effect_info->fade_time <= 0.0f) {
|
|
obj->effect_info->type_flags &= ~EF_FADING_IN;
|
|
}
|
|
}
|
|
|
|
if (obj->effect_info->type_flags & EF_CLOAKED) {
|
|
obj->effect_info->cloak_time -= Frametime;
|
|
if (obj->effect_info->cloak_time <= 0.0f) {
|
|
// reappear the object
|
|
MakeObjectVisible(obj);
|
|
}
|
|
}
|
|
|
|
if (obj->effect_info->type_flags & EF_FADING_OUT) {
|
|
obj->effect_info->fade_time -= Frametime;
|
|
if (obj->effect_info->fade_time <= 0.0f) {
|
|
obj->effect_info->type_flags &= ~EF_FADING_OUT;
|
|
obj->effect_info->type_flags |= EF_CLOAKED;
|
|
}
|
|
}
|
|
|
|
if (obj->effect_info->type_flags & EF_LIQUID) {
|
|
obj->effect_info->liquid_time_left -= Frametime;
|
|
if (obj->effect_info->liquid_time_left <= 0.0f) {
|
|
// Stop doing liquid effect
|
|
obj->effect_info->type_flags &= ~EF_LIQUID;
|
|
if (obj == Viewer_object)
|
|
Render_FOV = D3_DEFAULT_FOV;
|
|
} else {
|
|
if (obj == Viewer_object) {
|
|
int inttime = Gametime;
|
|
float normtime = Gametime - inttime;
|
|
|
|
float scalar = FixSin(normtime * 65535 * 4);
|
|
|
|
scalar *= ((float)obj->effect_info->liquid_mag);
|
|
if (obj->effect_info->liquid_time_left < 1)
|
|
scalar *= (obj->effect_info->liquid_time_left);
|
|
|
|
Render_FOV = D3_DEFAULT_FOV + scalar;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (obj->effect_info->type_flags & EF_DEFORM) {
|
|
obj->effect_info->deform_time -= Frametime;
|
|
if (obj->effect_info->deform_time <= 0.0f) {
|
|
// Stop deforming
|
|
obj->effect_info->type_flags &= ~EF_DEFORM;
|
|
}
|
|
}
|
|
|
|
if (obj->effect_info->type_flags & EF_FREEZE) {
|
|
obj->effect_info->freeze_scalar += (Frametime / 3);
|
|
if (obj->effect_info->freeze_scalar >= 1.0f) {
|
|
// Stop freezing
|
|
obj->effect_info->type_flags &= ~EF_FREEZE;
|
|
}
|
|
}
|
|
|
|
if (obj->effect_info->type_flags & EF_NAPALMED) {
|
|
if ((Gametime - obj->effect_info->last_damage_time) > 1.0) {
|
|
obj->effect_info->last_damage_time = Gametime;
|
|
|
|
// Apply damage to this napalmed object
|
|
object *killer;
|
|
|
|
if (obj->effect_info->damage_handle != OBJECT_HANDLE_NONE) {
|
|
uint32_t sig = obj->effect_info->damage_handle & HANDLE_COUNT_MASK;
|
|
int objnum = obj->effect_info->damage_handle & HANDLE_OBJNUM_MASK;
|
|
|
|
if ((Objects[objnum].handle & HANDLE_COUNT_MASK) != sig)
|
|
killer = NULL;
|
|
else
|
|
killer = &Objects[objnum];
|
|
} else
|
|
killer = NULL;
|
|
|
|
if (obj->type == OBJ_PLAYER) {
|
|
ApplyDamageToPlayer(obj, killer, PD_NONE, obj->effect_info->damage_per_second);
|
|
} else if (IS_GENERIC(obj->type) || (obj->type == OBJ_DOOR)) {
|
|
ApplyDamageToGeneric(obj, killer, GD_FIRE, obj->effect_info->damage_per_second);
|
|
}
|
|
}
|
|
|
|
obj->effect_info->damage_time -= Frametime;
|
|
if (obj->effect_info->damage_time <= 0) {
|
|
obj->effect_info->type_flags &= ~EF_NAPALMED;
|
|
obj->effect_info->last_damage_time = 0;
|
|
Sound_system.StopSoundLooping(obj->effect_info->sound_handle);
|
|
obj->effect_info->sound_handle = SOUND_NONE_INDEX;
|
|
}
|
|
|
|
// Drop some smoke/fire and stuff
|
|
AttachRandomNapalmEffectsToObject(obj);
|
|
}
|
|
|
|
// Do sparking stuff
|
|
if (obj->effect_info->type_flags & EF_SPARKING) {
|
|
int num_bolts = 0;
|
|
|
|
// Create spark effects
|
|
obj->effect_info->spark_timer -= Frametime;
|
|
while (obj->effect_info->spark_timer < 0.0) {
|
|
obj->effect_info->spark_timer += obj->effect_info->spark_delay;
|
|
num_bolts++;
|
|
}
|
|
CreateElectricalBolts(obj, num_bolts);
|
|
|
|
// Update timer
|
|
obj->effect_info->spark_time_left -= Frametime;
|
|
if (obj->effect_info->spark_time_left <= 0.0f) {
|
|
obj->effect_info->type_flags &= ~EF_SPARKING;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check passed-through faces for triggers, etc.
|
|
void ObjCheckTriggers(object *objp) {
|
|
for (int i = 0; i < Fvi_num_recorded_faces; i++) {
|
|
int roomnum = Fvi_recorded_faces[i].room_index;
|
|
int facenum = Fvi_recorded_faces[i].face_index;
|
|
|
|
CheckTrigger(roomnum, facenum, objp, TT_PASS_THROUGH);
|
|
}
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
void debug_check_terrain_objects() {
|
|
int i;
|
|
int j;
|
|
|
|
for (i = 0; i < TERRAIN_WIDTH * TERRAIN_DEPTH; i++) {
|
|
j = Terrain_seg[i].objects;
|
|
while (j != -1) {
|
|
ASSERT(j >= 0 && j <= Highest_object_index);
|
|
j = Objects[j].next;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef _DEBUG
|
|
extern int DoAI;
|
|
#else
|
|
#define DoAI 1
|
|
#endif
|
|
//--------------------------------------------------------------------
|
|
// Process an object for the current frame
|
|
void ObjDoFrame(object *obj) {
|
|
int previous_room = obj->roomnum;
|
|
float save_frametime = Frametime;
|
|
|
|
ASSERT(obj->type != OBJ_NONE);
|
|
|
|
// Clear attached vis for this frame
|
|
obj->lowest_attached_vis = -1;
|
|
|
|
// If this is a permissable netgame, accelerate this object if need by
|
|
if ((Game_mode & GM_MULTI) && Netgame.local_role == LR_CLIENT && (obj->flags & OF_PING_ACCELERATE)) {
|
|
Frametime += (NetPlayers[Player_num].ping_time / 2.0);
|
|
obj->flags &= ~OF_PING_ACCELERATE;
|
|
}
|
|
// Update previous position before moving
|
|
obj->last_pos = obj->pos;
|
|
|
|
// Inevitable countdown towards death
|
|
if (obj->flags & OF_USES_LIFELEFT)
|
|
obj->lifeleft -= Frametime;
|
|
|
|
// #ifdef _DEBUG
|
|
// if(Physics_player_verbose)
|
|
// {
|
|
// debug_check_terrain_objects();
|
|
// }
|
|
// #endif _DEBUG
|
|
|
|
// Process the control for this object
|
|
switch (obj->control_type) {
|
|
|
|
case CT_POWERUP:
|
|
case CT_NONE:
|
|
break;
|
|
case CT_FLYING: {
|
|
RTP_STARTINCTIME(ct_flying_time);
|
|
DoFlyingControl(obj);
|
|
RTP_ENDINCTIME(ct_flying_time);
|
|
} break;
|
|
case CT_AI:
|
|
if (DoAI) {
|
|
RTP_STARTINCTIME(ct_aidoframe_time);
|
|
AIDoFrame(obj);
|
|
RTP_ENDINCTIME(ct_aidoframe_time);
|
|
}
|
|
break;
|
|
case CT_WEAPON: {
|
|
RTP_STARTINCTIME(ct_weaponframe_time);
|
|
WeaponDoFrame(obj);
|
|
RTP_ENDINCTIME(ct_weaponframe_time);
|
|
} break;
|
|
case CT_EXPLOSION: {
|
|
RTP_STARTINCTIME(ct_explosionframe_time);
|
|
DoExplosionFrame(obj);
|
|
RTP_ENDINCTIME(ct_explosionframe_time);
|
|
} break;
|
|
case CT_DEBRIS: {
|
|
RTP_STARTINCTIME(ct_debrisframe_time);
|
|
DoDebrisFrame(obj);
|
|
RTP_ENDINCTIME(ct_debrisframe_time);
|
|
} break;
|
|
case CT_SPLINTER: {
|
|
RTP_STARTINCTIME(ct_splinterframe_time);
|
|
DoSplinterFrame(obj);
|
|
RTP_ENDINCTIME(ct_splinterframe_time);
|
|
} break;
|
|
case CT_DYING:
|
|
DoDyingFrame(obj);
|
|
break;
|
|
case CT_DYING_AND_AI:
|
|
if (DoAI)
|
|
AIDoFrame(obj);
|
|
DoDyingFrame(obj);
|
|
break;
|
|
case CT_SOUNDSOURCE:
|
|
break; // do nothing
|
|
case CT_SOAR:
|
|
break;
|
|
|
|
#ifdef _DEBUG
|
|
case CT_SLEW:
|
|
SlewFrame(obj);
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
Error("Unknown control type %d in object %i, handle/type/id = %i/%i/%i", obj->control_type, obj - Objects,
|
|
obj->handle, obj->type, obj->id);
|
|
break;
|
|
}
|
|
|
|
// #ifdef _DEBUG
|
|
// if(Physics_player_verbose)
|
|
// {
|
|
// debug_check_terrain_objects();
|
|
// }
|
|
// #endif _DEBUG
|
|
|
|
// Update cycled animation
|
|
if ((obj->control_type != CT_AI) && (obj->control_type != CT_DYING_AND_AI) && (obj->control_type != CT_DEBRIS) &&
|
|
IS_GENERIC(obj->type) && (obj->type != OBJ_ROOM)) {
|
|
if (Object_info[obj->id].anim && Object_info[obj->id].anim[MC_STANDING].elem[AS_ALERT].to) {
|
|
RTP_STARTINCTIME(cycle_anim);
|
|
DoCycledAnim(obj);
|
|
RTP_ENDINCTIME(cycle_anim);
|
|
}
|
|
}
|
|
|
|
// #ifdef _DEBUG
|
|
// if(Physics_player_verbose)
|
|
// {
|
|
// debug_check_terrain_objects();
|
|
// }
|
|
// #endif _DEBUG
|
|
|
|
// Do freeze stuff on AI controlled robots
|
|
if ((obj->type == OBJ_ROBOT || obj->type == OBJ_BUILDING) && obj->control_type == CT_AI && obj->effect_info &&
|
|
(obj->effect_info->type_flags & EF_FREEZE))
|
|
obj->mtype.phys_info.velocity *= obj->effect_info->freeze_scalar;
|
|
|
|
// #ifdef _DEBUG
|
|
// if(Physics_player_verbose)
|
|
// {
|
|
// debug_check_terrain_objects();
|
|
// }
|
|
// #endif _DEBUG
|
|
|
|
// Check for object dead of old age
|
|
if ((obj->flags & OF_USES_LIFELEFT) && (obj->lifeleft < 0)) {
|
|
bool kill_it = true;
|
|
bool tell_clients = false;
|
|
|
|
ASSERT(obj->type != OBJ_PLAYER);
|
|
|
|
if (Game_mode & GM_MULTI) {
|
|
if (Netgame.local_role == LR_CLIENT) {
|
|
if (obj->type == OBJ_POWERUP || obj->type == OBJ_ROBOT || obj->type == OBJ_CLUTTER ||
|
|
obj->type == OBJ_BUILDING) {
|
|
ASSERT(obj->flags & OF_SERVER_OBJECT);
|
|
|
|
if (!(obj->flags & OF_SERVER_SAYS_DELETE)) {
|
|
kill_it = false;
|
|
obj->lifeleft = .001f;
|
|
}
|
|
}
|
|
} else {
|
|
if (obj->type == OBJ_POWERUP || obj->type == OBJ_ROBOT || obj->type == OBJ_CLUTTER || obj->type == OBJ_BUILDING)
|
|
tell_clients = true;
|
|
}
|
|
}
|
|
|
|
if (kill_it) {
|
|
TimeoutObject(obj);
|
|
SetObjectDeadFlag(obj, tell_clients);
|
|
}
|
|
}
|
|
|
|
// #ifdef _DEBUG
|
|
// if(Physics_player_verbose)
|
|
// {
|
|
// debug_check_terrain_objects();
|
|
// }
|
|
// #endif _DEBUG
|
|
|
|
// If object is dead, don't do any more processing on it
|
|
if (obj->flags & OF_DEAD) {
|
|
Frametime = save_frametime;
|
|
return;
|
|
}
|
|
|
|
// #ifdef _DEBUG
|
|
// if(Physics_player_verbose)
|
|
// {
|
|
// debug_check_terrain_objects();
|
|
// }
|
|
// #endif _DEBUG
|
|
|
|
// Do the movement for this object
|
|
switch (obj->movement_type) {
|
|
case MT_NONE:
|
|
if ((obj->flags & OF_STUCK_ON_PORTAL) &&
|
|
!(Rooms[obj->mtype.phys_info.stuck_room].portals[obj->mtype.phys_info.stuck_portal].flags & PF_RENDER_FACES)) {
|
|
if (IS_GENERIC(obj->type) && (obj->flags & OF_CLIENT_KNOWS))
|
|
SetObjectDeadFlag(obj, true);
|
|
else
|
|
SetObjectDeadFlag(obj, false);
|
|
}
|
|
break; // this doesn't move
|
|
|
|
case MT_PHYSICS: {
|
|
RTP_STARTINCTIME(mt_physicsframe_time);
|
|
|
|
do_physics_sim(obj);
|
|
ObjCheckTriggers(obj);
|
|
|
|
RTP_ENDINCTIME(mt_physicsframe_time);
|
|
} break;
|
|
|
|
case MT_WALKING: {
|
|
RTP_STARTINCTIME(mt_walkingframe_time);
|
|
do_walking_sim(obj);
|
|
ObjCheckTriggers(obj);
|
|
RTP_ENDINCTIME(mt_walkingframe_time);
|
|
} break;
|
|
|
|
case MT_SHOCKWAVE: {
|
|
RTP_STARTINCTIME(mt_shockwave_time);
|
|
DoConcussiveForce(obj, obj->parent_handle);
|
|
RTP_ENDINCTIME(mt_shockwave_time);
|
|
} break;
|
|
|
|
case MT_OBJ_LINKED:
|
|
PhysicsLinkList[Physics_NumLinked++] = OBJNUM(obj);
|
|
break;
|
|
|
|
default:
|
|
Int3(); // unknown movement type
|
|
}
|
|
|
|
// #ifdef _DEBUG
|
|
// if(Physics_player_verbose)
|
|
// {
|
|
// debug_check_terrain_objects();
|
|
// }
|
|
// #endif _DEBUG
|
|
|
|
// Do special effects stuff to object
|
|
if (obj->effect_info) {
|
|
RTP_STARTINCTIME(obj_doeffect_time);
|
|
ObjDoEffects(obj);
|
|
RTP_ENDINCTIME(obj_doeffect_time);
|
|
}
|
|
|
|
// #ifdef _DEBUG
|
|
// if(Physics_player_verbose)
|
|
// {
|
|
// debug_check_terrain_objects();
|
|
// }
|
|
// #endif _DEBUG
|
|
|
|
// Deal with special player movement stuff
|
|
if (obj->type == OBJ_PLAYER) {
|
|
RTP_STARTINCTIME(obj_move_player_time);
|
|
DoSpecialPlayerStuff(obj);
|
|
RTP_ENDINCTIME(obj_move_player_time);
|
|
}
|
|
|
|
// #ifdef _DEBUG
|
|
// if(Physics_player_verbose)
|
|
// {
|
|
// debug_check_terrain_objects();
|
|
// }
|
|
// #endif _DEBUG
|
|
|
|
// Handle interval event for script.
|
|
do {
|
|
RTP_STARTINCTIME(obj_d3xint_time);
|
|
//@$-D3XExecScript(obj, EVT_INTERVAL, obj);
|
|
tOSIRISEventInfo ei;
|
|
ei.evt_interval.frame_time = Frametime;
|
|
ei.evt_interval.game_time = Gametime;
|
|
Osiris_CallEvent(obj, EVT_INTERVAL, &ei);
|
|
RTP_ENDINCTIME(obj_d3xint_time);
|
|
} while (0); // this do{}while(0) is here because the RTP_STARTINCTIME/RTP_ENDINCTIME must be in the same scope
|
|
// and not share scope with another RTP_STARTINCTIME
|
|
|
|
// #ifdef _DEBUG
|
|
// if(Physics_player_verbose)
|
|
// {
|
|
// debug_check_terrain_objects();
|
|
// }
|
|
// #endif _DEBUG
|
|
|
|
// Cast light
|
|
do {
|
|
RTP_STARTINCTIME(obj_objlight_time);
|
|
DoObjectLight(obj);
|
|
RTP_ENDINCTIME(obj_objlight_time);
|
|
} while (0); // this do{}while(0) is here because the RTP_STARTINCTIME/RTP_ENDINCTIME must be in the same scope
|
|
// and not share scope with another RTP_STARTINCTIME
|
|
|
|
// #ifdef _DEBUG
|
|
// if(Physics_player_verbose)
|
|
// {
|
|
// debug_check_terrain_objects();
|
|
// }
|
|
// #endif _DEBUG
|
|
|
|
// if this object is walking/rolling on the terrain, make it not LOD at that point
|
|
if (obj->movement_type == MT_WALKING && (obj->flags & OF_RENDERED) && OBJECT_OUTSIDE(obj))
|
|
TurnOffLODForCell(CELLNUM(obj->roomnum));
|
|
|
|
// #ifdef _DEBUG
|
|
// if(Physics_player_verbose)
|
|
// {
|
|
// debug_check_terrain_objects();
|
|
// }
|
|
// #endif _DEBUG
|
|
|
|
// Mark object as not rendered for this frame
|
|
obj->flags &= ~OF_RENDERED;
|
|
|
|
Frametime = save_frametime;
|
|
}
|
|
|
|
int Max_used_objects = MAX_OBJECTS - 20;
|
|
float Last_position_history_update[MAX_POSITION_HISTORY]; // gametime of the last position history update of the object
|
|
float Last_position_history_update_time = 0.0f;
|
|
|
|
//--------------------------------------------------------------------
|
|
// Process all objects for the current frame
|
|
#define OBJ_POS_SAMPLE_TIME (1.0f / (((float)MAX_POSITION_HISTORY) * 20.0f)) // super sample at 20 fps
|
|
void ObjDoFrameAll() {
|
|
int i;
|
|
object *objp;
|
|
int objs_live = 0;
|
|
bool update_position_history = false;
|
|
|
|
if (Last_position_history_update_time > Gametime) {
|
|
// the Gametime rolled over (new level)
|
|
update_position_history = true;
|
|
}
|
|
|
|
if (Gametime >= Last_position_history_update_time + OBJ_POS_SAMPLE_TIME) {
|
|
update_position_history = true;
|
|
}
|
|
|
|
if (update_position_history) {
|
|
Object_position_head--;
|
|
if (Object_position_head == 255)
|
|
Object_position_head = MAX_POSITION_HISTORY - 1;
|
|
|
|
Last_position_history_update[Object_position_head] = Gametime;
|
|
Last_position_history_update_time = Gametime;
|
|
}
|
|
|
|
// MT: seed code removed 9/20/99 because it breaks the randomness of the
|
|
// ambient sound patterns.
|
|
// IMPORTANT NOTE: Because this code could have far-reaching consequences,
|
|
// please DO NOT check this in until after the regular patches for D3 are
|
|
// done. This code should only go in for the Mercenary patch, which will
|
|
// get adequate testing to make sure nothing serious has broken.
|
|
//@@ // Seed timer for this frame
|
|
//@@ int seed=Gametime*1000;
|
|
//@@ ps_srand (seed);
|
|
|
|
// Does control for soar objects
|
|
if (Soar_active)
|
|
SoarTick(Frametime);
|
|
|
|
Physics_NumLinked = 0;
|
|
|
|
// Process each object
|
|
for (i = 0, objp = Objects; i <= Highest_object_index; i++, objp++) {
|
|
if ((objp->type != OBJ_NONE) && (!(objp->flags & OF_DEAD))) {
|
|
RTP_STARTINCTIME(obj_do_frm);
|
|
ObjDoFrame(objp);
|
|
|
|
if (update_position_history) {
|
|
int slot = Object_map_position_history[OBJNUM(objp)];
|
|
if (slot != -1) {
|
|
Object_position_samples[slot].pos[Object_position_head] = objp->pos;
|
|
}
|
|
}
|
|
|
|
RTP_ENDINCTIME(obj_do_frm);
|
|
objs_live++;
|
|
}
|
|
}
|
|
|
|
// Account for linked objects
|
|
for (i = 0; i < Physics_NumLinked; i++) {
|
|
RTP_STARTINCTIME(phys_link);
|
|
DoPhysLinkedFrame(&Objects[PhysicsLinkList[i]]);
|
|
RTP_ENDINCTIME(phys_link);
|
|
}
|
|
|
|
// Move vis effects
|
|
{
|
|
RTP_STARTINCTIME(vis_eff_move);
|
|
VisEffectMoveAll();
|
|
RTP_ENDINCTIME(vis_eff_move);
|
|
}
|
|
|
|
// Blend all lights that are needed
|
|
BlendAllLightingEdges();
|
|
|
|
mprintf_at(1, 5, 40, "Objs=%d ", objs_live);
|
|
|
|
// Delete everything that died
|
|
ObjDeleteDead();
|
|
}
|
|
|
|
#define FUELCEN_SOUND_DELAY 0.25 // play every quarter second
|
|
#define FUELCEN_GIVE_RATE 25 // give 25 units per second
|
|
|
|
// Give the player fuel. Called when the player is in an energy center
|
|
void RefuelPlayer(object *objp) {
|
|
float max, amount;
|
|
|
|
ASSERT(objp->type == OBJ_PLAYER);
|
|
ASSERT(Rooms[objp->roomnum].flags & RF_FUELCEN);
|
|
|
|
max = INITIAL_ENERGY - Players[objp->id].energy;
|
|
amount = FUELCEN_GIVE_RATE * Frametime;
|
|
|
|
if (amount > max)
|
|
amount = max;
|
|
|
|
if ((amount > 0)) {
|
|
Players[objp->id].energy += amount;
|
|
|
|
Sound_system.Play3dSound(SOUND_REFUELING, SND_PRIORITY_HIGH, objp);
|
|
|
|
ain_hear hear;
|
|
hear.f_directly_player = true;
|
|
hear.hostile_level = 0.01f;
|
|
hear.curiosity_level = 1.0f;
|
|
hear.max_dist = Sounds[SOUND_REFUELING].max_distance;
|
|
AINotify(objp, AIN_HEAR_NOISE, (void *)&hear);
|
|
|
|
// if (Game_mode & GM_MULTI)
|
|
// multi_send_play_sound(SOUND_REFUEL_STATION_GIVING_FUEL, F1_0/2);
|
|
}
|
|
}
|
|
|
|
// Clears the secret flag in the specified room, and recursively, all connected rooms
|
|
void ClearSecretFlags(room *rp) {
|
|
int p;
|
|
portal *pp;
|
|
|
|
rp->flags &= ~RF_SECRET;
|
|
|
|
for (p = 0, pp = rp->portals; p < rp->num_portals; p++, pp++) {
|
|
room *rp2 = &Rooms[pp->croom];
|
|
if (rp2->flags & RF_SECRET)
|
|
ClearSecretFlags(rp2);
|
|
}
|
|
}
|
|
// Prints out a message when you've found a secret
|
|
#define SECRET_COLOR GR_RGB(0, 128, 255)
|
|
void DoSecretForPlayer(room *rp, object *objp) {
|
|
int y = Game_window_h / 4;
|
|
AddPersistentHUDMessage(SECRET_COLOR, HUD_MSG_PERSISTENT_CENTER, y, 5, HPF_FADEOUT + HPF_FREESPACE_DRAW,
|
|
SOUND_GAME_MESSAGE, TXT_FOUND_SECRET);
|
|
|
|
ClearSecretFlags(rp);
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
// Do refueling centers & damage rooms
|
|
void DoSpecialPlayerStuff(object *objp) {
|
|
if (!OBJECT_OUTSIDE(objp)) {
|
|
room *rp = &Rooms[objp->roomnum];
|
|
|
|
// Check for refueling
|
|
if (rp->flags & RF_FUELCEN)
|
|
RefuelPlayer(objp);
|
|
|
|
if (objp->id == Player_num && (rp->flags & RF_SECRET))
|
|
DoSecretForPlayer(rp, objp);
|
|
|
|
// Check for damage room
|
|
if (rp->damage > 0.0) {
|
|
if (!(objp->effect_info->type_flags & EF_NAPALMED)) {
|
|
|
|
// Set the player on fire
|
|
ASSERT(objp->effect_info);
|
|
objp->effect_info->type_flags |= EF_NAPALMED;
|
|
objp->effect_info->damage_time = 1.0;
|
|
objp->effect_info->damage_per_second = rp->damage;
|
|
if (Gametime - objp->effect_info->last_damage_time > 1.0f)
|
|
objp->effect_info->last_damage_time = 0;
|
|
objp->effect_info->damage_handle = OBJECT_HANDLE_NONE;
|
|
if (objp->effect_info->sound_handle == SOUND_NONE_INDEX)
|
|
objp->effect_info->sound_handle = Sound_system.Play3dSound(SOUND_PLAYER_BURNING, SND_PRIORITY_HIGHEST, objp);
|
|
}
|
|
}
|
|
|
|
// Check for & set waypoint
|
|
if (rp->flags & RF_WAYPOINT)
|
|
SetAutoWaypoint(objp);
|
|
} else { // outside
|
|
|
|
// Check for & set waypoint
|
|
// if (Terrain_seg[CELLNUM(objp->roomnum)].flags & TF_WAYPOINT)
|
|
// SetAutoWaypoint(objp);
|
|
}
|
|
}
|
|
|
|
// Builds the free object list by scanning the list of free objects & adding unused ones to the list
|
|
// Also sets Highest_object_index
|
|
void ResetFreeObjects() {
|
|
int i;
|
|
|
|
Highest_object_index = -1;
|
|
|
|
for (i = Num_objects = MAX_OBJECTS; --i >= 0;)
|
|
if (Objects[i].type == OBJ_NONE)
|
|
free_obj_list[--Num_objects] = i;
|
|
else if (Highest_object_index == -1)
|
|
Highest_object_index = i;
|
|
}
|
|
|
|
// sets the orientation of an object. This should be called to orient an object
|
|
void ObjSetOrient(object *obj, const matrix *orient) {
|
|
// Accounts for if the orientation was set and then this function is being used
|
|
// to update the other stuff
|
|
if (&obj->orient != orient)
|
|
obj->orient = *orient;
|
|
|
|
// Recompute the orientation dependant information
|
|
if (obj->flags & OF_POLYGON_OBJECT) {
|
|
if (obj->type != OBJ_WEAPON && obj->type != OBJ_DEBRIS && obj->type != OBJ_POWERUP && obj->type != OBJ_ROOM) {
|
|
matrix m;
|
|
|
|
m = obj->orient;
|
|
vm_TransposeMatrix(&m);
|
|
|
|
obj->wall_sphere_offset = Poly_models[obj->rtype.pobj_info.model_num].wall_size_offset * m;
|
|
obj->anim_sphere_offset = Poly_models[obj->rtype.pobj_info.model_num].anim_size_offset * m;
|
|
} else {
|
|
obj->wall_sphere_offset = Zero_vector;
|
|
obj->anim_sphere_offset = Zero_vector;
|
|
}
|
|
}
|
|
}
|
|
|
|
// sets the position of an object. This should be called to move an object
|
|
void ObjSetPos(object *obj, vector *pos, int roomnum, matrix *orient, bool f_update_attached_children) {
|
|
int oldroomnum = obj->roomnum;
|
|
vector old_pos = obj->pos;
|
|
|
|
// Reset the position & recalculate the AABB
|
|
obj->pos = *pos;
|
|
ObjSetAABB(obj);
|
|
|
|
// Reset the orientation if changed
|
|
if (orient != NULL)
|
|
ObjSetOrient(obj, orient);
|
|
|
|
// Clear the outside-mine flag
|
|
obj->flags &= ~OF_OUTSIDE_MINE;
|
|
|
|
// If changed rooms, do a bunch of stuff
|
|
if (obj->roomnum != roomnum) {
|
|
|
|
// Let the script know
|
|
tOSIRISEventInfo ei;
|
|
ei.evt_changeseg.room_num = roomnum;
|
|
Osiris_CallEvent(obj, EVT_CHANGESEG, &ei);
|
|
|
|
if (obj->type == OBJ_PLAYER && !ROOMNUM_OUTSIDE(roomnum) && (Rooms[roomnum].flags & RF_INFORM_RELINK_TO_LG)) {
|
|
Level_goals.Inform(LIT_INTERNAL_ROOM, LGF_COMP_ENTER, roomnum);
|
|
}
|
|
|
|
// Relink the object
|
|
ObjRelink(OBJNUM(obj), roomnum);
|
|
|
|
// Call DLL function if the server player changed rooms
|
|
if ((Game_mode & GM_MULTI) && (Netgame.local_role == LR_SERVER)) {
|
|
DLLInfo.me_handle = obj->handle;
|
|
DLLInfo.it_handle = obj->handle;
|
|
DLLInfo.oldseg = oldroomnum;
|
|
DLLInfo.newseg = obj->roomnum;
|
|
|
|
if (obj->type == OBJ_PLAYER) {
|
|
CallGameDLL(EVT_GAMEPLAYERCHANGESEG, &DLLInfo);
|
|
} else {
|
|
CallGameDLL(EVT_GAMEOBJCHANGESEG, &DLLInfo);
|
|
}
|
|
}
|
|
|
|
// Slowly change volume lighting if going between rooms, if not in the editor
|
|
if (GetFunctionMode() != EDITOR_MODE) {
|
|
if ((obj->effect_info != NULL) && (obj->effect_info->type_flags & EF_VOLUME_LIT)) {
|
|
if (!ROOMNUM_OUTSIDE(oldroomnum) && !ROOMNUM_OUTSIDE(roomnum)) {
|
|
if (!(obj->effect_info->type_flags & EF_VOLUME_CHANGING)) {
|
|
obj->effect_info->type_flags |= EF_VOLUME_CHANGING;
|
|
obj->effect_info->volume_change_time = 1.0;
|
|
obj->effect_info->volume_old_room = oldroomnum;
|
|
obj->effect_info->volume_old_pos = old_pos;
|
|
}
|
|
} else // either old or new room was outside, so don't do volume changing
|
|
obj->effect_info->type_flags &= ~EF_VOLUME_CHANGING;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Delete objects, such as weapons & explosions, that shouldn't stay between levels
|
|
// if clear_all is set, clear even proximity bombs
|
|
void ClearTransientObjects(int clear_all) {
|
|
int objnum;
|
|
object *objp;
|
|
|
|
for (objnum = 0, objp = Objects; objnum <= Highest_object_index; objnum++, objp++) {
|
|
if (((objp->type == OBJ_WEAPON) && !(Weapons[objp->id].flags & WF_COUNTERMEASURE)) ||
|
|
(objp->type == OBJ_FIREBALL) || (objp->type == OBJ_DEBRIS) || (objp->type == OBJ_SHARD) ||
|
|
(objp->type == OBJ_SHOCKWAVE) || (objp->type == OBJ_PARTICLE) || (objp->type == OBJ_SPLINTER) ||
|
|
((objp->type != OBJ_NONE) && (objp->flags & OF_DYING))) {
|
|
mprintf(0, "Clearing object %d type = %d\n", objnum, objp->type);
|
|
ObjDelete(objnum);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remaps all static powerups,sounds,etc to their appropriate indices
|
|
void RemapEverything() {
|
|
RemapStaticIDs();
|
|
RemapDoors();
|
|
RemapShips();
|
|
RemapWeapons();
|
|
RemapSounds();
|
|
RemapPolyModels();
|
|
}
|
|
|
|
// Retruns a pointer to an object given its handle. Returns NULL if object no longer exists.
|
|
object *ObjGet(int handle) {
|
|
if (handle == OBJECT_HANDLE_NONE)
|
|
return NULL;
|
|
|
|
if (handle == OBJECT_HANDLE_BAD) {
|
|
Int3();
|
|
return NULL;
|
|
}
|
|
|
|
int objnum = handle & HANDLE_OBJNUM_MASK;
|
|
object *objp;
|
|
|
|
ASSERT((handle & HANDLE_COUNT_MASK) != 0); // count == 0 means never-used object
|
|
ASSERT(objnum < MAX_OBJECTS);
|
|
|
|
objp = &Objects[objnum];
|
|
|
|
if ((objp->type != OBJ_NONE) && (objp->handle == handle))
|
|
return objp;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
void GetObjectPointInWorld(vector *dest, object *obj, int subnum, int vertnum) {
|
|
poly_model *pm = &Poly_models[obj->rtype.pobj_info.model_num];
|
|
bsp_info *sm = &pm->submodel[subnum];
|
|
float normalized_time[MAX_SUBOBJECTS];
|
|
int i;
|
|
|
|
if (!pm->new_style)
|
|
return;
|
|
|
|
for (i = 0; i < MAX_SUBOBJECTS; i++)
|
|
normalized_time[i] = 0.0;
|
|
|
|
SetModelAnglesAndPos(pm, normalized_time);
|
|
|
|
vector pnt = sm->verts[vertnum];
|
|
int mn = subnum;
|
|
matrix m;
|
|
|
|
// Instance up the tree for this gun
|
|
while (mn != -1) {
|
|
vector tpnt;
|
|
|
|
vm_AnglesToMatrix(&m, pm->submodel[mn].angs.p, pm->submodel[mn].angs.h, pm->submodel[mn].angs.b);
|
|
vm_TransposeMatrix(&m);
|
|
|
|
tpnt = pnt * m;
|
|
|
|
pnt = tpnt + pm->submodel[mn].offset + pm->submodel[mn].mod_pos;
|
|
|
|
mn = pm->submodel[mn].parent;
|
|
}
|
|
|
|
// now instance for the entire object
|
|
m = obj->orient;
|
|
vm_TransposeMatrix(&m);
|
|
|
|
*dest = pnt * m;
|
|
*dest += obj->pos;
|
|
}
|
|
|
|
bool ObjGetAnimUpdate(uint16_t objnum, custom_anim *multi_anim_info) {
|
|
object *obj = &Objects[objnum];
|
|
|
|
if ((objnum >= 0) && (obj->type != OBJ_NONE) && (obj->type != OBJ_WEAPON) && (obj->flags & OF_POLYGON_OBJECT)) {
|
|
polyobj_info *pm = &obj->rtype.pobj_info;
|
|
ai_frame *ai_info = obj->ai_info;
|
|
|
|
multi_anim_info->server_time = Gametime;
|
|
multi_anim_info->server_anim_frame = (uint16_t)(pm->anim_frame * 256.0f);
|
|
|
|
multi_anim_info->anim_start_frame = pm->anim_start_frame;
|
|
multi_anim_info->anim_end_frame = pm->anim_end_frame;
|
|
multi_anim_info->anim_time = pm->anim_time;
|
|
multi_anim_info->max_speed = pm->max_speed;
|
|
|
|
multi_anim_info->flags = 0;
|
|
|
|
if (pm->anim_flags & AIAF_LOOPING)
|
|
multi_anim_info->flags |= FMA_LOOPING;
|
|
|
|
if (obj->ai_info != NULL) {
|
|
multi_anim_info->anim_sound_index = obj->ai_info->last_played_sound_index;
|
|
multi_anim_info->flags |= FMA_HAS_AI;
|
|
} else {
|
|
multi_anim_info->anim_sound_index = -1;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void SetObjectControlType(object *obj, int control_type) {
|
|
ASSERT(obj);
|
|
ASSERT(OBJNUM(obj) >= 0 && OBJNUM(obj) < MAX_OBJECTS);
|
|
|
|
if ((control_type == CT_AI) && (obj->ai_info == NULL)) {
|
|
poly_model *pm = &Poly_models[obj->rtype.pobj_info.model_num];
|
|
polyobj_info *p_info = &obj->rtype.pobj_info;
|
|
int num_wbs = pm->num_wbs;
|
|
int count = 0;
|
|
int i;
|
|
|
|
obj->ai_info = (ai_frame *)mem_malloc(sizeof(ai_frame));
|
|
memset(obj->ai_info, 0x00, sizeof(ai_frame)); // DAJ clear the baby
|
|
|
|
for (i = 0; i < num_wbs; i++) {
|
|
ASSERT(pm->poly_wb[i].num_turrets >= 0 && pm->poly_wb[i].num_turrets <= 6400);
|
|
count += pm->poly_wb[i].num_turrets;
|
|
}
|
|
|
|
p_info->multi_turret_info.num_turrets = count;
|
|
|
|
if ((count > 0) && (p_info->multi_turret_info.keyframes == NULL)) {
|
|
int cur = 0;
|
|
|
|
p_info->multi_turret_info.time = 0;
|
|
p_info->multi_turret_info.keyframes = (float *)mem_malloc(sizeof(float) * count);
|
|
p_info->multi_turret_info.last_keyframes = (float *)mem_malloc(sizeof(float) * count);
|
|
p_info->multi_turret_info.flags = 0;
|
|
}
|
|
}
|
|
|
|
obj->control_type = control_type;
|
|
|
|
if (obj->control_type == CT_AI || obj->type == OBJ_PLAYER) {
|
|
poly_model *pm = &Poly_models[obj->rtype.pobj_info.model_num];
|
|
int num_wbs = pm->num_wbs;
|
|
|
|
if (obj->dynamic_wb == NULL) {
|
|
if (obj->type == OBJ_PLAYER) {
|
|
obj->dynamic_wb = (dynamic_wb_info *)mem_malloc(sizeof(dynamic_wb_info) * MAX_WBS_PER_OBJ);
|
|
} else {
|
|
if (num_wbs)
|
|
obj->dynamic_wb = (dynamic_wb_info *)mem_malloc(sizeof(dynamic_wb_info) * num_wbs);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ObjSetAnimUpdate(uint16_t objnum, custom_anim *multi_anim_info) {
|
|
object *obj = &Objects[objnum];
|
|
polyobj_info *pm;
|
|
|
|
if ((objnum >= 0) && (obj->type != OBJ_NONE) && (obj->type != OBJ_WEAPON) && (obj->flags & OF_POLYGON_OBJECT)) {
|
|
pm = &obj->rtype.pobj_info;
|
|
pm->multi_anim_info = *multi_anim_info;
|
|
|
|
// Make sure we mark it as current and
|
|
pm->multi_anim_info.flags |= FMA_VALID;
|
|
pm->multi_anim_info.flags &= ~FMA_CURRENT;
|
|
}
|
|
}
|
|
|
|
void ObjGetTurretUpdate(uint16_t objnum, multi_turret *multi_turret_info) {
|
|
object *obj = &Objects[objnum];
|
|
poly_model *pm = &Poly_models[obj->rtype.pobj_info.model_num];
|
|
polyobj_info *p_info = &obj->rtype.pobj_info;
|
|
|
|
if ((objnum >= 0) && (obj->type != OBJ_NONE) && (obj->type != OBJ_WEAPON) && (obj->flags & OF_POLYGON_OBJECT) &&
|
|
(p_info->multi_turret_info.num_turrets)) {
|
|
int i;
|
|
int j;
|
|
int count = 0;
|
|
|
|
multi_turret_info->time = Gametime;
|
|
multi_turret_info->num_turrets = p_info->multi_turret_info.num_turrets;
|
|
|
|
for (i = 0; i < pm->num_wbs; i++) {
|
|
for (j = 0; j < pm->poly_wb[i].num_turrets; j++) {
|
|
multi_turret_info->keyframes[count++] = obj->dynamic_wb[i].norm_turret_angle[j];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ObjSetTurretUpdate(uint16_t objnum, multi_turret *multi_turret_info) {
|
|
object *obj = &Objects[objnum];
|
|
poly_model *pm = &Poly_models[obj->rtype.pobj_info.model_num];
|
|
polyobj_info *p_info = &obj->rtype.pobj_info;
|
|
|
|
if ((objnum >= 0) && (obj->type != OBJ_NONE) && (obj->type != OBJ_WEAPON) && (obj->flags & OF_POLYGON_OBJECT) &&
|
|
(p_info->multi_turret_info.num_turrets)) {
|
|
int i;
|
|
|
|
p_info->multi_turret_info.time = multi_turret_info->time;
|
|
|
|
for (i = 0; i < p_info->multi_turret_info.num_turrets; i++) {
|
|
p_info->multi_turret_info.keyframes[i] = multi_turret_info->keyframes[i];
|
|
}
|
|
|
|
p_info->multi_turret_info.flags = FMT_NEW_DATA;
|
|
} else {
|
|
mprintf(0, "Woops, no turret here to update!\n");
|
|
}
|
|
}
|
|
|
|
// Returns the original parent for the given object. Returns self if it has no parent
|
|
object *ObjGetUltimateParent(object *child) {
|
|
ASSERT(child);
|
|
|
|
object *ret = child;
|
|
int handle;
|
|
|
|
handle = child->parent_handle;
|
|
|
|
while (ObjGet(handle)) {
|
|
ret = ObjGet(handle);
|
|
handle = ret->parent_handle;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// Sets an object to a type OBJ_DUMMY (saves it's old type) so it won't be renderered, etc, but still alive
|
|
void ObjGhostObject(int objnum) {
|
|
ASSERT(objnum >= 0 && objnum < MAX_OBJECTS);
|
|
if (objnum < 0 || objnum >= MAX_OBJECTS)
|
|
return;
|
|
|
|
object *obj = &Objects[objnum];
|
|
|
|
ASSERT(obj->type != OBJ_NONE);
|
|
ASSERT(obj->type != OBJ_DUMMY); // Don't ghost a ghosted object!!
|
|
ASSERT(obj->type != OBJ_PLAYER);
|
|
ASSERT(obj->type != OBJ_GHOST);
|
|
|
|
if (obj->type == OBJ_NONE || obj->type == OBJ_DUMMY || obj->type == OBJ_PLAYER || obj->type == OBJ_GHOST)
|
|
return;
|
|
|
|
obj->dummy_type = obj->type;
|
|
obj->type = OBJ_DUMMY;
|
|
}
|
|
|
|
// Restores a ghosted object back to it's old type
|
|
void ObjUnGhostObject(int objnum) {
|
|
ASSERT(objnum >= 0 && objnum < MAX_OBJECTS);
|
|
if (objnum < 0 || objnum >= MAX_OBJECTS)
|
|
return;
|
|
|
|
object *obj = &Objects[objnum];
|
|
|
|
ASSERT(obj->type == OBJ_DUMMY);
|
|
|
|
if (obj->type != OBJ_DUMMY)
|
|
return;
|
|
|
|
//@@ASSERT (!(obj->flags&OF_INPLAYERINVENTORY));
|
|
//@@if(obj->flags&OF_INPLAYERINVENTORY)
|
|
//@@ return;
|
|
if (obj->flags & OF_INPLAYERINVENTORY) {
|
|
mprintf(0, "UnGhosting Object in that is currently in a player's inventory!\n");
|
|
}
|
|
|
|
obj->type = obj->dummy_type;
|
|
obj->dummy_type = OBJ_NONE;
|
|
}
|