From ff569bd2ae2a849a1145be4a9479246b6cd8d0e3 Mon Sep 17 00:00:00 2001
From: loop <126372784+tribes2@users.noreply.github.com>
Date: Sun, 7 Jan 2024 04:36:33 +0000
Subject: [PATCH] t2 engine svn checkout
---
Engine.dsp | 3133 +++++++
Engine.plg | 34 +
Makefile | 34 +
ai/aiConnection.cc | 2975 ++++++
ai/aiConnection.h | 403 +
ai/aiConsole.cc | 766 ++
ai/aiDebug.cc | 246 +
ai/aiMath.cc | 341 +
ai/aiMath.h | 349 +
ai/aiNavJetting.cc | 485 +
ai/aiNavJetting.h | 79 +
ai/aiNavStep.cc | 28 +
ai/aiNavStep.h | 32 +
ai/aiObjective.cc | 183 +
ai/aiObjective.h | 105 +
ai/aiPlayer.cc | 446 +
ai/aiPlayer.h | 92 +
ai/aiStep.cc | 805 ++
ai/aiStep.h | 143 +
ai/aiTask.cc | 235 +
ai/aiTask.h | 55 +
ai/graph.cc | 1528 +++
ai/graph.h | 341 +
ai/graphBSP.h | 220 +
ai/graphBase.cc | 388 +
ai/graphBase.h | 228 +
ai/graphBridge.cc | 974 ++
ai/graphBridge.h | 96 +
ai/graphBuildLOS.cc | 208 +
ai/graphConjoin.cc | 660 ++
ai/graphData.cc | 1283 +++
ai/graphData.h | 531 ++
ai/graphDebug.cc | 117 +
ai/graphDefines.h | 102 +
ai/graphDijkstra.cc | 377 +
ai/graphFind.cc | 562 ++
ai/graphFloorPlan.cc | 2392 +++++
ai/graphFloorPlan.h | 231 +
ai/graphFloorRender.cc | 761 ++
ai/graphForceField.cc | 162 +
ai/graphForceField.h | 45 +
ai/graphGenUtils.cc | 212 +
ai/graphGenUtils.h | 304 +
ai/graphGroundPlan.cc | 1067 +++
ai/graphGroundPlan.h | 185 +
ai/graphGroundVisit.h | 93 +
ai/graphIndoors.cc | 267 +
ai/graphIsland.cc | 95 +
ai/graphJetting.cc | 485 +
ai/graphJetting.h | 69 +
ai/graphLOS.cc | 225 +
ai/graphLOS.h | 32 +
ai/graphLocate.cc | 529 ++
ai/graphLocate.h | 76 +
ai/graphMake.cc | 363 +
ai/graphMath.cc | 341 +
ai/graphMath.h | 349 +
ai/graphNodes.h | 93 +
ai/graphOutdoors.cc | 530 ++
ai/graphPartition.cc | 127 +
ai/graphPartition.h | 56 +
ai/graphPath.cc | 831 ++
ai/graphPath.h | 133 +
ai/graphQueries.cc | 148 +
ai/graphRender.cc | 420 +
ai/graphSearchLOS.cc | 331 +
ai/graphSearches.h | 170 +
ai/graphSmooth.cc | 270 +
ai/graphSpawn.cc | 372 +
ai/graphThreats.cc | 245 +
ai/graphThreats.h | 56 +
ai/graphTransient.cc | 318 +
ai/graphTransient.h | 49 +
ai/graphVolume.cc | 387 +
ai/oVector.h | 195 +
ai/tBinHeap.h | 415 +
ai/texturePreload.h | 23 +
audio/audio.cc | 3372 +++++++
audio/audio.h | 21 +
audio/audioBuffer.cc | 119 +
audio/audioBuffer.h | 47 +
audio/audioCodec.cc | 311 +
audio/audioCodec.h | 147 +
audio/audioCodecGSM.cc | 195 +
audio/audioCodecGSM.h | 52 +
audio/audioCodecMiles.cc | 276 +
audio/audioCodecMiles.h | 87 +
audio/audioDataBlock.cc | 512 +
audio/audioDataBlock.h | 132 +
audio/audioFunctions.cc | 773 ++
audio/audioMss.cc | 352 +
audio/audioMss.h | 128 +
audio/audioNet.cc | 197 +
audio/audioNet.h | 65 +
audio/audioThread.cc | 218 +
audio/audioThread.h | 90 +
audio/bufferQueue.cc | 111 +
audio/bufferQueue.h | 106 +
collision/abstractPolyList.cc | 99 +
collision/abstractPolyList.h | 118 +
collision/boxConvex.cc | 201 +
collision/boxConvex.h | 48 +
collision/clippedPolyList.cc | 277 +
collision/clippedPolyList.h | 81 +
collision/collision.h | 66 +
collision/concretePolyList.cc | 150 +
collision/concretePolyList.h | 61 +
collision/convex.cc | 936 ++
collision/convex.h | 173 +
collision/depthSortList.cc | 900 ++
collision/depthSortList.h | 110 +
collision/earlyOutPolyList.cc | 269 +
collision/earlyOutPolyList.h | 73 +
collision/extrudedPolyList.cc | 497 +
collision/extrudedPolyList.h | 103 +
collision/gjk.cc | 500 +
collision/gjk.h | 72 +
collision/planeExtractor.cc | 119 +
collision/planeExtractor.h | 60 +
collision/polyhedron.cc | 152 +
collision/polyhedron.h | 47 +
collision/polytope.cc | 435 +
collision/polytope.h | 98 +
console/ast.h | 439 +
console/compiledEval.cc | 1222 +++
console/compiler.cc | 2609 ++++++
console/compiler.h | 246 +
console/console.cc | 688 ++
console/console.h | 199 +
console/consoleDoc.cc | 136 +
console/consoleFunctions.cc | 1110 +++
console/consoleInternal.cc | 943 ++
console/consoleInternal.h | 280 +
console/consoleObject.cc | 191 +
console/consoleObject.h | 334 +
console/consoleTypes.cc | 429 +
console/consoleTypes.h | 71 +
console/gram.cc | 2439 +++++
console/gram.h | 84 +
console/gram.y | 493 +
console/objectTypes.h | 79 +
console/scan.cc | 1560 ++++
console/scan.l | 405 +
console/scriptObject.cc | 64 +
console/simBase.cc | 1527 +++
console/simBase.h | 599 ++
console/simDictionary.cc | 247 +
console/simDictionary.h | 84 +
console/simManager.cc | 449 +
console/telnetConsole.cc | 256 +
console/telnetConsole.h | 60 +
console/telnetDebugger.cc | 557 ++
console/telnetDebugger.h | 90 +
console/typeValidators.cc | 72 +
console/typeValidators.h | 72 +
console/yylex.c | 627 ++
console/yyparse.c | 918 ++
core/bitMatrix.h | 137 +
core/bitRender.cc | 834 ++
core/bitRender.h | 29 +
core/bitSet.h | 52 +
core/bitStream.cc | 916 ++
core/bitStream.h | 175 +
core/bitTables.cc | 27 +
core/bitTables.h | 38 +
core/bitVector.h | 155 +
core/bitVectorW.h | 92 +
core/color.h | 442 +
core/coreRes.h | 66 +
core/crc.cc | 74 +
core/crc.h | 19 +
core/dataChunker.cc | 61 +
core/dataChunker.h | 53 +
core/dnet.cc | 260 +
core/dnet.h | 56 +
core/fileObject.cc | 156 +
core/fileObject.h | 45 +
core/fileStream.cc | 504 +
core/fileStream.h | 88 +
core/fileio.h | 68 +
core/filterStream.cc | 63 +
core/filterStream.h | 41 +
core/findMatch.cc | 114 +
core/findMatch.h | 36 +
core/idGenerator.cc | 21 +
core/idGenerator.h | 78 +
core/llist.h | 308 +
core/memStream.cc | 159 +
core/memstream.h | 47 +
core/nStream.cc | 155 +
core/nTypes.cc | 60 +
core/polyList.h | 56 +
core/realComp.h | 29 +
core/resDictionary.cc | 130 +
core/resManager.cc | 970 ++
core/resManager.h | 323 +
core/resizeStream.cc | 141 +
core/resizeStream.h | 49 +
core/stream.h | 154 +
core/stringTable.cc | 206 +
core/stringTable.h | 60 +
core/tAlgorithm.h | 21 +
core/tSortedSceneObjectList.cc | 136 +
core/tSortedSceneObjectList.h | 53 +
core/tSparseArray.h | 135 +
core/tVector.cc | 77 +
core/tVector.h | 642 ++
core/tagDictionary.cc | 305 +
core/tagDictionary.h | 68 +
core/zipAggregate.cc | 212 +
core/zipAggregate.h | 69 +
core/zipHeaders.cc | 120 +
core/zipHeaders.h | 211 +
core/zipSubStream.cc | 401 +
core/zipSubStream.h | 98 +
crypt/cryptMGF.cc | 44 +
crypt/cryptMGF.h | 18 +
crypt/cryptRandPool.cc | 102 +
crypt/cryptRandPool.h | 39 +
crypt/cryptSHA1.cc | 229 +
crypt/cryptSHA1.h | 47 +
dgl/bitmapBm8.cc | 52 +
dgl/bitmapBmp.cc | 205 +
dgl/bitmapGif.cc | 172 +
dgl/bitmapJpeg.cc | 189 +
dgl/bitmapPng.cc | 415 +
dgl/dgl.cc | 746 ++
dgl/dgl.h | 120 +
dgl/dglMatrix.cc | 146 +
dgl/gBitmap.cc | 771 ++
dgl/gBitmap.h | 221 +
dgl/gChunkedTexManager.h | 76 +
dgl/gFont.cc | 470 +
dgl/gFont.h | 156 +
dgl/gPalette.cc | 148 +
dgl/gPalette.h | 101 +
dgl/gTexManager.cc | 1468 +++
dgl/gTexManager.h | 285 +
dgl/lensFlare.cc | 99 +
dgl/lensFlare.h | 67 +
dgl/materialList.cc | 305 +
dgl/materialList.h | 103 +
dgl/materialPropertyMap.cc | 189 +
dgl/materialPropertyMap.h | 75 +
dgl/rectClipper.cc | 147 +
dgl/rectClipper.h | 57 +
dgl/splineUtil.cc | 165 +
dgl/splineUtil.h | 59 +
dgl/stripCache.cc | 59 +
dgl/stripCache.h | 35 +
editor/compTest.cc | 537 ++
editor/creator.cc | 465 +
editor/creator.h | 131 +
editor/editTSCtrl.cc | 583 ++
editor/editTSCtrl.h | 100 +
editor/editor.cc | 116 +
editor/editor.h | 46 +
editor/editorButtonCtrl.cc | 119 +
editor/editorButtonCtrl.h | 45 +
editor/editorCheckboxCtrl.cc | 118 +
editor/editorCheckboxCtrl.h | 44 +
editor/guiTerrPreviewCtrl.cc | 324 +
editor/guiTerrPreviewCtrl.h | 61 +
editor/missionAreaEditor.cc | 1103 +++
editor/missionAreaEditor.h | 148 +
editor/terraformer.cc | 1371 +++
editor/terraformer.h | 232 +
editor/terraformerNoise.cc | 325 +
editor/terraformerNoise.h | 60 +
editor/terraformerTexture.cc | 415 +
editor/terrainActions.cc | 454 +
editor/terrainActions.h | 233 +
editor/terrainEditor.cc | 1703 ++++
editor/terrainEditor.h | 313 +
editor/worldEditor.cc | 2479 +++++
editor/worldEditor.h | 375 +
engine.overview.txt | 891 ++
game/RCa03592 | Bin 0 -> 7160 bytes
game/RDa03592 | Bin 0 -> 76328 bytes
game/aiConnection.cc | 198 +
game/aiConnection.h | 41 +
game/aiCore.cc | 75 +
game/aiCore.h | 53 +
game/aiPlayer.cc | 449 +
game/aiPlayer.h | 93 +
game/ambientAudioManager.cc | 262 +
game/ambientAudioManager.h | 53 +
game/audioEmitter.cc | 884 ++
game/audioEmitter.h | 150 +
game/auth.h | 54 +
game/badWordFilter.cc | 217 +
game/badWordFilter.h | 54 +
game/banList.cc | 188 +
game/banList.h | 48 +
game/bombSight.cc | 127 +
game/bombSight.h | 48 +
game/camera.cc | 645 ++
game/camera.h | 115 +
game/cameraFXMgr.cc | 155 +
game/cameraFXMgr.h | 91 +
game/collisionTest.cc | 216 +
game/collisionTest.h | 75 +
game/commanderMapIcon.cc | 225 +
game/commanderMapIcon.h | 87 +
game/debris.cc | 979 ++
game/debris.h | 155 +
game/debugView.cc | 212 +
game/debugView.h | 59 +
game/demoGame.h | 35 +
game/explosion.cc | 1061 +++
game/explosion.h | 166 +
game/fireballAtmosphere.cc | 331 +
game/fireballAtmosphere.h | 90 +
game/flyingVehicle.cc | 748 ++
game/flyingVehicle.h | 183 +
game/forceFieldBare.cc | 854 ++
game/forceFieldBare.h | 153 +
game/fps/guiClockHud.cc | 112 +
game/fps/guiCrossHairHud.cc | 69 +
game/fps/guiHealthBarHud.cc | 118 +
game/fps/guiShapeNameHud.cc | 260 +
game/fps/shapeNameHud.cc | 198 +
game/fps/shapeNameHud.h | 45 +
game/fx/cameraFXMgr.cc | 156 +
game/fx/cameraFXMgr.h | 91 +
game/fx/explosion.cc | 1022 ++
game/fx/explosion.h | 166 +
game/fx/lightning.cc | 1228 +++
game/fx/lightning.h | 216 +
game/fx/particleEmitter.cc | 226 +
game/fx/particleEmitter.h | 82 +
game/fx/particleEngine.cc | 1477 +++
game/fx/particleEngine.h | 178 +
game/fx/precipitation.cc | 976 ++
game/fx/precipitation.h | 193 +
game/fx/splash.cc | 834 ++
game/fx/splash.h | 185 +
game/fx/underLava.cc | 144 +
game/fx/underLava.h | 58 +
game/game.cc | 1210 +++
game/game.h | 29 +
game/gameBase.cc | 739 ++
game/gameBase.h | 266 +
game/gameConnection.cc | 1635 ++++
game/gameConnection.h | 265 +
game/gameConnectionEvents.cc | 301 +
game/gameConnectionEvents.h | 288 +
game/gameConnectionMoves.cc | 366 +
game/gameFunctions.cc | 76 +
game/gameFunctions.h | 13 +
game/gameProcess.cc | 248 +
game/gameTSCtrl.cc | 394 +
game/gameTSCtrl.h | 76 +
game/guiNoMouseCtrl.cc | 19 +
game/guiPlayerView.cc | 336 +
game/guiPlayerView.h | 95 +
game/guiServerBrowser.cc | 904 ++
game/guiServerBrowser.h | 89 +
game/hoverVehicle.cc | 974 ++
game/hoverVehicle.h | 200 +
game/httpObject.cc | 319 +
game/httpObject.h | 68 +
game/item.cc | 1094 +++
game/item.h | 160 +
game/lightning.cc | 1228 +++
game/lightning.h | 216 +
game/linearProjectile.cc | 1280 +++
game/linearProjectile.h | 181 +
game/main.cc | 713 ++
game/missionArea.cc | 143 +
game/missionArea.h | 55 +
game/missionMarker.cc | 281 +
game/missionMarker.h | 163 +
game/motionBlurLine.cc | 213 +
game/motionBlurLine.h | 80 +
game/moveManager.h | 67 +
game/net.cc | 289 +
game/net/httpObject.cc | 320 +
game/net/httpObject.h | 68 +
game/net/net.cc | 288 +
game/net/netDispatch.cc | 1062 +++
game/net/netDispatch.h | 51 +
game/net/netTest.cc | 93 +
game/net/serverQuery.cc | 2023 ++++
game/net/serverQuery.h | 129 +
game/net/tcpObject.cc | 318 +
game/net/tcpObject.h | 72 +
game/netDispatch.cc | 980 ++
game/netDispatch.h | 58 +
game/netTest.cc | 93 +
game/objectTypes.h | 78 +
game/particleEmitter.cc | 226 +
game/particleEmitter.h | 82 +
game/particleEngine.cc | 1477 +++
game/particleEngine.h | 178 +
game/physicalZone.cc | 345 +
game/physicalZone.h | 76 +
game/platTest.cc | 710 ++
game/player.cc | 4383 +++++++++
game/player.h | 489 +
game/precipitation.cc | 980 ++
game/precipitation.h | 189 +
game/projBomb.cc | 405 +
game/projBomb.h | 88 +
game/projELF.cc | 1011 ++
game/projELF.h | 201 +
game/projEnergy.cc | 809 ++
game/projEnergy.h | 98 +
game/projFlareGrenade.cc | 432 +
game/projFlareGrenade.h | 123 +
game/projGrenade.cc | 933 ++
game/projGrenade.h | 142 +
game/projLinearFlare.cc | 469 +
game/projLinearFlare.h | 119 +
game/projRepair.cc | 674 ++
game/projRepair.h | 173 +
game/projSeeker.cc | 1600 ++++
game/projSeeker.h | 205 +
game/projShockLance.cc | 1179 +++
game/projShockLance.h | 205 +
game/projSniper.cc | 933 ++
game/projSniper.h | 167 +
game/projTargeting.cc | 914 ++
game/projTargeting.h | 158 +
game/projTracer.cc | 406 +
game/projTracer.h | 94 +
game/projectile.cc | 924 ++
game/projectile.h | 235 +
game/resource.h | 20 +
game/rigid.cc | 262 +
game/rigid.h | 87 +
game/scopeAlwaysShape.cc | 31 +
game/sensor.cc | 80 +
game/sensor.h | 47 +
game/serverQuery.cc | 1767 ++++
game/serverQuery.h | 113 +
game/shadow.cc | 562 ++
game/shadow.h | 143 +
game/shapeBase.cc | 5025 ++++++++++
game/shapeBase.h | 984 ++
game/shapeCollision.cc | 44 +
game/shapeImage.cc | 1918 ++++
game/shieldImpact.cc | 238 +
game/shieldImpact.h | 78 +
game/shockwave.cc | 963 ++
game/shockwave.h | 141 +
game/showTSShape.cc | 1176 +++
game/showTSShape.h | 129 +
game/sphere.cc | 235 +
game/sphere.h | 67 +
game/splash.cc | 834 ++
game/splash.h | 185 +
game/staticShape.cc | 224 +
game/staticShape.h | 78 +
game/stationFXPersonal.cc | 418 +
game/stationFXPersonal.h | 100 +
game/stationFXVehicle.cc | 697 ++
game/stationFXVehicle.h | 122 +
game/targetManager.cc | 2877 ++++++
game/targetManager.h | 367 +
game/tcpObject.cc | 317 +
game/tcpObject.h | 72 +
game/tribes2.aps | Bin 0 -> 672 bytes
game/tribes2.ico | Bin 0 -> 8422 bytes
game/tribes2.rc | 140 +
game/tribesGame.h | 35 +
game/trigger.cc | 614 ++
game/trigger.h | 105 +
game/tsStatic.cc | 556 ++
game/tsStatic.h | 108 +
game/turret.cc | 1580 ++++
game/turret.h | 266 +
game/underLava.cc | 143 +
game/underLava.h | 58 +
game/v12.rc | 140 +
game/v12Game.h | 35 +
game/vehicle.cc | 1730 ++++
game/vehicle.h | 259 +
game/vehicleBlocker.cc | 136 +
game/vehicleBlocker.h | 47 +
game/vehicles/flyingVehicle.cc | 748 ++
game/vehicles/flyingVehicle.h | 187 +
game/vehicles/hoverVehicle.cc | 974 ++
game/vehicles/hoverVehicle.h | 200 +
game/vehicles/vehicle.cc | 1728 ++++
game/vehicles/vehicle.h | 259 +
game/vehicles/vehicleBlocker.cc | 136 +
game/vehicles/vehicleBlocker.h | 47 +
game/vehicles/wheeledVehicle.cc | 1287 +++
game/vehicles/wheeledVehicle.h | 189 +
game/version.cc | 18 +
game/version.h | 13 +
game/weaponBeam.cc | 76 +
game/weaponBeam.h | 68 +
game/wheeledVehicle.cc | 1287 +++
game/wheeledVehicle.h | 185 +
gui/channelVector.cc | 283 +
gui/channelVector.h | 202 +
gui/guiArrayCtrl.cc | 488 +
gui/guiArrayCtrl.h | 76 +
gui/guiAviBitmapCtrl.cc | 1387 +++
gui/guiAviBitmapCtrl.h | 190 +
gui/guiBackgroundCtrl.cc | 26 +
gui/guiBackgroundCtrl.h | 30 +
gui/guiBitmapBorderCtrl.cc | 120 +
gui/guiBitmapCtrl.cc | 148 +
gui/guiBitmapCtrl.h | 50 +
gui/guiBorderButton.cc | 42 +
gui/guiBubbleTextCtrl.cc | 73 +
gui/guiBubbleTextCtrl.h | 39 +
gui/guiButtonBaseCtrl.cc | 189 +
gui/guiButtonBaseCtrl.h | 42 +
gui/guiButtonCtrl.cc | 262 +
gui/guiButtonCtrl.h | 54 +
gui/guiCanvas.cc | 1260 +++
gui/guiCanvas.h | 147 +
gui/guiChannelVectorCtrl.cc | 300 +
gui/guiChannelVectorCtrl.h | 35 +
gui/guiChatMenuTreeCtrl.cc | 262 +
gui/guiChatMenuTreeCtrl.h | 60 +
gui/guiCheckBoxCtrl.cc | 233 +
gui/guiCheckBoxCtrl.h | 44 +
gui/guiChunkedBitmapCtrl.cc | 127 +
gui/guiConsole.cc | 102 +
gui/guiConsole.h | 34 +
gui/guiConsoleEditCtrl.cc | 97 +
gui/guiConsoleEditCtrl.h | 36 +
gui/guiConsoleTextCtrl.cc | 160 +
gui/guiConsoleTextCtrl.h | 61 +
gui/guiControl.cc | 1276 +++
gui/guiControl.h | 243 +
gui/guiControlListPopup.cc | 40 +
gui/guiDebugger.cc | 1462 +++
gui/guiDebugger.h | 85 +
gui/guiDefaultControlRender.cc | 51 +
gui/guiDefaultControlRender.h | 13 +
gui/guiEditCtrl.cc | 775 ++
gui/guiEditCtrl.h | 79 +
gui/guiFilterCtrl.cc | 251 +
gui/guiFilterCtrl.h | 77 +
gui/guiFrameCtrl.cc | 1048 +++
gui/guiFrameCtrl.h | 182 +
gui/guiHelpCtrl.cc | 99 +
gui/guiHelpCtrl.h | 40 +
gui/guiInputCtrl.cc | 94 +
gui/guiInputCtrl.h | 32 +
gui/guiInspector.cc | 491 +
gui/guiInspector.h | 44 +
gui/guiMLTextCtrl.cc | 1990 ++++
gui/guiMLTextCtrl.h | 257 +
gui/guiMLTextEditCtrl.cc | 365 +
gui/guiMLTextEditCtrl.h | 44 +
gui/guiMenuBar.cc | 693 ++
gui/guiMenuBar.h | 125 +
gui/guiMessageVectorCtrl.cc | 840 ++
gui/guiMessageVectorCtrl.h | 130 +
gui/guiMouseEventCtrl.cc | 100 +
gui/guiMouseEventCtrl.h | 49 +
gui/guiPopUpCtrl.cc | 929 ++
gui/guiPopUpCtrl.h | 139 +
gui/guiProgressCtrl.cc | 84 +
gui/guiProgressCtrl.h | 35 +
gui/guiRadioCtrl.cc | 228 +
gui/guiRadioCtrl.h | 47 +
gui/guiScrollCtrl.cc | 956 ++
gui/guiScrollCtrl.h | 238 +
gui/guiSliderCtrl.cc | 256 +
gui/guiSliderCtrl.h | 51 +
gui/guiTSControl.cc | 130 +
gui/guiTSControl.h | 61 +
gui/guiTextCtrl.cc | 191 +
gui/guiTextCtrl.h | 63 +
gui/guiTextEditCtrl.cc | 1110 +++
gui/guiTextEditCtrl.h | 99 +
gui/guiTextEditSliderCtrl.cc | 280 +
gui/guiTextEditSliderCtrl.h | 70 +
gui/guiTextListCtrl.cc | 584 ++
gui/guiTextListCtrl.h | 93 +
gui/guiTreeViewCtrl.cc | 1473 +++
gui/guiTreeViewCtrl.h | 253 +
gui/guiTypes.cc | 233 +
gui/guiTypes.h | 148 +
gui/guiVoteCtrl.cc | 231 +
gui/guiVoteCtrl.h | 72 +
gui/guiWindowCtrl.cc | 582 ++
gui/guiWindowCtrl.h | 109 +
gui/messageVector.cc | 408 +
gui/messageVector.h | 108 +
hud/hudBarBaseCtrl.cc | 109 +
hud/hudBarBaseCtrl.h | 66 +
hud/hudBarDisplayCtrl.cc | 170 +
hud/hudBarDisplayCtrl.h | 50 +
hud/hudBezierDisplayCtrl.cc | 179 +
hud/hudBezierDisplayCtrl.h | 50 +
hud/hudBitmapCtrl.cc | 121 +
hud/hudBitmapCtrl.h | 58 +
hud/hudBitmapFrameCtrl.cc | 31 +
hud/hudBitmapFrameCtrl.h | 35 +
hud/hudClock.cc | 104 +
hud/hudClockCtrl.cc | 116 +
hud/hudCompass.cc | 194 +
hud/hudCrosshair.cc | 63 +
hud/hudCtrl.cc | 42 +
hud/hudCtrl.h | 37 +
hud/hudDamageCtrl.cc | 102 +
hud/hudEnergyCtrl.cc | 67 +
hud/hudEnergyDamage.cc | 296 +
hud/hudGLEx.cc | 311 +
hud/hudGLEx.h | 35 +
hud/hudHealthCtrl.cc | 105 +
hud/hudHeat.cc | 86 +
hud/hudObject.cc | 81 +
hud/hudObject.h | 51 +
hud/hudZoom.cc | 83 +
hud/mBezier2D.cc | 225 +
hud/mBezier2D.h | 50 +
interior/floorPlanRes.cc | 123 +
interior/floorPlanRes.h | 52 +
interior/forceField.cc | 472 +
interior/forceField.h | 182 +
interior/interior.cc | 2173 +++++
interior/interior.h | 859 ++
interior/interiorCollision.cc | 1623 ++++
interior/interiorDebug.cc | 803 ++
interior/interiorIO.cc | 1128 +++
interior/interiorInstance.cc | 1965 ++++
interior/interiorInstance.h | 293 +
interior/interiorLMManager.cc | 549 ++
interior/interiorLMManager.h | 90 +
interior/interiorLightAnim.cc | 404 +
interior/interiorRender.cc | 1330 +++
interior/interiorRes.cc | 320 +
interior/interiorRes.h | 152 +
interior/interiorResObjects.cc | 200 +
interior/interiorResObjects.h | 115 +
interior/interiorSubObject.cc | 93 +
interior/interiorSubObject.h | 65 +
interior/itf.h | 104 +
interior/itfdump.asm | 657 ++
interior/itfdump.cc | 311 +
interior/itfdump_c.cc | 311 +
interior/lightUpdateGrouper.cc | 77 +
interior/lightUpdateGrouper.h | 108 +
interior/mirrorSubObject.cc | 532 ++
interior/mirrorSubObject.h | 87 +
math/mBezier2D.cc | 223 +
math/mBezier2D.h | 50 +
math/mBox.cc | 163 +
math/mBox.h | 222 +
math/mConsoleFunctions.cc | 150 +
math/mConstants.h | 20 +
math/mMath.h | 41 +
math/mMathAMD.cc | 225 +
math/mMathAMD_ASM.asm | 163 +
math/mMathFn.cc | 21 +
math/mMathFn.h | 445 +
math/mMathSSE.cc | 113 +
math/mMathSSE_ASM.asm | 114 +
math/mMath_ASM.asm | 267 +
math/mMath_C.cc | 815 ++
math/mMatrix.cc | 139 +
math/mMatrix.h | 363 +
math/mPlane.h | 292 +
math/mPlaneTransformer.cc | 54 +
math/mPlaneTransformer.h | 35 +
math/mPoint.h | 1611 ++++
math/mQuadPatch.cc | 61 +
math/mQuadPatch.h | 47 +
math/mQuat.cc | 347 +
math/mQuat.h | 331 +
math/mRandom.cc | 158 +
math/mRandom.h | 121 +
math/mRect.h | 310 +
math/mSolver.cc | 243 +
math/mSphere.h | 72 +
math/mSplinePatch.cc | 99 +
math/mSplinePatch.h | 70 +
math/mTrig.h | 43 +
math/mathIO.h | 233 +
math/mathTypes.cc | 546 ++
math/mathTypes.h | 14 +
math/mathUtils.cc | 103 +
math/mathUtils.h | 25 +
platform/3Dfx.h | 111 +
platform/event.h | 373 +
platform/gameInterface.cc | 239 +
platform/gameInterface.h | 74 +
platform/platform.h | 495 +
platform/platformAL.h | 162 +
platform/platformAssert.cc | 115 +
platform/platformAssert.h | 77 +
platform/platformAudio.h | 171 +
platform/platformCPU.cc | 192 +
platform/platformCPUInfo.asm | 96 +
platform/platformInput.h | 111 +
platform/platformMemory.cc | 1146 +++
platform/platformMutex.h | 19 +
platform/platformRedBook.cc | 267 +
platform/platformRedBook.h | 74 +
platform/platformSemaphore.h | 26 +
platform/platformThread.h | 34 +
platform/platformVideo.cc | 659 ++
platform/platformVideo.h | 159 +
platform/profiler.cc | 538 ++
platform/profiler.h | 103 +
platform/types.h | 153 +
platform/typesLinux.h | 63 +
platform/typesPPC.h | 81 +
platform/typesWin32.h | 98 +
platform/typesX86UNIX.h | 65 +
platformLinux/async.c | 350 +
platformLinux/async.h | 34 +
platformLinux/audio.cc | 3574 +++++++
platformLinux/blender.asm | 1126 +++
platformLinux/fixcalls.pl | 8 +
platformLinux/linuxAL.cc | 335 +
platformLinux/linuxALStub.cc | 246 +
platformLinux/linuxAsmBlit.cc | 327 +
platformLinux/linuxCPUInfo.cc | 227 +
platformLinux/linuxCPUInfo_ASM.asm | 64 +
platformLinux/linuxCodeMap.cc | 249 +
platformLinux/linuxConsole.cc | 573 ++
platformLinux/linuxConsole.h | 80 +
platformLinux/linuxFileio.cc | 853 ++
platformLinux/linuxFont.cc | 209 +
platformLinux/linuxGL.cc | 2409 +++++
platformLinux/linuxIO.cc | 24 +
platformLinux/linuxInput.cc | 537 ++
platformLinux/linuxMath.cc | 459 +
platformLinux/linuxMath_ASM.asm | 1014 ++
platformLinux/linuxMath_VC.c | 1145 +++
platformLinux/linuxMemory.cc | 41 +
platformLinux/linuxMutex.cc | 58 +
platformLinux/linuxNet.cc | 738 ++
platformLinux/linuxOGLVideo.cc | 612 ++
platformLinux/linuxOGLVideo.h | 40 +
platformLinux/linuxOpenAL.cc | 241 +
platformLinux/linuxProcessControl.cc | 41 +
platformLinux/linuxRedBook.cc | 205 +
platformLinux/linuxSemaphore.cc | 73 +
platformLinux/linuxStrings.cc | 229 +
platformLinux/linuxThread.cc | 118 +
platformLinux/linuxTime.cc | 139 +
platformLinux/linuxWindow.cc | 1026 ++
platformLinux/lokiOpenAL.cc | 866 ++
platformLinux/lokiOpenAL.h | 11 +
platformLinux/mSolver_ASM.asm | 387 +
platformLinux/platformAL.h | 136 +
platformLinux/platformGL.h | 3997 ++++++++
platformLinux/platformLinux.h | 67 +
platformMacCarb/macCarb.rsrc | 1 +
platformMacCarb/macCarbAudio.cc | 260 +
platformMacCarb/macCarbCPUInfo.cc | 79 +
platformMacCarb/macCarbConsole.cc | 322 +
platformMacCarb/macCarbConsole.h | 56 +
platformMacCarb/macCarbFileio.cc | 944 ++
platformMacCarb/macCarbFileio.h | 14 +
platformMacCarb/macCarbFont.cc | 182 +
platformMacCarb/macCarbGG.icns | Bin 0 -> 60562 bytes
platformMacCarb/macCarbGL.cc | 265 +
platformMacCarb/macCarbHeaders.h | 205 +
platformMacCarb/macCarbInput.cc | 1316 +++
platformMacCarb/macCarbMath.cc | 73 +
platformMacCarb/macCarbMemory.cc | 60 +
platformMacCarb/macCarbNPatch.h | 55 +
platformMacCarb/macCarbNet.cc | 982 ++
platformMacCarb/macCarbOGLVideo.cc | 1734 ++++
platformMacCarb/macCarbOGLVideo.h | 38 +
platformMacCarb/macCarbProcessControl.cc | 25 +
platformMacCarb/macCarbStrings.cc | 370 +
platformMacCarb/macCarbTime.cc | 64 +
platformMacCarb/macCarbWindow.cc | 1281 +++
platformMacCarb/macCarb_common_prefix.h | 20 +
platformMacCarb/macCarb_debug_prefix.h | 21 +
platformMacCarb/macCarb_release_prefix.h | 19 +
platformMacCarb/platformAL.h | 148 +
platformMacCarb/platformGL.h | 191 +
platformMacCarb/platformMacCarb.h | 65 +
platformPPC/platformGL.h | 115 +
platformPPC/platformPPC.h | 69 +
platformPPC/ppcAudio.cc | 28 +
platformPPC/ppcCPUInfo.cc | 34 +
platformPPC/ppcConsole.cc | 231 +
platformPPC/ppcConsole.h | 48 +
platformPPC/ppcFileio.cc | 636 ++
platformPPC/ppcFont.cc | 144 +
platformPPC/ppcGL.cc | 116 +
platformPPC/ppcInput.cc | 305 +
platformPPC/ppcMath.cc | 73 +
platformPPC/ppcMemory.cc | 59 +
platformPPC/ppcNet.cc | 787 ++
platformPPC/ppcOGLVideo.cc | 422 +
platformPPC/ppcOGLVideo.h | 31 +
platformPPC/ppcProcessControl.cc | 23 +
platformPPC/ppcStrings.cc | 306 +
platformPPC/ppcTime.cc | 67 +
platformPPC/ppcUtils.cc | 22 +
platformPPC/ppcUtils.h | 15 +
platformPPC/ppcWindow.cc | 1046 +++
platformWin32/d3dgl.cc | 10381 +++++++++++++++++++++
platformWin32/d3dgl.h | 222 +
platformWin32/gllist.h | 346 +
platformWin32/platformAL.h | 148 +
platformWin32/platformGL.h | 1809 ++++
platformWin32/platformWin32.h | 90 +
platformWin32/win32NPatch.h | 37 +
platformWin32/winAsmBlit.cc | 338 +
platformWin32/winCPUInfo.cc | 280 +
platformWin32/winConsole.cc | 244 +
platformWin32/winConsole.h | 49 +
platformWin32/winD3DVideo.cc | 720 ++
platformWin32/winD3DVideo.h | 41 +
platformWin32/winDInputDevice.cc | 1629 ++++
platformWin32/winDInputDevice.h | 144 +
platformWin32/winDirectInput.cc | 553 ++
platformWin32/winDirectInput.h | 88 +
platformWin32/winFileio.cc | 589 ++
platformWin32/winFont.cc | 133 +
platformWin32/winGL.cc | 5948 ++++++++++++
platformWin32/winInput.cc | 808 ++
platformWin32/winMath.cc | 103 +
platformWin32/winMath_ASM.cc | 54 +
platformWin32/winMemory.cc | 53 +
platformWin32/winMutex.cc | 35 +
platformWin32/winNet.cc | 801 ++
platformWin32/winOGLVideo.cc | 1219 +++
platformWin32/winOGLVideo.h | 39 +
platformWin32/winOpenAL.cc | 253 +
platformWin32/winProcessControl.cc | 23 +
platformWin32/winRedbook.cc | 468 +
platformWin32/winSemaphore.cc | 44 +
platformWin32/winStrings.cc | 288 +
platformWin32/winThread.cc | 98 +
platformWin32/winTime.cc | 87 +
platformWin32/winV2Video.cc | 668 ++
platformWin32/winV2Video.h | 35 +
platformWin32/winWindow.cc | 1385 +++
platformWin32/win_common_prefix.h | 18 +
platformWin32/win_debug_prefix.h | 9 +
platformWin32/win_release_prefix.h | 23 +
platformX86UNIX/gl_func.h | 798 ++
platformX86UNIX/gl_types.h | 939 ++
platformX86UNIX/glu_func.h | 14 +
platformX86UNIX/platformAL.h | 147 +
platformX86UNIX/platformGL.h | 2552 +++++
platformX86UNIX/platformX86UNIX.h | 84 +
platformX86UNIX/x86UNIXAsmBlit.cc | 50 +
platformX86UNIX/x86UNIXCPUInfo.cc | 94 +
platformX86UNIX/x86UNIXConsole.cc | 257 +
platformX86UNIX/x86UNIXDedicatedStub.cc | 95 +
platformX86UNIX/x86UNIXFileio.cc | 785 ++
platformX86UNIX/x86UNIXFont.cc | 132 +
platformX86UNIX/x86UNIXGL.cc | 131 +
platformX86UNIX/x86UNIXGLX.h | 377 +
platformX86UNIX/x86UNIXIO.cc | 24 +
platformX86UNIX/x86UNIXInput.cc | 831 ++
platformX86UNIX/x86UNIXInputManager.cc | 1154 +++
platformX86UNIX/x86UNIXInputManager.h | 140 +
platformX86UNIX/x86UNIXMath.cc | 107 +
platformX86UNIX/x86UNIXMath_ASM.cc | 40 +
platformX86UNIX/x86UNIXMemory.cc | 54 +
platformX86UNIX/x86UNIXMessageBox.cc | 341 +
platformX86UNIX/x86UNIXMessageBox.h | 78 +
platformX86UNIX/x86UNIXMutex.cc | 39 +
platformX86UNIX/x86UNIXMutex.h | 26 +
platformX86UNIX/x86UNIXNet.cc | 782 ++
platformX86UNIX/x86UNIXOGLVideo.cc | 613 ++
platformX86UNIX/x86UNIXOGLVideo.h | 39 +
platformX86UNIX/x86UNIXOpenAL.cc | 175 +
platformX86UNIX/x86UNIXProcessControl.cc | 23 +
platformX86UNIX/x86UNIXRedbook.cc | 407 +
platformX86UNIX/x86UNIXSemaphore.cc | 47 +
platformX86UNIX/x86UNIXState.h | 377 +
platformX86UNIX/x86UNIXStdConsole.h | 49 +
platformX86UNIX/x86UNIXStrings.cc | 364 +
platformX86UNIX/x86UNIXThread.cc | 97 +
platformX86UNIX/x86UNIXTime.cc | 90 +
platformX86UNIX/x86UNIXUtils.cc | 30 +
platformX86UNIX/x86UNIXUtils.h | 31 +
platformX86UNIX/x86UNIXWindow.cc | 1525 +++
sceneGraph/detailManager.cc | 447 +
sceneGraph/detailManager.h | 110 +
sceneGraph/lightManager.cc | 412 +
sceneGraph/lightManager.h | 116 +
sceneGraph/sceneGraph.cc | 1042 +++
sceneGraph/sceneGraph.h | 318 +
sceneGraph/sceneLighting.cc | 2904 ++++++
sceneGraph/sceneLighting.h | 266 +
sceneGraph/sceneRoot.cc | 87 +
sceneGraph/sceneRoot.h | 44 +
sceneGraph/sceneState.cc | 992 ++
sceneGraph/sceneState.h | 308 +
sceneGraph/sceneTraversal.cc | 419 +
sceneGraph/sgUtil.cc | 365 +
sceneGraph/sgUtil.h | 55 +
sceneGraph/shadowVolumeBSP.cc | 708 ++
sceneGraph/shadowVolumeBSP.h | 140 +
sceneGraph/windingClipper.cc | 129 +
sceneGraph/windingClipper.h | 17 +
shell/shellFancyArray.cc | 2132 +++++
shell/shellFancyArray.h | 324 +
shell/shellFancyTextList.cc | 669 ++
shell/shellFancyTextList.h | 108 +
shell/shellScrollCtrl.cc | 512 +
shell/shellScrollCtrl.h | 119 +
shell/shellTextEditCtrl.cc | 124 +
shell/shellTextEditCtrl.h | 54 +
sim/actionMap.cc | 1637 ++++
sim/actionMap.h | 142 +
sim/cannedChatDataBlock.cc | 72 +
sim/cannedChatDataBlock.h | 37 +
sim/decalManager.cc | 392 +
sim/decalManager.h | 93 +
sim/frameAllocator.cc | 80 +
sim/frameAllocator.h | 33 +
sim/netConnection.cc | 1164 +++
sim/netConnection.h | 526 ++
sim/netDownload.cc | 270 +
sim/netEvent.cc | 355 +
sim/netGhost.cc | 908 ++
sim/netObject.cc | 249 +
sim/netObject.h | 143 +
sim/netStringTable.cc | 218 +
sim/netStringTable.h | 67 +
sim/pathManager.cc | 406 +
sim/pathManager.h | 105 +
sim/sceneObject.cc | 2071 ++++
sim/sceneObject.h | 502 +
sim/simPath.cc | 361 +
sim/simPath.h | 89 +
targets.torque.mk | 520 ++
targets.v12.mk | 540 ++
terrain/blender.cc | 1714 ++++
terrain/blender.h | 92 +
terrain/blender_asm.asm | 1351 +++
terrain/bvQuadTree.cc | 178 +
terrain/bvQuadTree.h | 57 +
terrain/fluid.h | 326 +
terrain/fluidQuadTree.cc | 1060 +++
terrain/fluidRender.cc | 249 +
terrain/fluidSupport.cc | 607 ++
terrain/sky.cc | 1788 ++++
terrain/sky.h | 261 +
terrain/sun.cc | 116 +
terrain/sun.h | 54 +
terrain/terrCollision.cc | 1018 ++
terrain/terrData.cc | 1420 +++
terrain/terrData.h | 414 +
terrain/terrLighting.cc | 766 ++
terrain/terrRender.cc | 2298 +++++
terrain/terrRender.h | 342 +
terrain/terrRender2.cc | 655 ++
terrain/waterBlock.cc | 724 ++
terrain/waterBlock.h | 138 +
ts/tsAnimate.cc | 1099 +++
ts/tsCollision.cc | 374 +
ts/tsDecal.cc | 249 +
ts/tsDecal.h | 46 +
ts/tsDump.cc | 233 +
ts/tsIntegerSet.cc | 222 +
ts/tsIntegerSet.h | 84 +
ts/tsLastDetail.cc | 356 +
ts/tsLastDetail.h | 63 +
ts/tsMaterialList.cc | 323 +
ts/tsMesh.cc | 3129 +++++++
ts/tsMesh.h | 283 +
ts/tsPartInstance.cc | 476 +
ts/tsPartInstance.h | 107 +
ts/tsShape.cc | 1666 ++++
ts/tsShape.h | 521 ++
ts/tsShapeAlloc.cc | 211 +
ts/tsShapeAlloc.h | 126 +
ts/tsShapeConstruct.cc | 168 +
ts/tsShapeConstruct.h | 50 +
ts/tsShapeInstance.cc | 1833 ++++
ts/tsShapeInstance.h | 673 ++
ts/tsShapeOldRead.cc | 1519 +++
ts/tsSortedMesh.cc | 276 +
ts/tsSortedMesh.h | 60 +
ts/tsThread.cc | 799 ++
ts/tsTransform.cc | 133 +
ts/tsTransform.h | 91 +
v12 Engine Lib.dsp | 3596 +++++++
v12 Engine.dsp | 7336 +++++++++++++++
v12 Engine.dsw | 41 +
v12 Engine.opt | Bin 0 -> 60928 bytes
vc60.idb | 1 +
vc60.pdb | 1 +
988 files changed, 394180 insertions(+)
create mode 100644 Engine.dsp
create mode 100644 Engine.plg
create mode 100644 Makefile
create mode 100644 ai/aiConnection.cc
create mode 100644 ai/aiConnection.h
create mode 100644 ai/aiConsole.cc
create mode 100644 ai/aiDebug.cc
create mode 100644 ai/aiMath.cc
create mode 100644 ai/aiMath.h
create mode 100644 ai/aiNavJetting.cc
create mode 100644 ai/aiNavJetting.h
create mode 100644 ai/aiNavStep.cc
create mode 100644 ai/aiNavStep.h
create mode 100644 ai/aiObjective.cc
create mode 100644 ai/aiObjective.h
create mode 100644 ai/aiPlayer.cc
create mode 100644 ai/aiPlayer.h
create mode 100644 ai/aiStep.cc
create mode 100644 ai/aiStep.h
create mode 100644 ai/aiTask.cc
create mode 100644 ai/aiTask.h
create mode 100644 ai/graph.cc
create mode 100644 ai/graph.h
create mode 100644 ai/graphBSP.h
create mode 100644 ai/graphBase.cc
create mode 100644 ai/graphBase.h
create mode 100644 ai/graphBridge.cc
create mode 100644 ai/graphBridge.h
create mode 100644 ai/graphBuildLOS.cc
create mode 100644 ai/graphConjoin.cc
create mode 100644 ai/graphData.cc
create mode 100644 ai/graphData.h
create mode 100644 ai/graphDebug.cc
create mode 100644 ai/graphDefines.h
create mode 100644 ai/graphDijkstra.cc
create mode 100644 ai/graphFind.cc
create mode 100644 ai/graphFloorPlan.cc
create mode 100644 ai/graphFloorPlan.h
create mode 100644 ai/graphFloorRender.cc
create mode 100644 ai/graphForceField.cc
create mode 100644 ai/graphForceField.h
create mode 100644 ai/graphGenUtils.cc
create mode 100644 ai/graphGenUtils.h
create mode 100644 ai/graphGroundPlan.cc
create mode 100644 ai/graphGroundPlan.h
create mode 100644 ai/graphGroundVisit.h
create mode 100644 ai/graphIndoors.cc
create mode 100644 ai/graphIsland.cc
create mode 100644 ai/graphJetting.cc
create mode 100644 ai/graphJetting.h
create mode 100644 ai/graphLOS.cc
create mode 100644 ai/graphLOS.h
create mode 100644 ai/graphLocate.cc
create mode 100644 ai/graphLocate.h
create mode 100644 ai/graphMake.cc
create mode 100644 ai/graphMath.cc
create mode 100644 ai/graphMath.h
create mode 100644 ai/graphNodes.h
create mode 100644 ai/graphOutdoors.cc
create mode 100644 ai/graphPartition.cc
create mode 100644 ai/graphPartition.h
create mode 100644 ai/graphPath.cc
create mode 100644 ai/graphPath.h
create mode 100644 ai/graphQueries.cc
create mode 100644 ai/graphRender.cc
create mode 100644 ai/graphSearchLOS.cc
create mode 100644 ai/graphSearches.h
create mode 100644 ai/graphSmooth.cc
create mode 100644 ai/graphSpawn.cc
create mode 100644 ai/graphThreats.cc
create mode 100644 ai/graphThreats.h
create mode 100644 ai/graphTransient.cc
create mode 100644 ai/graphTransient.h
create mode 100644 ai/graphVolume.cc
create mode 100644 ai/oVector.h
create mode 100644 ai/tBinHeap.h
create mode 100644 ai/texturePreload.h
create mode 100644 audio/audio.cc
create mode 100644 audio/audio.h
create mode 100644 audio/audioBuffer.cc
create mode 100644 audio/audioBuffer.h
create mode 100644 audio/audioCodec.cc
create mode 100644 audio/audioCodec.h
create mode 100644 audio/audioCodecGSM.cc
create mode 100644 audio/audioCodecGSM.h
create mode 100644 audio/audioCodecMiles.cc
create mode 100644 audio/audioCodecMiles.h
create mode 100644 audio/audioDataBlock.cc
create mode 100644 audio/audioDataBlock.h
create mode 100644 audio/audioFunctions.cc
create mode 100644 audio/audioMss.cc
create mode 100644 audio/audioMss.h
create mode 100644 audio/audioNet.cc
create mode 100644 audio/audioNet.h
create mode 100644 audio/audioThread.cc
create mode 100644 audio/audioThread.h
create mode 100644 audio/bufferQueue.cc
create mode 100644 audio/bufferQueue.h
create mode 100644 collision/abstractPolyList.cc
create mode 100644 collision/abstractPolyList.h
create mode 100644 collision/boxConvex.cc
create mode 100644 collision/boxConvex.h
create mode 100644 collision/clippedPolyList.cc
create mode 100644 collision/clippedPolyList.h
create mode 100644 collision/collision.h
create mode 100644 collision/concretePolyList.cc
create mode 100644 collision/concretePolyList.h
create mode 100644 collision/convex.cc
create mode 100644 collision/convex.h
create mode 100644 collision/depthSortList.cc
create mode 100644 collision/depthSortList.h
create mode 100644 collision/earlyOutPolyList.cc
create mode 100644 collision/earlyOutPolyList.h
create mode 100644 collision/extrudedPolyList.cc
create mode 100644 collision/extrudedPolyList.h
create mode 100644 collision/gjk.cc
create mode 100644 collision/gjk.h
create mode 100644 collision/planeExtractor.cc
create mode 100644 collision/planeExtractor.h
create mode 100644 collision/polyhedron.cc
create mode 100644 collision/polyhedron.h
create mode 100644 collision/polytope.cc
create mode 100644 collision/polytope.h
create mode 100644 console/ast.h
create mode 100644 console/compiledEval.cc
create mode 100644 console/compiler.cc
create mode 100644 console/compiler.h
create mode 100644 console/console.cc
create mode 100644 console/console.h
create mode 100644 console/consoleDoc.cc
create mode 100644 console/consoleFunctions.cc
create mode 100644 console/consoleInternal.cc
create mode 100644 console/consoleInternal.h
create mode 100644 console/consoleObject.cc
create mode 100644 console/consoleObject.h
create mode 100644 console/consoleTypes.cc
create mode 100644 console/consoleTypes.h
create mode 100644 console/gram.cc
create mode 100644 console/gram.h
create mode 100644 console/gram.y
create mode 100644 console/objectTypes.h
create mode 100644 console/scan.cc
create mode 100644 console/scan.l
create mode 100644 console/scriptObject.cc
create mode 100644 console/simBase.cc
create mode 100644 console/simBase.h
create mode 100644 console/simDictionary.cc
create mode 100644 console/simDictionary.h
create mode 100644 console/simManager.cc
create mode 100644 console/telnetConsole.cc
create mode 100644 console/telnetConsole.h
create mode 100644 console/telnetDebugger.cc
create mode 100644 console/telnetDebugger.h
create mode 100644 console/typeValidators.cc
create mode 100644 console/typeValidators.h
create mode 100644 console/yylex.c
create mode 100644 console/yyparse.c
create mode 100644 core/bitMatrix.h
create mode 100644 core/bitRender.cc
create mode 100644 core/bitRender.h
create mode 100644 core/bitSet.h
create mode 100644 core/bitStream.cc
create mode 100644 core/bitStream.h
create mode 100644 core/bitTables.cc
create mode 100644 core/bitTables.h
create mode 100644 core/bitVector.h
create mode 100644 core/bitVectorW.h
create mode 100644 core/color.h
create mode 100644 core/coreRes.h
create mode 100644 core/crc.cc
create mode 100644 core/crc.h
create mode 100644 core/dataChunker.cc
create mode 100644 core/dataChunker.h
create mode 100644 core/dnet.cc
create mode 100644 core/dnet.h
create mode 100644 core/fileObject.cc
create mode 100644 core/fileObject.h
create mode 100644 core/fileStream.cc
create mode 100644 core/fileStream.h
create mode 100644 core/fileio.h
create mode 100644 core/filterStream.cc
create mode 100644 core/filterStream.h
create mode 100644 core/findMatch.cc
create mode 100644 core/findMatch.h
create mode 100644 core/idGenerator.cc
create mode 100644 core/idGenerator.h
create mode 100644 core/llist.h
create mode 100644 core/memStream.cc
create mode 100644 core/memstream.h
create mode 100644 core/nStream.cc
create mode 100644 core/nTypes.cc
create mode 100644 core/polyList.h
create mode 100644 core/realComp.h
create mode 100644 core/resDictionary.cc
create mode 100644 core/resManager.cc
create mode 100644 core/resManager.h
create mode 100644 core/resizeStream.cc
create mode 100644 core/resizeStream.h
create mode 100644 core/stream.h
create mode 100644 core/stringTable.cc
create mode 100644 core/stringTable.h
create mode 100644 core/tAlgorithm.h
create mode 100644 core/tSortedSceneObjectList.cc
create mode 100644 core/tSortedSceneObjectList.h
create mode 100644 core/tSparseArray.h
create mode 100644 core/tVector.cc
create mode 100644 core/tVector.h
create mode 100644 core/tagDictionary.cc
create mode 100644 core/tagDictionary.h
create mode 100644 core/zipAggregate.cc
create mode 100644 core/zipAggregate.h
create mode 100644 core/zipHeaders.cc
create mode 100644 core/zipHeaders.h
create mode 100644 core/zipSubStream.cc
create mode 100644 core/zipSubStream.h
create mode 100644 crypt/cryptMGF.cc
create mode 100644 crypt/cryptMGF.h
create mode 100644 crypt/cryptRandPool.cc
create mode 100644 crypt/cryptRandPool.h
create mode 100644 crypt/cryptSHA1.cc
create mode 100644 crypt/cryptSHA1.h
create mode 100644 dgl/bitmapBm8.cc
create mode 100644 dgl/bitmapBmp.cc
create mode 100644 dgl/bitmapGif.cc
create mode 100644 dgl/bitmapJpeg.cc
create mode 100644 dgl/bitmapPng.cc
create mode 100644 dgl/dgl.cc
create mode 100644 dgl/dgl.h
create mode 100644 dgl/dglMatrix.cc
create mode 100644 dgl/gBitmap.cc
create mode 100644 dgl/gBitmap.h
create mode 100644 dgl/gChunkedTexManager.h
create mode 100644 dgl/gFont.cc
create mode 100644 dgl/gFont.h
create mode 100644 dgl/gPalette.cc
create mode 100644 dgl/gPalette.h
create mode 100644 dgl/gTexManager.cc
create mode 100644 dgl/gTexManager.h
create mode 100644 dgl/lensFlare.cc
create mode 100644 dgl/lensFlare.h
create mode 100644 dgl/materialList.cc
create mode 100644 dgl/materialList.h
create mode 100644 dgl/materialPropertyMap.cc
create mode 100644 dgl/materialPropertyMap.h
create mode 100644 dgl/rectClipper.cc
create mode 100644 dgl/rectClipper.h
create mode 100644 dgl/splineUtil.cc
create mode 100644 dgl/splineUtil.h
create mode 100644 dgl/stripCache.cc
create mode 100644 dgl/stripCache.h
create mode 100644 editor/compTest.cc
create mode 100644 editor/creator.cc
create mode 100644 editor/creator.h
create mode 100644 editor/editTSCtrl.cc
create mode 100644 editor/editTSCtrl.h
create mode 100644 editor/editor.cc
create mode 100644 editor/editor.h
create mode 100644 editor/editorButtonCtrl.cc
create mode 100644 editor/editorButtonCtrl.h
create mode 100644 editor/editorCheckboxCtrl.cc
create mode 100644 editor/editorCheckboxCtrl.h
create mode 100644 editor/guiTerrPreviewCtrl.cc
create mode 100644 editor/guiTerrPreviewCtrl.h
create mode 100644 editor/missionAreaEditor.cc
create mode 100644 editor/missionAreaEditor.h
create mode 100644 editor/terraformer.cc
create mode 100644 editor/terraformer.h
create mode 100644 editor/terraformerNoise.cc
create mode 100644 editor/terraformerNoise.h
create mode 100644 editor/terraformerTexture.cc
create mode 100644 editor/terrainActions.cc
create mode 100644 editor/terrainActions.h
create mode 100644 editor/terrainEditor.cc
create mode 100644 editor/terrainEditor.h
create mode 100644 editor/worldEditor.cc
create mode 100644 editor/worldEditor.h
create mode 100644 engine.overview.txt
create mode 100644 game/RCa03592
create mode 100644 game/RDa03592
create mode 100644 game/aiConnection.cc
create mode 100644 game/aiConnection.h
create mode 100644 game/aiCore.cc
create mode 100644 game/aiCore.h
create mode 100644 game/aiPlayer.cc
create mode 100644 game/aiPlayer.h
create mode 100644 game/ambientAudioManager.cc
create mode 100644 game/ambientAudioManager.h
create mode 100644 game/audioEmitter.cc
create mode 100644 game/audioEmitter.h
create mode 100644 game/auth.h
create mode 100644 game/badWordFilter.cc
create mode 100644 game/badWordFilter.h
create mode 100644 game/banList.cc
create mode 100644 game/banList.h
create mode 100644 game/bombSight.cc
create mode 100644 game/bombSight.h
create mode 100644 game/camera.cc
create mode 100644 game/camera.h
create mode 100644 game/cameraFXMgr.cc
create mode 100644 game/cameraFXMgr.h
create mode 100644 game/collisionTest.cc
create mode 100644 game/collisionTest.h
create mode 100644 game/commanderMapIcon.cc
create mode 100644 game/commanderMapIcon.h
create mode 100644 game/debris.cc
create mode 100644 game/debris.h
create mode 100644 game/debugView.cc
create mode 100644 game/debugView.h
create mode 100644 game/demoGame.h
create mode 100644 game/explosion.cc
create mode 100644 game/explosion.h
create mode 100644 game/fireballAtmosphere.cc
create mode 100644 game/fireballAtmosphere.h
create mode 100644 game/flyingVehicle.cc
create mode 100644 game/flyingVehicle.h
create mode 100644 game/forceFieldBare.cc
create mode 100644 game/forceFieldBare.h
create mode 100644 game/fps/guiClockHud.cc
create mode 100644 game/fps/guiCrossHairHud.cc
create mode 100644 game/fps/guiHealthBarHud.cc
create mode 100644 game/fps/guiShapeNameHud.cc
create mode 100644 game/fps/shapeNameHud.cc
create mode 100644 game/fps/shapeNameHud.h
create mode 100644 game/fx/cameraFXMgr.cc
create mode 100644 game/fx/cameraFXMgr.h
create mode 100644 game/fx/explosion.cc
create mode 100644 game/fx/explosion.h
create mode 100644 game/fx/lightning.cc
create mode 100644 game/fx/lightning.h
create mode 100644 game/fx/particleEmitter.cc
create mode 100644 game/fx/particleEmitter.h
create mode 100644 game/fx/particleEngine.cc
create mode 100644 game/fx/particleEngine.h
create mode 100644 game/fx/precipitation.cc
create mode 100644 game/fx/precipitation.h
create mode 100644 game/fx/splash.cc
create mode 100644 game/fx/splash.h
create mode 100644 game/fx/underLava.cc
create mode 100644 game/fx/underLava.h
create mode 100644 game/game.cc
create mode 100644 game/game.h
create mode 100644 game/gameBase.cc
create mode 100644 game/gameBase.h
create mode 100644 game/gameConnection.cc
create mode 100644 game/gameConnection.h
create mode 100644 game/gameConnectionEvents.cc
create mode 100644 game/gameConnectionEvents.h
create mode 100644 game/gameConnectionMoves.cc
create mode 100644 game/gameFunctions.cc
create mode 100644 game/gameFunctions.h
create mode 100644 game/gameProcess.cc
create mode 100644 game/gameTSCtrl.cc
create mode 100644 game/gameTSCtrl.h
create mode 100644 game/guiNoMouseCtrl.cc
create mode 100644 game/guiPlayerView.cc
create mode 100644 game/guiPlayerView.h
create mode 100644 game/guiServerBrowser.cc
create mode 100644 game/guiServerBrowser.h
create mode 100644 game/hoverVehicle.cc
create mode 100644 game/hoverVehicle.h
create mode 100644 game/httpObject.cc
create mode 100644 game/httpObject.h
create mode 100644 game/item.cc
create mode 100644 game/item.h
create mode 100644 game/lightning.cc
create mode 100644 game/lightning.h
create mode 100644 game/linearProjectile.cc
create mode 100644 game/linearProjectile.h
create mode 100644 game/main.cc
create mode 100644 game/missionArea.cc
create mode 100644 game/missionArea.h
create mode 100644 game/missionMarker.cc
create mode 100644 game/missionMarker.h
create mode 100644 game/motionBlurLine.cc
create mode 100644 game/motionBlurLine.h
create mode 100644 game/moveManager.h
create mode 100644 game/net.cc
create mode 100644 game/net/httpObject.cc
create mode 100644 game/net/httpObject.h
create mode 100644 game/net/net.cc
create mode 100644 game/net/netDispatch.cc
create mode 100644 game/net/netDispatch.h
create mode 100644 game/net/netTest.cc
create mode 100644 game/net/serverQuery.cc
create mode 100644 game/net/serverQuery.h
create mode 100644 game/net/tcpObject.cc
create mode 100644 game/net/tcpObject.h
create mode 100644 game/netDispatch.cc
create mode 100644 game/netDispatch.h
create mode 100644 game/netTest.cc
create mode 100644 game/objectTypes.h
create mode 100644 game/particleEmitter.cc
create mode 100644 game/particleEmitter.h
create mode 100644 game/particleEngine.cc
create mode 100644 game/particleEngine.h
create mode 100644 game/physicalZone.cc
create mode 100644 game/physicalZone.h
create mode 100644 game/platTest.cc
create mode 100644 game/player.cc
create mode 100644 game/player.h
create mode 100644 game/precipitation.cc
create mode 100644 game/precipitation.h
create mode 100644 game/projBomb.cc
create mode 100644 game/projBomb.h
create mode 100644 game/projELF.cc
create mode 100644 game/projELF.h
create mode 100644 game/projEnergy.cc
create mode 100644 game/projEnergy.h
create mode 100644 game/projFlareGrenade.cc
create mode 100644 game/projFlareGrenade.h
create mode 100644 game/projGrenade.cc
create mode 100644 game/projGrenade.h
create mode 100644 game/projLinearFlare.cc
create mode 100644 game/projLinearFlare.h
create mode 100644 game/projRepair.cc
create mode 100644 game/projRepair.h
create mode 100644 game/projSeeker.cc
create mode 100644 game/projSeeker.h
create mode 100644 game/projShockLance.cc
create mode 100644 game/projShockLance.h
create mode 100644 game/projSniper.cc
create mode 100644 game/projSniper.h
create mode 100644 game/projTargeting.cc
create mode 100644 game/projTargeting.h
create mode 100644 game/projTracer.cc
create mode 100644 game/projTracer.h
create mode 100644 game/projectile.cc
create mode 100644 game/projectile.h
create mode 100644 game/resource.h
create mode 100644 game/rigid.cc
create mode 100644 game/rigid.h
create mode 100644 game/scopeAlwaysShape.cc
create mode 100644 game/sensor.cc
create mode 100644 game/sensor.h
create mode 100644 game/serverQuery.cc
create mode 100644 game/serverQuery.h
create mode 100644 game/shadow.cc
create mode 100644 game/shadow.h
create mode 100644 game/shapeBase.cc
create mode 100644 game/shapeBase.h
create mode 100644 game/shapeCollision.cc
create mode 100644 game/shapeImage.cc
create mode 100644 game/shieldImpact.cc
create mode 100644 game/shieldImpact.h
create mode 100644 game/shockwave.cc
create mode 100644 game/shockwave.h
create mode 100644 game/showTSShape.cc
create mode 100644 game/showTSShape.h
create mode 100644 game/sphere.cc
create mode 100644 game/sphere.h
create mode 100644 game/splash.cc
create mode 100644 game/splash.h
create mode 100644 game/staticShape.cc
create mode 100644 game/staticShape.h
create mode 100644 game/stationFXPersonal.cc
create mode 100644 game/stationFXPersonal.h
create mode 100644 game/stationFXVehicle.cc
create mode 100644 game/stationFXVehicle.h
create mode 100644 game/targetManager.cc
create mode 100644 game/targetManager.h
create mode 100644 game/tcpObject.cc
create mode 100644 game/tcpObject.h
create mode 100644 game/tribes2.aps
create mode 100644 game/tribes2.ico
create mode 100644 game/tribes2.rc
create mode 100644 game/tribesGame.h
create mode 100644 game/trigger.cc
create mode 100644 game/trigger.h
create mode 100644 game/tsStatic.cc
create mode 100644 game/tsStatic.h
create mode 100644 game/turret.cc
create mode 100644 game/turret.h
create mode 100644 game/underLava.cc
create mode 100644 game/underLava.h
create mode 100644 game/v12.rc
create mode 100644 game/v12Game.h
create mode 100644 game/vehicle.cc
create mode 100644 game/vehicle.h
create mode 100644 game/vehicleBlocker.cc
create mode 100644 game/vehicleBlocker.h
create mode 100644 game/vehicles/flyingVehicle.cc
create mode 100644 game/vehicles/flyingVehicle.h
create mode 100644 game/vehicles/hoverVehicle.cc
create mode 100644 game/vehicles/hoverVehicle.h
create mode 100644 game/vehicles/vehicle.cc
create mode 100644 game/vehicles/vehicle.h
create mode 100644 game/vehicles/vehicleBlocker.cc
create mode 100644 game/vehicles/vehicleBlocker.h
create mode 100644 game/vehicles/wheeledVehicle.cc
create mode 100644 game/vehicles/wheeledVehicle.h
create mode 100644 game/version.cc
create mode 100644 game/version.h
create mode 100644 game/weaponBeam.cc
create mode 100644 game/weaponBeam.h
create mode 100644 game/wheeledVehicle.cc
create mode 100644 game/wheeledVehicle.h
create mode 100644 gui/channelVector.cc
create mode 100644 gui/channelVector.h
create mode 100644 gui/guiArrayCtrl.cc
create mode 100644 gui/guiArrayCtrl.h
create mode 100644 gui/guiAviBitmapCtrl.cc
create mode 100644 gui/guiAviBitmapCtrl.h
create mode 100644 gui/guiBackgroundCtrl.cc
create mode 100644 gui/guiBackgroundCtrl.h
create mode 100644 gui/guiBitmapBorderCtrl.cc
create mode 100644 gui/guiBitmapCtrl.cc
create mode 100644 gui/guiBitmapCtrl.h
create mode 100644 gui/guiBorderButton.cc
create mode 100644 gui/guiBubbleTextCtrl.cc
create mode 100644 gui/guiBubbleTextCtrl.h
create mode 100644 gui/guiButtonBaseCtrl.cc
create mode 100644 gui/guiButtonBaseCtrl.h
create mode 100644 gui/guiButtonCtrl.cc
create mode 100644 gui/guiButtonCtrl.h
create mode 100644 gui/guiCanvas.cc
create mode 100644 gui/guiCanvas.h
create mode 100644 gui/guiChannelVectorCtrl.cc
create mode 100644 gui/guiChannelVectorCtrl.h
create mode 100644 gui/guiChatMenuTreeCtrl.cc
create mode 100644 gui/guiChatMenuTreeCtrl.h
create mode 100644 gui/guiCheckBoxCtrl.cc
create mode 100644 gui/guiCheckBoxCtrl.h
create mode 100644 gui/guiChunkedBitmapCtrl.cc
create mode 100644 gui/guiConsole.cc
create mode 100644 gui/guiConsole.h
create mode 100644 gui/guiConsoleEditCtrl.cc
create mode 100644 gui/guiConsoleEditCtrl.h
create mode 100644 gui/guiConsoleTextCtrl.cc
create mode 100644 gui/guiConsoleTextCtrl.h
create mode 100644 gui/guiControl.cc
create mode 100644 gui/guiControl.h
create mode 100644 gui/guiControlListPopup.cc
create mode 100644 gui/guiDebugger.cc
create mode 100644 gui/guiDebugger.h
create mode 100644 gui/guiDefaultControlRender.cc
create mode 100644 gui/guiDefaultControlRender.h
create mode 100644 gui/guiEditCtrl.cc
create mode 100644 gui/guiEditCtrl.h
create mode 100644 gui/guiFilterCtrl.cc
create mode 100644 gui/guiFilterCtrl.h
create mode 100644 gui/guiFrameCtrl.cc
create mode 100644 gui/guiFrameCtrl.h
create mode 100644 gui/guiHelpCtrl.cc
create mode 100644 gui/guiHelpCtrl.h
create mode 100644 gui/guiInputCtrl.cc
create mode 100644 gui/guiInputCtrl.h
create mode 100644 gui/guiInspector.cc
create mode 100644 gui/guiInspector.h
create mode 100644 gui/guiMLTextCtrl.cc
create mode 100644 gui/guiMLTextCtrl.h
create mode 100644 gui/guiMLTextEditCtrl.cc
create mode 100644 gui/guiMLTextEditCtrl.h
create mode 100644 gui/guiMenuBar.cc
create mode 100644 gui/guiMenuBar.h
create mode 100644 gui/guiMessageVectorCtrl.cc
create mode 100644 gui/guiMessageVectorCtrl.h
create mode 100644 gui/guiMouseEventCtrl.cc
create mode 100644 gui/guiMouseEventCtrl.h
create mode 100644 gui/guiPopUpCtrl.cc
create mode 100644 gui/guiPopUpCtrl.h
create mode 100644 gui/guiProgressCtrl.cc
create mode 100644 gui/guiProgressCtrl.h
create mode 100644 gui/guiRadioCtrl.cc
create mode 100644 gui/guiRadioCtrl.h
create mode 100644 gui/guiScrollCtrl.cc
create mode 100644 gui/guiScrollCtrl.h
create mode 100644 gui/guiSliderCtrl.cc
create mode 100644 gui/guiSliderCtrl.h
create mode 100644 gui/guiTSControl.cc
create mode 100644 gui/guiTSControl.h
create mode 100644 gui/guiTextCtrl.cc
create mode 100644 gui/guiTextCtrl.h
create mode 100644 gui/guiTextEditCtrl.cc
create mode 100644 gui/guiTextEditCtrl.h
create mode 100644 gui/guiTextEditSliderCtrl.cc
create mode 100644 gui/guiTextEditSliderCtrl.h
create mode 100644 gui/guiTextListCtrl.cc
create mode 100644 gui/guiTextListCtrl.h
create mode 100644 gui/guiTreeViewCtrl.cc
create mode 100644 gui/guiTreeViewCtrl.h
create mode 100644 gui/guiTypes.cc
create mode 100644 gui/guiTypes.h
create mode 100644 gui/guiVoteCtrl.cc
create mode 100644 gui/guiVoteCtrl.h
create mode 100644 gui/guiWindowCtrl.cc
create mode 100644 gui/guiWindowCtrl.h
create mode 100644 gui/messageVector.cc
create mode 100644 gui/messageVector.h
create mode 100644 hud/hudBarBaseCtrl.cc
create mode 100644 hud/hudBarBaseCtrl.h
create mode 100644 hud/hudBarDisplayCtrl.cc
create mode 100644 hud/hudBarDisplayCtrl.h
create mode 100644 hud/hudBezierDisplayCtrl.cc
create mode 100644 hud/hudBezierDisplayCtrl.h
create mode 100644 hud/hudBitmapCtrl.cc
create mode 100644 hud/hudBitmapCtrl.h
create mode 100644 hud/hudBitmapFrameCtrl.cc
create mode 100644 hud/hudBitmapFrameCtrl.h
create mode 100644 hud/hudClock.cc
create mode 100644 hud/hudClockCtrl.cc
create mode 100644 hud/hudCompass.cc
create mode 100644 hud/hudCrosshair.cc
create mode 100644 hud/hudCtrl.cc
create mode 100644 hud/hudCtrl.h
create mode 100644 hud/hudDamageCtrl.cc
create mode 100644 hud/hudEnergyCtrl.cc
create mode 100644 hud/hudEnergyDamage.cc
create mode 100644 hud/hudGLEx.cc
create mode 100644 hud/hudGLEx.h
create mode 100644 hud/hudHealthCtrl.cc
create mode 100644 hud/hudHeat.cc
create mode 100644 hud/hudObject.cc
create mode 100644 hud/hudObject.h
create mode 100644 hud/hudZoom.cc
create mode 100644 hud/mBezier2D.cc
create mode 100644 hud/mBezier2D.h
create mode 100644 interior/floorPlanRes.cc
create mode 100644 interior/floorPlanRes.h
create mode 100644 interior/forceField.cc
create mode 100644 interior/forceField.h
create mode 100644 interior/interior.cc
create mode 100644 interior/interior.h
create mode 100644 interior/interiorCollision.cc
create mode 100644 interior/interiorDebug.cc
create mode 100644 interior/interiorIO.cc
create mode 100644 interior/interiorInstance.cc
create mode 100644 interior/interiorInstance.h
create mode 100644 interior/interiorLMManager.cc
create mode 100644 interior/interiorLMManager.h
create mode 100644 interior/interiorLightAnim.cc
create mode 100644 interior/interiorRender.cc
create mode 100644 interior/interiorRes.cc
create mode 100644 interior/interiorRes.h
create mode 100644 interior/interiorResObjects.cc
create mode 100644 interior/interiorResObjects.h
create mode 100644 interior/interiorSubObject.cc
create mode 100644 interior/interiorSubObject.h
create mode 100644 interior/itf.h
create mode 100644 interior/itfdump.asm
create mode 100644 interior/itfdump.cc
create mode 100644 interior/itfdump_c.cc
create mode 100644 interior/lightUpdateGrouper.cc
create mode 100644 interior/lightUpdateGrouper.h
create mode 100644 interior/mirrorSubObject.cc
create mode 100644 interior/mirrorSubObject.h
create mode 100644 math/mBezier2D.cc
create mode 100644 math/mBezier2D.h
create mode 100644 math/mBox.cc
create mode 100644 math/mBox.h
create mode 100644 math/mConsoleFunctions.cc
create mode 100644 math/mConstants.h
create mode 100644 math/mMath.h
create mode 100644 math/mMathAMD.cc
create mode 100644 math/mMathAMD_ASM.asm
create mode 100644 math/mMathFn.cc
create mode 100644 math/mMathFn.h
create mode 100644 math/mMathSSE.cc
create mode 100644 math/mMathSSE_ASM.asm
create mode 100644 math/mMath_ASM.asm
create mode 100644 math/mMath_C.cc
create mode 100644 math/mMatrix.cc
create mode 100644 math/mMatrix.h
create mode 100644 math/mPlane.h
create mode 100644 math/mPlaneTransformer.cc
create mode 100644 math/mPlaneTransformer.h
create mode 100644 math/mPoint.h
create mode 100644 math/mQuadPatch.cc
create mode 100644 math/mQuadPatch.h
create mode 100644 math/mQuat.cc
create mode 100644 math/mQuat.h
create mode 100644 math/mRandom.cc
create mode 100644 math/mRandom.h
create mode 100644 math/mRect.h
create mode 100644 math/mSolver.cc
create mode 100644 math/mSphere.h
create mode 100644 math/mSplinePatch.cc
create mode 100644 math/mSplinePatch.h
create mode 100644 math/mTrig.h
create mode 100644 math/mathIO.h
create mode 100644 math/mathTypes.cc
create mode 100644 math/mathTypes.h
create mode 100644 math/mathUtils.cc
create mode 100644 math/mathUtils.h
create mode 100644 platform/3Dfx.h
create mode 100644 platform/event.h
create mode 100644 platform/gameInterface.cc
create mode 100644 platform/gameInterface.h
create mode 100644 platform/platform.h
create mode 100644 platform/platformAL.h
create mode 100644 platform/platformAssert.cc
create mode 100644 platform/platformAssert.h
create mode 100644 platform/platformAudio.h
create mode 100644 platform/platformCPU.cc
create mode 100644 platform/platformCPUInfo.asm
create mode 100644 platform/platformInput.h
create mode 100644 platform/platformMemory.cc
create mode 100644 platform/platformMutex.h
create mode 100644 platform/platformRedBook.cc
create mode 100644 platform/platformRedBook.h
create mode 100644 platform/platformSemaphore.h
create mode 100644 platform/platformThread.h
create mode 100644 platform/platformVideo.cc
create mode 100644 platform/platformVideo.h
create mode 100644 platform/profiler.cc
create mode 100644 platform/profiler.h
create mode 100644 platform/types.h
create mode 100644 platform/typesLinux.h
create mode 100644 platform/typesPPC.h
create mode 100644 platform/typesWin32.h
create mode 100644 platform/typesX86UNIX.h
create mode 100644 platformLinux/async.c
create mode 100644 platformLinux/async.h
create mode 100644 platformLinux/audio.cc
create mode 100644 platformLinux/blender.asm
create mode 100644 platformLinux/fixcalls.pl
create mode 100644 platformLinux/linuxAL.cc
create mode 100644 platformLinux/linuxALStub.cc
create mode 100644 platformLinux/linuxAsmBlit.cc
create mode 100644 platformLinux/linuxCPUInfo.cc
create mode 100644 platformLinux/linuxCPUInfo_ASM.asm
create mode 100644 platformLinux/linuxCodeMap.cc
create mode 100644 platformLinux/linuxConsole.cc
create mode 100644 platformLinux/linuxConsole.h
create mode 100644 platformLinux/linuxFileio.cc
create mode 100644 platformLinux/linuxFont.cc
create mode 100644 platformLinux/linuxGL.cc
create mode 100644 platformLinux/linuxIO.cc
create mode 100644 platformLinux/linuxInput.cc
create mode 100644 platformLinux/linuxMath.cc
create mode 100644 platformLinux/linuxMath_ASM.asm
create mode 100644 platformLinux/linuxMath_VC.c
create mode 100644 platformLinux/linuxMemory.cc
create mode 100644 platformLinux/linuxMutex.cc
create mode 100644 platformLinux/linuxNet.cc
create mode 100644 platformLinux/linuxOGLVideo.cc
create mode 100644 platformLinux/linuxOGLVideo.h
create mode 100644 platformLinux/linuxOpenAL.cc
create mode 100644 platformLinux/linuxProcessControl.cc
create mode 100644 platformLinux/linuxRedBook.cc
create mode 100644 platformLinux/linuxSemaphore.cc
create mode 100644 platformLinux/linuxStrings.cc
create mode 100644 platformLinux/linuxThread.cc
create mode 100644 platformLinux/linuxTime.cc
create mode 100644 platformLinux/linuxWindow.cc
create mode 100644 platformLinux/lokiOpenAL.cc
create mode 100644 platformLinux/lokiOpenAL.h
create mode 100644 platformLinux/mSolver_ASM.asm
create mode 100644 platformLinux/platformAL.h
create mode 100644 platformLinux/platformGL.h
create mode 100644 platformLinux/platformLinux.h
create mode 100644 platformMacCarb/macCarb.rsrc
create mode 100644 platformMacCarb/macCarbAudio.cc
create mode 100644 platformMacCarb/macCarbCPUInfo.cc
create mode 100644 platformMacCarb/macCarbConsole.cc
create mode 100644 platformMacCarb/macCarbConsole.h
create mode 100644 platformMacCarb/macCarbFileio.cc
create mode 100644 platformMacCarb/macCarbFileio.h
create mode 100644 platformMacCarb/macCarbFont.cc
create mode 100644 platformMacCarb/macCarbGG.icns
create mode 100644 platformMacCarb/macCarbGL.cc
create mode 100644 platformMacCarb/macCarbHeaders.h
create mode 100644 platformMacCarb/macCarbInput.cc
create mode 100644 platformMacCarb/macCarbMath.cc
create mode 100644 platformMacCarb/macCarbMemory.cc
create mode 100644 platformMacCarb/macCarbNPatch.h
create mode 100644 platformMacCarb/macCarbNet.cc
create mode 100644 platformMacCarb/macCarbOGLVideo.cc
create mode 100644 platformMacCarb/macCarbOGLVideo.h
create mode 100644 platformMacCarb/macCarbProcessControl.cc
create mode 100644 platformMacCarb/macCarbStrings.cc
create mode 100644 platformMacCarb/macCarbTime.cc
create mode 100644 platformMacCarb/macCarbWindow.cc
create mode 100644 platformMacCarb/macCarb_common_prefix.h
create mode 100644 platformMacCarb/macCarb_debug_prefix.h
create mode 100644 platformMacCarb/macCarb_release_prefix.h
create mode 100644 platformMacCarb/platformAL.h
create mode 100644 platformMacCarb/platformGL.h
create mode 100644 platformMacCarb/platformMacCarb.h
create mode 100644 platformPPC/platformGL.h
create mode 100644 platformPPC/platformPPC.h
create mode 100644 platformPPC/ppcAudio.cc
create mode 100644 platformPPC/ppcCPUInfo.cc
create mode 100644 platformPPC/ppcConsole.cc
create mode 100644 platformPPC/ppcConsole.h
create mode 100644 platformPPC/ppcFileio.cc
create mode 100644 platformPPC/ppcFont.cc
create mode 100644 platformPPC/ppcGL.cc
create mode 100644 platformPPC/ppcInput.cc
create mode 100644 platformPPC/ppcMath.cc
create mode 100644 platformPPC/ppcMemory.cc
create mode 100644 platformPPC/ppcNet.cc
create mode 100644 platformPPC/ppcOGLVideo.cc
create mode 100644 platformPPC/ppcOGLVideo.h
create mode 100644 platformPPC/ppcProcessControl.cc
create mode 100644 platformPPC/ppcStrings.cc
create mode 100644 platformPPC/ppcTime.cc
create mode 100644 platformPPC/ppcUtils.cc
create mode 100644 platformPPC/ppcUtils.h
create mode 100644 platformPPC/ppcWindow.cc
create mode 100644 platformWin32/d3dgl.cc
create mode 100644 platformWin32/d3dgl.h
create mode 100644 platformWin32/gllist.h
create mode 100644 platformWin32/platformAL.h
create mode 100644 platformWin32/platformGL.h
create mode 100644 platformWin32/platformWin32.h
create mode 100644 platformWin32/win32NPatch.h
create mode 100644 platformWin32/winAsmBlit.cc
create mode 100644 platformWin32/winCPUInfo.cc
create mode 100644 platformWin32/winConsole.cc
create mode 100644 platformWin32/winConsole.h
create mode 100644 platformWin32/winD3DVideo.cc
create mode 100644 platformWin32/winD3DVideo.h
create mode 100644 platformWin32/winDInputDevice.cc
create mode 100644 platformWin32/winDInputDevice.h
create mode 100644 platformWin32/winDirectInput.cc
create mode 100644 platformWin32/winDirectInput.h
create mode 100644 platformWin32/winFileio.cc
create mode 100644 platformWin32/winFont.cc
create mode 100644 platformWin32/winGL.cc
create mode 100644 platformWin32/winInput.cc
create mode 100644 platformWin32/winMath.cc
create mode 100644 platformWin32/winMath_ASM.cc
create mode 100644 platformWin32/winMemory.cc
create mode 100644 platformWin32/winMutex.cc
create mode 100644 platformWin32/winNet.cc
create mode 100644 platformWin32/winOGLVideo.cc
create mode 100644 platformWin32/winOGLVideo.h
create mode 100644 platformWin32/winOpenAL.cc
create mode 100644 platformWin32/winProcessControl.cc
create mode 100644 platformWin32/winRedbook.cc
create mode 100644 platformWin32/winSemaphore.cc
create mode 100644 platformWin32/winStrings.cc
create mode 100644 platformWin32/winThread.cc
create mode 100644 platformWin32/winTime.cc
create mode 100644 platformWin32/winV2Video.cc
create mode 100644 platformWin32/winV2Video.h
create mode 100644 platformWin32/winWindow.cc
create mode 100644 platformWin32/win_common_prefix.h
create mode 100644 platformWin32/win_debug_prefix.h
create mode 100644 platformWin32/win_release_prefix.h
create mode 100644 platformX86UNIX/gl_func.h
create mode 100644 platformX86UNIX/gl_types.h
create mode 100644 platformX86UNIX/glu_func.h
create mode 100644 platformX86UNIX/platformAL.h
create mode 100644 platformX86UNIX/platformGL.h
create mode 100644 platformX86UNIX/platformX86UNIX.h
create mode 100644 platformX86UNIX/x86UNIXAsmBlit.cc
create mode 100644 platformX86UNIX/x86UNIXCPUInfo.cc
create mode 100644 platformX86UNIX/x86UNIXConsole.cc
create mode 100644 platformX86UNIX/x86UNIXDedicatedStub.cc
create mode 100644 platformX86UNIX/x86UNIXFileio.cc
create mode 100644 platformX86UNIX/x86UNIXFont.cc
create mode 100644 platformX86UNIX/x86UNIXGL.cc
create mode 100644 platformX86UNIX/x86UNIXGLX.h
create mode 100644 platformX86UNIX/x86UNIXIO.cc
create mode 100644 platformX86UNIX/x86UNIXInput.cc
create mode 100644 platformX86UNIX/x86UNIXInputManager.cc
create mode 100644 platformX86UNIX/x86UNIXInputManager.h
create mode 100644 platformX86UNIX/x86UNIXMath.cc
create mode 100644 platformX86UNIX/x86UNIXMath_ASM.cc
create mode 100644 platformX86UNIX/x86UNIXMemory.cc
create mode 100644 platformX86UNIX/x86UNIXMessageBox.cc
create mode 100644 platformX86UNIX/x86UNIXMessageBox.h
create mode 100644 platformX86UNIX/x86UNIXMutex.cc
create mode 100644 platformX86UNIX/x86UNIXMutex.h
create mode 100644 platformX86UNIX/x86UNIXNet.cc
create mode 100644 platformX86UNIX/x86UNIXOGLVideo.cc
create mode 100644 platformX86UNIX/x86UNIXOGLVideo.h
create mode 100644 platformX86UNIX/x86UNIXOpenAL.cc
create mode 100644 platformX86UNIX/x86UNIXProcessControl.cc
create mode 100644 platformX86UNIX/x86UNIXRedbook.cc
create mode 100644 platformX86UNIX/x86UNIXSemaphore.cc
create mode 100644 platformX86UNIX/x86UNIXState.h
create mode 100644 platformX86UNIX/x86UNIXStdConsole.h
create mode 100644 platformX86UNIX/x86UNIXStrings.cc
create mode 100644 platformX86UNIX/x86UNIXThread.cc
create mode 100644 platformX86UNIX/x86UNIXTime.cc
create mode 100644 platformX86UNIX/x86UNIXUtils.cc
create mode 100644 platformX86UNIX/x86UNIXUtils.h
create mode 100644 platformX86UNIX/x86UNIXWindow.cc
create mode 100644 sceneGraph/detailManager.cc
create mode 100644 sceneGraph/detailManager.h
create mode 100644 sceneGraph/lightManager.cc
create mode 100644 sceneGraph/lightManager.h
create mode 100644 sceneGraph/sceneGraph.cc
create mode 100644 sceneGraph/sceneGraph.h
create mode 100644 sceneGraph/sceneLighting.cc
create mode 100644 sceneGraph/sceneLighting.h
create mode 100644 sceneGraph/sceneRoot.cc
create mode 100644 sceneGraph/sceneRoot.h
create mode 100644 sceneGraph/sceneState.cc
create mode 100644 sceneGraph/sceneState.h
create mode 100644 sceneGraph/sceneTraversal.cc
create mode 100644 sceneGraph/sgUtil.cc
create mode 100644 sceneGraph/sgUtil.h
create mode 100644 sceneGraph/shadowVolumeBSP.cc
create mode 100644 sceneGraph/shadowVolumeBSP.h
create mode 100644 sceneGraph/windingClipper.cc
create mode 100644 sceneGraph/windingClipper.h
create mode 100644 shell/shellFancyArray.cc
create mode 100644 shell/shellFancyArray.h
create mode 100644 shell/shellFancyTextList.cc
create mode 100644 shell/shellFancyTextList.h
create mode 100644 shell/shellScrollCtrl.cc
create mode 100644 shell/shellScrollCtrl.h
create mode 100644 shell/shellTextEditCtrl.cc
create mode 100644 shell/shellTextEditCtrl.h
create mode 100644 sim/actionMap.cc
create mode 100644 sim/actionMap.h
create mode 100644 sim/cannedChatDataBlock.cc
create mode 100644 sim/cannedChatDataBlock.h
create mode 100644 sim/decalManager.cc
create mode 100644 sim/decalManager.h
create mode 100644 sim/frameAllocator.cc
create mode 100644 sim/frameAllocator.h
create mode 100644 sim/netConnection.cc
create mode 100644 sim/netConnection.h
create mode 100644 sim/netDownload.cc
create mode 100644 sim/netEvent.cc
create mode 100644 sim/netGhost.cc
create mode 100644 sim/netObject.cc
create mode 100644 sim/netObject.h
create mode 100644 sim/netStringTable.cc
create mode 100644 sim/netStringTable.h
create mode 100644 sim/pathManager.cc
create mode 100644 sim/pathManager.h
create mode 100644 sim/sceneObject.cc
create mode 100644 sim/sceneObject.h
create mode 100644 sim/simPath.cc
create mode 100644 sim/simPath.h
create mode 100644 targets.torque.mk
create mode 100644 targets.v12.mk
create mode 100644 terrain/blender.cc
create mode 100644 terrain/blender.h
create mode 100644 terrain/blender_asm.asm
create mode 100644 terrain/bvQuadTree.cc
create mode 100644 terrain/bvQuadTree.h
create mode 100644 terrain/fluid.h
create mode 100644 terrain/fluidQuadTree.cc
create mode 100644 terrain/fluidRender.cc
create mode 100644 terrain/fluidSupport.cc
create mode 100644 terrain/sky.cc
create mode 100644 terrain/sky.h
create mode 100644 terrain/sun.cc
create mode 100644 terrain/sun.h
create mode 100644 terrain/terrCollision.cc
create mode 100644 terrain/terrData.cc
create mode 100644 terrain/terrData.h
create mode 100644 terrain/terrLighting.cc
create mode 100644 terrain/terrRender.cc
create mode 100644 terrain/terrRender.h
create mode 100644 terrain/terrRender2.cc
create mode 100644 terrain/waterBlock.cc
create mode 100644 terrain/waterBlock.h
create mode 100644 ts/tsAnimate.cc
create mode 100644 ts/tsCollision.cc
create mode 100644 ts/tsDecal.cc
create mode 100644 ts/tsDecal.h
create mode 100644 ts/tsDump.cc
create mode 100644 ts/tsIntegerSet.cc
create mode 100644 ts/tsIntegerSet.h
create mode 100644 ts/tsLastDetail.cc
create mode 100644 ts/tsLastDetail.h
create mode 100644 ts/tsMaterialList.cc
create mode 100644 ts/tsMesh.cc
create mode 100644 ts/tsMesh.h
create mode 100644 ts/tsPartInstance.cc
create mode 100644 ts/tsPartInstance.h
create mode 100644 ts/tsShape.cc
create mode 100644 ts/tsShape.h
create mode 100644 ts/tsShapeAlloc.cc
create mode 100644 ts/tsShapeAlloc.h
create mode 100644 ts/tsShapeConstruct.cc
create mode 100644 ts/tsShapeConstruct.h
create mode 100644 ts/tsShapeInstance.cc
create mode 100644 ts/tsShapeInstance.h
create mode 100644 ts/tsShapeOldRead.cc
create mode 100644 ts/tsSortedMesh.cc
create mode 100644 ts/tsSortedMesh.h
create mode 100644 ts/tsThread.cc
create mode 100644 ts/tsTransform.cc
create mode 100644 ts/tsTransform.h
create mode 100644 v12 Engine Lib.dsp
create mode 100644 v12 Engine.dsp
create mode 100644 v12 Engine.dsw
create mode 100644 v12 Engine.opt
create mode 100644 vc60.idb
create mode 100644 vc60.pdb
diff --git a/Engine.dsp b/Engine.dsp
new file mode 100644
index 0000000..70cb7e7
--- /dev/null
+++ b/Engine.dsp
@@ -0,0 +1,3133 @@
+# Microsoft Developer Studio Project File - Name="Engine" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) External Target" 0x0106
+
+CFG=Engine - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "Engine.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "Engine.mak" CFG="Engine - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "Engine - Win32 Release" (based on "Win32 (x86) External Target")
+!MESSAGE "Engine - Win32 Debug" (based on "Win32 (x86) External Target")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+
+!IF "$(CFG)" == "Engine - Win32 Release"
+
+# PROP BASE Use_MFC
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Cmd_Line "NMAKE /f Engine.mak"
+# PROP BASE Rebuild_Opt "/a"
+# PROP BASE Target_File "Engine.exe"
+# PROP BASE Bsc_Name "Engine.bsc"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Cmd_Line "make OS=WIN32 COMPILER=VC6 BUILD=RELEASE DIR.OBJ=out.VC6.RELEASE"
+# PROP Rebuild_Opt "/a"
+# PROP Target_File "v12_RELEASE.exe"
+# PROP Bsc_Name ""
+# PROP Target_Dir ""
+
+!ELSEIF "$(CFG)" == "Engine - Win32 Debug"
+
+# PROP BASE Use_MFC
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Cmd_Line "NMAKE /f Engine.mak"
+# PROP BASE Rebuild_Opt "/a"
+# PROP BASE Target_File "Engine.exe"
+# PROP BASE Bsc_Name "Engine.bsc"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Cmd_Line "make OS=WIN32 COMPILER=VC6 BUILD=DEBUG DIR.OBJ=out.VC6.DEBUG"
+# PROP Rebuild_Opt "/a"
+# PROP Target_File "v12_DEBUG.exe"
+# PROP Bsc_Name ""
+# PROP Target_Dir ""
+
+!ENDIF
+
+# Begin Target
+
+# Name "Engine - Win32 Release"
+# Name "Engine - Win32 Debug"
+
+!IF "$(CFG)" == "Engine - Win32 Release"
+
+!ELSEIF "$(CFG)" == "Engine - Win32 Debug"
+
+!ENDIF
+
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Group "ai"
+
+# PROP Default_Filter "cc"
+# Begin Source File
+
+SOURCE=.\ai\aiConnection.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\aiConsole.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\aiDebug.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\aiNavJetting.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\aiNavStep.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\aiObjective.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\aiStep.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\aiTask.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graph.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphBase.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphBridge.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphBuildLOS.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphConjoin.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphData.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphDebug.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphDijkstra.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphFind.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphFloorPlan.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphFloorRender.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphForceField.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphGenUtils.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphGroundPlan.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphIndoors.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphIsland.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphJetting.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphLocate.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphLOS.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphMake.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphMath.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphOutdoors.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphPartition.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphPath.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphQueries.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphRender.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphSearchLOS.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphSmooth.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphSpawn.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphThreats.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphTransient.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphVolume.cc
+# End Source File
+# End Group
+# Begin Group "audio"
+
+# PROP Default_Filter "cc"
+# Begin Source File
+
+SOURCE=.\audio\audio.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\audio\audioBuffer.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\audio\audioCodec.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\audio\audioCodecMiles.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\audio\audioDataBlock.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\audio\audioMss.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\audio\audioNet.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\audio\audioThread.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\audio\bufferQueue.cc
+# End Source File
+# End Group
+# Begin Group "collision"
+
+# PROP Default_Filter "cc"
+# Begin Source File
+
+SOURCE=.\collision\abstractPolyList.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\collision\boxConvex.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\collision\clippedPolyList.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\collision\convex.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\collision\depthSortList.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\collision\earlyOutPolyList.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\collision\extrudedPolyList.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\collision\gjk.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\collision\planeExtractor.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\collision\polyhedron.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\collision\polytope.cc
+# End Source File
+# End Group
+# Begin Group "console"
+
+# PROP Default_Filter "cc"
+# Begin Source File
+
+SOURCE=.\console\compiledEval.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\console\compiler.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\console\console.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\console\consoleFunctions.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\console\consoleInternal.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\console\consoleObject.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\console\consoleTypes.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\console\gram.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\console\scan.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\console\scriptObject.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\console\simBase.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\console\simDictionary.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\console\simManager.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\console\telnetConsole.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\console\telnetDebugger.cc
+# End Source File
+# End Group
+# Begin Group "core"
+
+# PROP Default_Filter "cc"
+# Begin Source File
+
+SOURCE=.\core\bitRender.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\bitStream.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\BitTables.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\dataChunker.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\dnet.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\fileObject.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\fileStream.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\filterStream.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\findMatch.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\idGenerator.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\memStream.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\nStream.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\nTypes.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\resDictionary.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\resizeStream.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\resManager.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\stringTable.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\tagDictionary.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\tVector.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\zipAggregate.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\zipHeaders.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\zipSubStream.cc
+# End Source File
+# End Group
+# Begin Group "crypt"
+
+# PROP Default_Filter "cc"
+# Begin Source File
+
+SOURCE=.\crypt\cryptMGF.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\crypt\cryptRandPool.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\crypt\cryptSHA1.cc
+# End Source File
+# End Group
+# Begin Group "dgl"
+
+# PROP Default_Filter "cc"
+# Begin Source File
+
+SOURCE=.\dgl\bitmapBM8.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\dgl\bitmapBMP.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\dgl\bitmapGIF.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\dgl\bitmapJpeg.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\dgl\bitmapPng.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\dgl\dgl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\dgl\dglMatrix.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\dgl\gBitmap.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\dgl\gFont.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\dgl\gPalette.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\dgl\gTexManager.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\dgl\lensFlare.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\dgl\materialList.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\dgl\materialPropertyMap.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\dgl\rectClipper.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\dgl\splineUtil.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\dgl\stripCache.cc
+# End Source File
+# End Group
+# Begin Group "editor"
+
+# PROP Default_Filter "cc"
+# Begin Source File
+
+SOURCE=.\editor\compTest.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\editor\creator.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\editor\editor.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\editor\editorButtonCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\editor\editorCheckboxCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\editor\editTSCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\editor\guiTerrPreviewCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\editor\missionAreaEditor.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\editor\terraformer.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\editor\terraformer_noise.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\editor\terraformerTexture.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\editor\terrainActions.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\editor\terrainEditor.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\editor\worldEditor.cc
+# End Source File
+# End Group
+# Begin Group "game"
+
+# PROP Default_Filter "cc"
+# Begin Source File
+
+SOURCE=.\game\ambientAudioManager.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\audioEmitter.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\badWordFilter.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\banList.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\bombSight.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\camera.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\cameraFXMgr.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\collisionTest.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\commanderMapIcon.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\debris.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\debugView.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\explosion.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\fireballAtmosphere.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\flyingVehicle.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\forceFieldBare.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\game.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\gameBase.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\gameConnection.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\gameConnectionEvents.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\gameConnectionMoves.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\gameFunctions.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\gameProcess.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\gameTSCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\guiNoMouseCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\guiPlayerView.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\guiServerBrowser.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\hoverVehicle.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\httpObject.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\item.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\lightning.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\linearProjectile.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\missionArea.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\missionMarker.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\motionBlurLine.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\net.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\netDispatch.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\netTest.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\particleEmitter.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\particleEngine.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\physicalZone.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\platTest.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\player.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\precipitation.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\projBomb.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\projectile.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\projELF.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\projEnergy.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\projFlareGrenade.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\projGrenade.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\projLinearFlare.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\projRepair.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\projSeeker.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\projShockLance.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\projSniper.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\projTargeting.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\projTracer.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\rigid.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\scopeAlwaysShape.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\sensor.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\serverQuery.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\shadow.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\shapeBase.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\shapeCollision.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\shapeImage.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\shieldImpact.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\shockwave.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\showTSShape.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\sphere.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\splash.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\staticShape.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\targetManager.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\tcpObject.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\trigger.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\tsStatic.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\turret.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\underLava.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\vehicle.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\vehicleBlocker.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\version.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\weaponBeam.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\wheeledVehicle.cc
+# End Source File
+# End Group
+# Begin Group "gui"
+
+# PROP Default_Filter "cc"
+# Begin Source File
+
+SOURCE=.\gui\channelVector.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiArrayCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiAviBitmapCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiBackgroundCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiBitmapCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiBubbleTextCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiButtonCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiCanvas.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiChannelVectorCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiChatMenuTreeCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiCheckBoxCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiChunkedBitmapCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiConsole.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiConsoleEditCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiConsoleTextCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiControl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiControlListPopup.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiDebugger.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiEditCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiFilterCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiFrameCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiInputCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiInspector.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiMessageVectorCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiMLTextCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiMLTextEditCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiMouseEventCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiPopUpCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiProgressCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiRadioCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiScrollCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiSliderCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiTextCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiTextEditCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiTextEditSliderCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiTextListCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiTreeViewCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiTSControl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiTypes.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiVoteCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiWindowCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\messageVector.cc
+# End Source File
+# End Group
+# Begin Group "hud"
+
+# PROP Default_Filter "cc"
+# Begin Source File
+
+SOURCE=.\hud\hudBitmapCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\hud\hudBitmapFrameCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\hud\hudClock.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\hud\hudCompass.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\hud\hudCrosshair.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\hud\hudCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\hud\hudEnergyDamage.cc
+# End Source File
+# End Group
+# Begin Group "interior"
+
+# PROP Default_Filter "cc"
+# Begin Source File
+
+SOURCE=.\interior\FloorPlanRes.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\interior\forceField.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\interior\interior.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\interior\interiorCollision.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\interior\interiorDebug.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\interior\interiorInstance.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\interior\interiorIO.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\interior\interiorLightAnim.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\interior\interiorLMManager.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\interior\interiorRender.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\interior\interiorRes.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\interior\interiorResObjects.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\interior\interiorSubObject.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\interior\itfdump.asm
+# End Source File
+# Begin Source File
+
+SOURCE=.\interior\lightUpdateGrouper.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\interior\mirrorSubObject.cc
+# End Source File
+# End Group
+# Begin Group "math"
+
+# PROP Default_Filter "cc"
+# Begin Source File
+
+SOURCE=.\math\mathTypes.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\math\mathUtils.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\math\mBox.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\math\mConsoleFunctions.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\math\mMath_C.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\math\mMathAMD.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\math\mMathFn.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\math\mMathSSE.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\math\mMatrix.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\math\mPlaneTransformer.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\math\mQuadPatch.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\math\mQuat.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\math\mRandom.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\math\mSolver.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\math\mSplinePatch.cc
+# End Source File
+# End Group
+# Begin Group "platform"
+
+# PROP Default_Filter "cc"
+# Begin Source File
+
+SOURCE=.\platform\gameInterface.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\platform\platformAssert.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\platform\platformMemory.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\platform\platformRedBook.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\platform\platformVideo.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\platform\profiler.cc
+# End Source File
+# End Group
+# Begin Group "platformWIN32"
+
+# PROP Default_Filter "cc"
+# Begin Source File
+
+SOURCE=.\platformWIN32\winAsmBlit.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\platformWIN32\winConsole.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\platformWIN32\winCPUInfo.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\platformWIN32\winD3DVideo.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\platformWIN32\winDInputDevice.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\platformWIN32\winDirectInput.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\platformWIN32\winFileio.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\platformWIN32\winFont.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\platformWIN32\winGL.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\platformWIN32\winInput.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\platformWIN32\winMath.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\platformWIN32\winMath_ASM.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\platformWIN32\winMemory.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\platformWIN32\winMutex.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\platformWIN32\winNet.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\platformWIN32\winOGLVideo.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\platformWIN32\winOpenAL.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\platformWIN32\winProcessControl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\platformWIN32\winRedbook.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\platformWIN32\winSemaphore.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\platformWIN32\winStrings.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\platformWIN32\winThread.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\platformWIN32\winTime.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\platformWIN32\winV2Video.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\platformWIN32\winWindow.cc
+# End Source File
+# End Group
+# Begin Group "scenegraph"
+
+# PROP Default_Filter "cc"
+# Begin Source File
+
+SOURCE=.\scenegraph\detailManager.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\scenegraph\lightManager.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\scenegraph\sceneGraph.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\scenegraph\sceneLighting.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\scenegraph\sceneRoot.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\scenegraph\sceneState.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\scenegraph\sceneTraversal.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\scenegraph\sgUtil.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\scenegraph\shadowVolumeBSP.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\scenegraph\windingClipper.cc
+# End Source File
+# End Group
+# Begin Group "shell"
+
+# PROP Default_Filter "cc"
+# Begin Source File
+
+SOURCE=.\shell\shellFancyArray.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\shell\shellFancyTextList.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\shell\shellScrollCtrl.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\shell\shellTextEditCtrl.cc
+# End Source File
+# End Group
+# Begin Group "sim"
+
+# PROP Default_Filter "cc"
+# Begin Source File
+
+SOURCE=.\sim\actionMap.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\sim\cannedChatDataBlock.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\sim\decalManager.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\sim\frameAllocator.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\sim\netConnection.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\sim\netEvent.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\sim\netGhost.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\sim\netObject.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\sim\netStringTable.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\sim\pathManager.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\sim\sceneObject.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\sim\simPath.cc
+# End Source File
+# End Group
+# Begin Group "terrain"
+
+# PROP Default_Filter "cc"
+# Begin Source File
+
+SOURCE=.\terrain\blender.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\terrain\bvQuadTree.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\terrain\FluidQuadTree.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\terrain\FluidRender.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\terrain\FluidSupport.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\terrain\Sky.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\terrain\Sun.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\terrain\terrCollision.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\terrain\terrData.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\terrain\terrLighting.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\terrain\terrRender.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\terrain\terrRender2.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\terrain\waterBlock.cc
+# End Source File
+# End Group
+# Begin Group "ts"
+
+# PROP Default_Filter "cc"
+# Begin Source File
+
+SOURCE=.\ts\tsAnimate.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ts\tsCollision.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ts\tsDecal.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ts\tsDump.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ts\tsIntegerSet.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ts\tsLastDetail.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ts\tsMaterialList.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ts\tsMesh.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ts\tsPartInstance.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ts\tsShape.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ts\tsShapeAlloc.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ts\tsShapeConstruct.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ts\tsShapeInstance.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ts\tsShapeOldRead.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ts\tsSortedMesh.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ts\tsThread.cc
+# End Source File
+# Begin Source File
+
+SOURCE=.\ts\tsTransform.cc
+# End Source File
+# End Group
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Group "ai headers"
+
+# PROP Default_Filter "h"
+# Begin Source File
+
+SOURCE=.\ai\aiConnection.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\aiNavJetting.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\aiNavStep.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\aiObjective.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\aiStep.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\aiTask.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graph.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphBase.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphBridge.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphBSP.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphData.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphDefines.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphFloorPlan.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphForceField.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphGenUtils.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphGroundPlan.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphGroundVisit.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphJetting.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphLocate.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphLOS.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphMath.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphNodes.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphPartition.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphPath.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphSearches.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphThreats.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\graphTransient.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\oVector.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\tBinHeap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ai\texturePreload.h
+# End Source File
+# End Group
+# Begin Group "audio headers"
+
+# PROP Default_Filter "h"
+# Begin Source File
+
+SOURCE=.\audio\audio.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\audio\audioBuffer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\audio\audioCodec.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\audio\audioCodecMiles.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\audio\audioDataBlock.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\audio\audioMss.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\audio\audioNet.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\audio\audioThread.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\audio\bufferQueue.h
+# End Source File
+# End Group
+# Begin Group "collsion headers"
+
+# PROP Default_Filter "h"
+# Begin Source File
+
+SOURCE=.\collision\abstractPolyList.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\collision\boxConvex.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\collision\clippedPolyList.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\collision\collision.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\collision\convex.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\collision\depthSortList.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\collision\earlyOutPolyList.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\collision\extrudedPolyList.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\collision\gjk.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\collision\planeExtractor.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\collision\polyhedron.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\collision\polytope.h
+# End Source File
+# End Group
+# Begin Group "console headers"
+
+# PROP Default_Filter "h"
+# Begin Source File
+
+SOURCE=.\console\ast.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\console\compiler.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\console\console.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\console\consoleInternal.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\console\consoleObject.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\console\consoleTypes.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\console\gram.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\console\objectTypes.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\console\simBase.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\console\simDictionary.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\console\telnetConsole.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\console\telnetDebugger.h
+# End Source File
+# End Group
+# Begin Group "core headers"
+
+# PROP Default_Filter "h"
+# Begin Source File
+
+SOURCE=.\core\bitMatrix.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\bitRender.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\bitSet.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\bitStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\BitTables.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\bitVector.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\bitVectorW.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\color.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\coreRes.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\dataChunker.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\dnet.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\fileio.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\fileObject.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\fileStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\filterStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\findMatch.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\idGenerator.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\llist.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\memstream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\polyList.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\realComp.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\resizeStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\resManager.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\stream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\stringTable.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\tagDictionary.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\tAlgorithm.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\tSparseArray.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\tVector.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\zipAggregate.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\zipHeaders.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\core\zipSubStream.h
+# End Source File
+# End Group
+# Begin Group "crypt headers"
+
+# PROP Default_Filter "h"
+# Begin Source File
+
+SOURCE=.\crypt\cryptMGF.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\crypt\cryptRandPool.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\crypt\cryptSHA1.h
+# End Source File
+# End Group
+# Begin Group "dgl headers"
+
+# PROP Default_Filter "h"
+# Begin Source File
+
+SOURCE=.\dgl\dgl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\dgl\gBitmap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\dgl\gChunkedTexManager.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\dgl\gFont.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\dgl\gPalette.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\dgl\gTexManager.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\dgl\lensFlare.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\dgl\materialList.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\dgl\materialPropertyMap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\dgl\rectClipper.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\dgl\splineUtil.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\dgl\stripCache.h
+# End Source File
+# End Group
+# Begin Group "editor headers"
+
+# PROP Default_Filter "h"
+# Begin Source File
+
+SOURCE=.\editor\creator.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\editor\editor.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\editor\editorButtonCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\editor\editorCheckboxCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\editor\editTSCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\editor\guiTerrPreviewCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\editor\missionAreaEditor.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\editor\terraformer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\editor\terraformer_noise.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\editor\terrainActions.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\editor\terrainEditor.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\editor\worldEditor.h
+# End Source File
+# End Group
+# Begin Group "game headers"
+
+# PROP Default_Filter "h"
+# Begin Source File
+
+SOURCE=.\game\ambientAudioManager.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\audioEmitter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\auth.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\badWordFilter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\banList.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\bombSight.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\camera.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\cameraFXMgr.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\collisionTest.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\commanderMapIcon.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\debris.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\debugView.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\explosion.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\fireballAtmosphere.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\flyingVehicle.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\forceFieldBare.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\game.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\gameBase.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\gameConnection.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\gameConnectionEvents.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\gameFunctions.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\gameTSCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\guiPlayerView.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\guiServerBrowser.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\hoverVehicle.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\httpObject.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\item.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\lightning.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\linearProjectile.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\missionArea.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\missionMarker.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\motionBlurLine.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\moveManager.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\netDispatch.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\objectTypes.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\particleEmitter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\particleEngine.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\physicalZone.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\player.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\precipitation.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\projBomb.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\projectile.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\projELF.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\projEnergy.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\projFlareGrenade.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\projGrenade.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\projLinearFlare.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\projRepair.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\projSeeker.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\projShockLance.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\projSniper.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\projTargeting.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\projTracer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\resource.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\rigid.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\sensor.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\serverQuery.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\shadow.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\shapeBase.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\shieldImpact.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\shockwave.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\showTSShape.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\sphere.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\splash.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\staticShape.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\targetManager.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\tcpObject.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\tribesGame.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\trigger.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\tsStatic.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\turret.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\underLava.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\vehicle.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\vehicleBlocker.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\version.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\weaponBeam.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game\wheeledVehicle.h
+# End Source File
+# End Group
+# Begin Group "gui headers"
+
+# PROP Default_Filter "h"
+# Begin Source File
+
+SOURCE=.\gui\channelVector.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiArrayCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiAviBitmapCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiBackgroundCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiBitmapCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiBubbleTextCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiButtonCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiCanvas.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiChannelVectorCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiChatMenuTreeCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiCheckBoxCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiConsole.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiConsoleEditCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiConsoleTextCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiControl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiDebugger.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiEditCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiFilterCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiFrameCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiHelpCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiInputCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiInspector.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiMessageVectorCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiMLTextCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiMLTextEditCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiMouseEventCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiPopUpCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiProgressCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiRadioCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiScrollCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiSliderCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiTextCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiTextEditCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiTextEditSliderCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiTextListCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiTreeViewCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiTSControl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiTypes.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiVoteCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\guiWindowCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui\messageVector.h
+# End Source File
+# End Group
+# Begin Group "hud headers"
+
+# PROP Default_Filter "h"
+# Begin Source File
+
+SOURCE=.\hud\hudBitmapCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\hud\hudBitmapFrameCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\hud\hudCtrl.h
+# End Source File
+# End Group
+# Begin Group "interior headers"
+
+# PROP Default_Filter "h"
+# Begin Source File
+
+SOURCE=.\interior\FloorPlanRes.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\interior\forceField.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\interior\interior.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\interior\interiorInstance.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\interior\interiorLMManager.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\interior\interiorRes.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\interior\interiorResObjects.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\interior\interiorSubObject.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\interior\lightUpdateGrouper.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\interior\mirrorSubObject.h
+# End Source File
+# End Group
+# Begin Group "math headers"
+
+# PROP Default_Filter "h"
+# Begin Source File
+
+SOURCE=.\math\mathIO.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\math\mathTypes.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\math\mathUtils.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\math\mBox.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\math\mConstants.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\math\mMath.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\math\mMathFn.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\math\mMatrix.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\math\mPlane.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\math\mPlaneTransformer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\math\mPoint.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\math\mQuadPatch.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\math\mQuat.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\math\mRandom.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\math\mRect.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\math\mSphere.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\math\mSplinePatch.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\math\mTrig.h
+# End Source File
+# End Group
+# Begin Group "platform headers"
+
+# PROP Default_Filter "h"
+# Begin Source File
+
+SOURCE=.\platform\3DFX.H
+# End Source File
+# Begin Source File
+
+SOURCE=.\platform\event.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\platform\gameInterface.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\platform\platform.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\platform\platformAssert.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\platform\platformAudio.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\platform\platformInput.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\platform\platformMutex.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\platform\platformRedBook.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\platform\platformSemaphore.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\platform\platformThread.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\platform\platformVideo.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\platform\profiler.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\platform\types.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\platform\typesLinux.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\platform\typesPPC.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\platform\typesWin32.h
+# End Source File
+# End Group
+# Begin Group "platformWIN32 headers"
+
+# PROP Default_Filter "h"
+# Begin Source File
+
+SOURCE=.\platformWIN32\gllist.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\platformWIN32\platformAL.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\platformWIN32\platformGL.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\platformWIN32\platformWin32.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\platformWIN32\winConsole.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\platformWIN32\winD3DVideo.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\platformWIN32\winDInputDevice.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\platformWIN32\winDirectInput.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\platformWIN32\winOGLVideo.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\platformWIN32\winV2Video.h
+# End Source File
+# End Group
+# Begin Group "scenegraph headers"
+
+# PROP Default_Filter "h"
+# Begin Source File
+
+SOURCE=.\scenegraph\detailManager.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\scenegraph\lightManager.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\scenegraph\sceneGraph.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\scenegraph\sceneLighting.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\scenegraph\sceneRoot.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\scenegraph\sceneState.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\scenegraph\sgUtil.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\scenegraph\shadowVolumeBSP.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\scenegraph\windingClipper.h
+# End Source File
+# End Group
+# Begin Group "shell headers"
+
+# PROP Default_Filter "h"
+# Begin Source File
+
+SOURCE=.\shell\shellFancyArray.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\shell\shellFancyTextList.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\shell\shellScrollCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\shell\shellTextEditCtrl.h
+# End Source File
+# End Group
+# Begin Group "sim headers"
+
+# PROP Default_Filter "h"
+# Begin Source File
+
+SOURCE=.\sim\actionMap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\sim\cannedChatDataBlock.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\sim\decalManager.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\sim\frameAllocator.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\sim\netConnection.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\sim\netObject.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\sim\netStringTable.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\sim\pathManager.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\sim\sceneObject.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\sim\simPath.h
+# End Source File
+# End Group
+# Begin Group "terrain headers"
+
+# PROP Default_Filter "h"
+# Begin Source File
+
+SOURCE=.\terrain\blender.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\terrain\bvQuadTree.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\terrain\Fluid.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\terrain\Sky.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\terrain\Sun.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\terrain\terrData.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\terrain\terrRender.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\terrain\waterBlock.h
+# End Source File
+# End Group
+# Begin Group "ts headers"
+
+# PROP Default_Filter "h"
+# Begin Source File
+
+SOURCE=.\ts\tsDecal.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ts\tsIntegerSet.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ts\tsLastDetail.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ts\tsMesh.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ts\tsPartInstance.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ts\tsShape.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ts\tsShapeAlloc.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ts\tsShapeConstruct.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ts\tsShapeInstance.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ts\tsSortedMesh.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ts\tsTransform.h
+# End Source File
+# End Group
+# End Group
+# Begin Source File
+
+SOURCE=.\targets.v12.mk
+# End Source File
+# End Target
+# End Project
diff --git a/Engine.plg b/Engine.plg
new file mode 100644
index 0000000..4dc73a0
--- /dev/null
+++ b/Engine.plg
@@ -0,0 +1,34 @@
+
+
+
+Build Log
+
+--------------------Configuration: Engine - Win32 Release--------------------
+
+
+Compiling interior/itfdump.asm
+mBox.cc
+mConsoleFunctions.cc
+mMathFn.cc
+mMath_C.cc
+mMatrix.cc
+mPlaneTransformer.cc
+mQuadPatch.cc
+mQuat.cc
+mRandom.cc
+mSolver.cc
+mSplinePatch.cc
+mathTypes.cc
+mathUtils.cc
+mMathAMD.cc
+mMathSSE.cc
+Linking out.VC6.RELEASE/v12_RELEASE.exe
+cp out.VC6.RELEASE/v12_RELEASE* ../example
+
+
+
+Results
+v12_RELEASE.exe - 0 error(s), 0 warning(s)
+
+
+
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..cbdb6c1
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,34 @@
+
+#--------------------------------------
+# include and verify the users mk/conf.mk
+
+-include ../mk/conf.mk
+
+ifndef CONFIG_STATUS
+doConfigure:
+ $(warning Configuration file not defined)
+ #@make --no-print-directory -f ../mk/configure.mk
+else
+ifeq ($(CONFIG_STATUS),INVALID)
+doConfigure:
+ $(warning Invalid Configuration file)
+ #@make --no-print-directory -f mk/configure.mk
+else
+ include ../mk/conf.$(COMPILER).$(OS).mk
+ include ../mk/conf.$(COMPILER).mk
+endif
+endif
+
+
+include targets.v12.mk
+
+include ../mk/conf.common.mk
+
+
+#default:
+# echo default.
+
+ifneq ($(MAKECMDGOALS),clean)
+-include $(addprefix $(DIR.OBJ)/, $(addsuffix $(DEP), $(basename $(filter %.cc %.c,$(SOURCES)))))
+endif
+
diff --git a/ai/aiConnection.cc b/ai/aiConnection.cc
new file mode 100644
index 0000000..83a464b
--- /dev/null
+++ b/ai/aiConnection.cc
@@ -0,0 +1,2975 @@
+//-----------------------------------------------------------------------------
+// V12 Engine
+//
+// Copyright (c) 2001 GarageGames.Com
+// Portions Copyright (c) 2001 by Sierra Online, Inc.
+//-----------------------------------------------------------------------------
+
+#include "core/realComp.h"
+#include "math/mMatrix.h"
+#include "console/console.h"
+#include "game/gameBase.h"
+#include "ai/aiConnection.h"
+#include "ai/aiStep.h"
+#include "ai/aiNavStep.h"
+#include "ai/aiTask.h"
+#include "ai/graphMath.h"
+#include "terrain/waterBlock.h"
+#include "scenegraph/sceneGraph.h"
+#include "game/vehicle.h"
+#include "platform/profiler.h"
+
+IMPLEMENT_CONOBJECT(AIConnection);
+
+static S32 gAIDetectionOffset = 0;
+
+AIConnection::AIConnection()
+{
+ mAIControlled = true;
+ mMoveMode = mMoveModePending = ModeStop;
+ mMoveLocation.set(0, 0, 0);
+ mNodeLocation.set(0, 0, 0);
+ mAimLocation.set(0, 0, 0);
+ mMoveSpeed = 0.0f;
+ mMoveTolerance = 0.25f;
+ mStep = NULL;
+ mCurrentTask = NULL;
+ mCurrentTaskTime = 0;
+ mPathDest.set(0, 0, 0);
+ mNewPath = false;
+
+ //clear the triggers
+ for (int i = 0; i < MaxTriggerKeys; i++)
+ mTriggers[i] = false;
+
+ mPrevNodeLocation.set(0, 0, 0);
+ mInitialLocation.set(0, 0, 0);
+
+ mEnergyReserve = 0.0f;
+ mEnergyFloat = 0.10f;
+ mEnergyRecharge = false;
+
+ //init engage vars
+ mEngageState = ChooseWeapon;
+ mProjectileName = StringTable->insert("");
+ mProjectile = NULL;
+ mEngageMinDistance = 20;
+ mEngageMaxDistance = 75;
+ mDistToTarg2D = -1.0f;
+ mLookAtTargetTimeMS = 0;
+ mFiring = false;
+ mTriggerCounter = 0;
+ mScriptTriggerCounter = -1;
+ mVictimTime = 0;
+ mSkillLevel = 0.5;
+ mChangeWeaponCounter = 0;
+ mTurretMountedId = 0;
+ mMountedImage = NULL;
+
+ //piloting vars
+ mPilotDestination.set(0, 0, 0);
+ mPilotAimLocation.set(0, 0, 0);
+ mPilotSpeed = 1.0f;
+ mPitchUpMax = -0.25;
+ mPitchDownMax = 0.1f;
+ mPitchIncMax = 0.05f;
+ mCurrentPitch = 0;
+ mPreviousPitch = 0;
+ mDesiredPitch = 0;
+ mPitchIncrement = 0;
+
+ mTargStillTimeMS = 0;
+ mTargPrevTimeMS = 0;
+ mTargPrevLocation[0].set(0, 0, 0);
+ mTargPrevLocation[1].set(0, 0, 0);
+ mTargPrevLocation[2].set(0, 0, 0);
+ mTargPrevLocation[3].set(0, 0, 0);
+
+ mPlayerDetectionIndex = 0;
+ mPlayerDetectionCounter = gAIDetectionOffset;
+ gAIDetectionOffset++;
+ if (gAIDetectionOffset >= 3)
+ gAIDetectionOffset = 0;
+ mDetectHiddenPeriod = 6000;
+ mBlindedTimer = 0;
+
+ //init target object vars
+ mTargetObject = NULL;
+ mObjectMode = DestroyObject;
+ mDistToObject2D = -1.0f;
+
+ //these are used for both mEngageTarget and mTargetObject
+ mRangeToTarget = 30;
+ mCheckTargetLOSCounter = 0;
+ mTargetInRange = false;
+ mTargetInSight = false;
+ mWeaponEnergy = 0.0f;
+ mWeaponErrorFactor = 1.0f;
+
+ //init evading vars
+ mEnemyProjectile = NULL;
+ mEvadingCounter = 0;
+ mIsEvading = false;
+
+ mAvoidingObject = NULL;
+ mStuckInitialized = false;
+
+ // LH. Just for the heck of it (and as way to familiarize with code) - added in all
+ // these that weren't being constructed.
+ mMoveDestination.set(0, 0, 0);
+ mEvadeLocation.set(0, 0, 0);
+ mImpactLocation.set(0, 0, 0);
+ mCorpseLocation.set(0, 0, 0);
+ mStateCounter = 0;
+ mDelayCounter = 0;
+ mPackCheckCounter = 0;
+ mAimAtLazedTarget = false;
+ mProjectileCounter = 0;
+ mPath.setTeam(getSensorGroup());
+ mStuckDestination.set(0,0,0);
+ mTargetPlayer = NULL;
+ mLocation.set(0,0,0);
+ mVelocity = mVelocity2D = mRotation = mHeadRotation =
+ mMuzzlePosition = mEyePosition = mTargLocation =
+ mTargVelocity = mTargVelocity2D = mTargRotation = mLocation;
+ mTargEnergy = 1.0;
+ mTargDamage = 0;
+}
+
+AIConnection::~AIConnection()
+{
+}
+
+void AIConnection::setSkillLevel(F32 level)
+{
+ mSkillLevel = getMax(0.0f, getMin(1.0f, level));
+}
+
+void AIConnection::setMoveSpeed(F32 speed)
+{
+ if (speed <= 0.0f)
+ mMoveSpeed = 0.0f;
+ else
+ mMoveSpeed = getMin(1.0f, speed);
+}
+
+void AIConnection::setMoveMode(S32 mode, bool abortStuckCode)
+{
+ if (mode < 0 || mode >= ModeCount)
+ mode = 0;
+
+ //if we're setting mode::express and we're currently stuck, let the stuck code
+ //finish "unsticking" the bot... calling setMoveDestination will abort the stuck code
+ if (mMoveMode == ModeStuck && mode != ModeStop && !abortStuckCode)
+ return;
+
+ //make sure we're not moving if in the middle of a jet...
+ mMoveModePending = mode;
+ if (!mNavUsingJet)
+ mMoveMode = mode;
+}
+
+void AIConnection::setMoveTolerance(F32 tolerance)
+{
+ mMoveTolerance = getMax(0.1f, tolerance);
+}
+
+void AIConnection::setMoveDestination(const Point3F &location)
+{
+ if (mMoveDestination != location && mMoveMode == ModeStuck)
+ {
+ setMoveMode(ModeExpress, true);
+ mStuckLocation.set(0, 0, 0);
+ }
+ mMoveDestination = location;
+}
+
+void AIConnection::setMoveLocation(const Point3F &location)
+{
+ mMoveLocation = location;
+}
+
+void AIConnection::setAimLocation(const Point3F &location)
+{
+ mAimLocation = location;
+}
+
+bool AIConnection::setScriptAimLocation(const Point3F &location, S32 duration)
+{
+ //can't set through scripts if the bots are aiming at either an object, or an engage player
+ if (mTargetPlayer || bool(mTargetObject))
+ return false;
+
+ setAimLocation(location);
+ mLookAtTargetTimeMS = getMax(mLookAtTargetTimeMS, (S32)Sim::getCurrentTime() + duration);
+
+ return true;
+}
+
+void AIConnection::setPilotPitchRange(F32 pitchUpMax, F32 pitchDownMax, F32 pitchIncMax)
+{
+ mPitchUpMax = pitchUpMax;
+ mPitchDownMax = pitchDownMax;
+ mPitchIncMax = pitchIncMax;
+}
+
+void AIConnection::setPilotDestination(const Point3F &dest, F32 maxSpeed)
+{
+ //must always aim where you're flying to
+ mPilotDestination = dest;
+ mPilotAimLocation = dest;
+ mPilotSpeed = getMax(0.0f, getMin(1.0f, maxSpeed));
+}
+
+void AIConnection::setPilotAimLocation(const Point3F &aimLocation)
+{
+ //if you're aiming, you can't move
+ mPilotAimLocation = aimLocation;
+ mPilotSpeed = 0;
+}
+
+void AIConnection::setWeaponInfo(const char *projectile, S32 minDist, S32 maxDist, S32 triggerCount, F32 energyRequired, F32 errorFactor)
+{
+ //set the non-pointer params
+ mEngageMinDistance = minDist;
+ mEngageMaxDistance = maxDist;
+ mTriggerCounter = triggerCount;
+ mScriptTriggerCounter = -1;
+ mWeaponEnergy = energyRequired;
+ mWeaponErrorFactor = errorFactor;
+
+ if ((! projectile) || (! projectile[0]) || (! dStricmp(projectile, "NoAmmo")))
+ mProjectile = NULL;
+ else
+ {
+ if (! Sim::findObject(projectile, mProjectile))
+ {
+ Con::printf("setWeaponInfo() failed - unable to find datablock: %s", projectile);
+ mProjectile = NULL;
+ }
+ }
+ if (mProjectile)
+ mProjectileName = StringTable->insert(projectile);
+ else
+ mProjectileName = StringTable->insert("");
+}
+
+void AIConnection::setEnergyLevels(F32 eReserve, F32 eFloat)
+{
+ mEnergyReserve = eReserve;
+ mEnergyFloat = eFloat;
+}
+
+void AIConnection::setEngageTarget(GameConnection *target)
+{
+ //reset the bools if we're aiming at someone new...
+ //note, these extraneous retarded checks are because mTargetObject is a SimObjectPtr...
+ //won't compile just comparing target != mEngageTarget...
+ if (target != NULL && ((! bool(mEngageTarget)) || (target->getId() != mEngageTarget->getId())))
+ {
+ //reset some engagement vars
+ mTargetInRange = false;
+ mTargetInSight = false;
+ mEngageState = ChooseWeapon;
+ mFiring = false;
+ mTriggerCounter = 0;
+ mScriptTriggerCounter = -1;
+ }
+
+ mEngageTarget = target;
+ mTargetPlayer = NULL;
+ if ((! bool(mEngageTarget)) || (! mEngageTarget->getControlObject()))
+ mEngageTarget = NULL;
+ else
+ mTargetPlayer = dynamic_cast(mEngageTarget->getControlObject());
+
+ //make sure we actually got a target
+ if (! mTargetPlayer)
+ mEngageTarget = NULL;
+
+}
+
+S32 AIConnection::getEngageTarget()
+{
+ //see if have someone to shoot at
+ Player *targetPlayer = NULL;
+ if ((! bool(mEngageTarget)) || (! mEngageTarget->getControlObject()))
+ {
+ mTargetPlayer = NULL;
+ mEngageTarget = NULL;
+ return -1;
+ }
+ else
+ targetPlayer = dynamic_cast(mEngageTarget->getControlObject());
+
+ //see if the target is dead
+ if (! targetPlayer || ! dStricmp(targetPlayer->getStateName(), "dead"))
+ {
+ mTargetPlayer = NULL;
+ mEngageTarget = NULL;
+ return -1;
+ }
+
+ //return the id of the target
+ return mEngageTarget->getId();
+}
+
+void AIConnection::setVictim(GameConnection *victim, Player *corpse)
+{
+ mVictim = victim;
+ mCorpse = corpse;
+ mVictimTime = Sim::getCurrentTime();
+}
+
+S32 AIConnection::getVictimCorpse()
+{
+ if (bool(mCorpse))
+ return mCorpse->getId();
+ else
+ return -1;
+}
+
+S32 AIConnection::getVictimTime()
+{
+ return mVictimTime;
+}
+
+void AIConnection::setTargetObject(ShapeBase *targetObject, F32 range, S32 objectMode)
+{
+ //reset the bools if we're not aiming at a player, and we have a new target object
+ //note, these extraneous retarded checks are because mTargetObject is a SimObjectPtr...
+ //won't compile just comparing targetObject != mTargetObject...
+ if (! mTargetPlayer && (! targetObject || ! bool(mTargetObject) || targetObject->getId() != mTargetObject->getId()))
+ {
+ mTargetInRange = false;
+ mTargetInSight = false;
+ mEngageState = ChooseWeapon;
+ mFiring = false;
+ mTriggerCounter = 0;
+ mScriptTriggerCounter = -1;
+ }
+
+ mTargetObject = targetObject;
+ mRangeToTarget = range;
+ mObjectMode = objectMode;
+}
+
+S32 AIConnection::getTargetObject()
+{
+ if (bool(mTargetObject))
+ return mTargetObject->getId();
+ else
+ return -1;
+}
+
+void AIConnection::setPathDest(const Point3F * dest)
+{
+ if( dest )
+ mPathDest = * dest;
+ mNewPath = true;
+}
+
+const Point3F * AIConnection::getPathDest()
+{
+ if( mNewPath ){
+ mNewPath = false;
+ return & mPathDest;
+ }
+ return NULL;
+}
+
+// Pass down jetting abilities to the path machinery-
+void AIConnection::setPathCapabilities(Player * )
+{
+ PROFILE_START(AI_setPathCapabilities);
+ JetManager::Ability ability;
+
+// Tribes player jetting was remove from the player class.
+#if 0
+ player->getJetAbility(ability.acc, ability.dur, ability.v0);
+#else
+ ability.acc = 0;
+ ability.dur = 0;
+ ability.v0 = 0;
+#endif
+
+ mPath.setJetAbility(ability);
+ PROFILE_END();
+}
+
+F32 AIConnection::getPathDistance(const Point3F &destination, const Point3F &source)
+{
+ //find our current location
+ Point3F sourceLocation = source;
+ if (sourceLocation == Point3F(-1, -1, -1))
+ {
+ //make sure we have a valid player control object
+ Player *myPlayer = NULL;
+ if (! getControlObject())
+ return -1;
+ myPlayer = dynamic_cast(getControlObject());
+ if (! myPlayer)
+ return -1;
+
+ setPathCapabilities(myPlayer);
+
+ MatrixF const& tempTransform = myPlayer->getTransform();
+ tempTransform.getColumn(3, &sourceLocation);
+
+ //make sure the client can actually get to the destination
+ if (! mPath.canReachLoc(destination))
+ return -1;
+ }
+
+ //this is a weird change - want the bots to avoid vertical movement, so the distance function will
+ //exaggerate the z component...
+ Point3F distVec = sourceLocation - destination;
+ return mSqrt(distVec.x * distVec.x + distVec.y * distVec.y + 9 * distVec.z * distVec.z);
+
+ //this is just a euclidean distance anyways... if it becomes an actual path distance again, use it...
+ //return NavigationGraph::fastDistance(sourceLocation, destination);
+}
+
+F32 AIConnection::getPathDistRemaining(F32 maxDist)
+{
+ //if the path is current, use the path distRemaining function
+ if (mPath.isPathCurrent())
+ return mPath.distRemaining(maxDist);
+
+ //otherwise, return the euclidean dist
+ else
+ return getMin(maxDist, (mLocation - mMoveDestination).len());
+}
+
+Point3F AIConnection::getLOSLocation(const Point3F &targetPoint, F32 minDistance, F32 maxDistance, const Point3F &nearPoint)
+{
+ //find our current location
+ Point3F sourceLocation;
+ Player *myPlayer = NULL;
+ if (! getControlObject())
+ return targetPoint;
+ myPlayer = dynamic_cast(getControlObject());
+ if (! myPlayer)
+ return targetPoint;
+ MatrixF const& tempTransform = myPlayer->getTransform();
+ tempTransform.getColumn(3, &sourceLocation);
+
+ Point3F nearLocation = nearPoint;
+ if (nearLocation == Point3F(-1, -1, -1))
+ nearLocation = sourceLocation;
+
+ Point3F graphPoint;
+ if (minDistance < 0)
+ graphPoint = NavigationGraph::findLOSLocation(sourceLocation, targetPoint, 0, SphereF(nearLocation, 1e6), maxDistance);
+ else
+ graphPoint = NavigationGraph::findLOSLocation(sourceLocation, targetPoint, minDistance, SphereF(nearLocation, minDistance / 2.0f), maxDistance);
+ return graphPoint;
+}
+
+Point3F AIConnection::getHideLocation(const Point3F &targetPoint, F32 range, const Point3F &nearPoint, F32 hideLength)
+{
+ //find our current location
+ Point3F sourceLocation = nearPoint;
+ if (sourceLocation == Point3F(-1, -1, -1))
+ {
+ Player *myPlayer = NULL;
+ if (! getControlObject())
+ return targetPoint;
+ myPlayer = dynamic_cast(getControlObject());
+ if (! myPlayer)
+ return targetPoint;
+ MatrixF const& tempTransform = myPlayer->getTransform();
+ tempTransform.getColumn(3, &sourceLocation);
+ }
+
+ if (hideLength <= 0)
+ return NavigationGraph::hideOnSlope(sourceLocation, targetPoint, range, 20);
+ else
+ return NavigationGraph::hideOnDistance(sourceLocation, targetPoint, range, hideLength);
+}
+
+void AIConnection::process(ShapeBase *ctrlObject)
+{
+ if (!ctrlObject)
+ return;
+
+ PROFILE_START(AI_process);
+
+ //update the task queue
+ AITask *highestWeightTask = NULL;
+ S32 i;
+ for (i = 0; i < mTaskList.size(); i++)
+ {
+ AITask *task = mTaskList[i];
+ task->calcWeight(this);
+
+ //see if it's the highest so far
+ if ((! highestWeightTask) || (task->getWeight() > highestWeightTask->getWeight()))
+ highestWeightTask = task;
+ }
+
+ // Path needs team for avoiding threats-
+ mPath.setTeam(getSensorGroup());
+
+ //now see if we have a new task
+ if (highestWeightTask && mCurrentTask != highestWeightTask)
+ {
+ if (bool(mCurrentTask))
+ mCurrentTask->retire(this);
+ highestWeightTask->assume(this);
+ mCurrentTask = highestWeightTask;
+ mCurrentTaskTime = Sim::getCurrentTime();
+ }
+
+ //monitor the current task
+ if (mCurrentTask) {
+ PROFILE_START(AI_MonitorTask);
+ mCurrentTask->monitor(this);
+ PROFILE_END();
+ }
+
+ //see if our control object is a player
+ Player *myPlayer = NULL;
+ myPlayer = dynamic_cast(ctrlObject);
+ if (myPlayer)
+ {
+ //initialize the process vars
+ initProcessVars(myPlayer);
+
+ //next, process the current step
+ if (bool(mStep))
+ mStep->process(this, myPlayer);
+
+ //process the engagement
+ if (mTurretMountedId <= 0) {
+ PROFILE_START(AI_EngagementOuter);
+ processEngagement(myPlayer);
+ PROFILE_END();
+ }
+
+ //finally, process the movement itself
+ if (!myPlayer->isMounted())
+ processMovement(myPlayer);
+ else
+ processVehicleMovement(myPlayer);
+ }
+
+ //else see if we're trying to pilot a vehicle
+ else
+ {
+ Vehicle *myVehicle;
+ myVehicle = dynamic_cast(ctrlObject);
+ if (myVehicle)
+ {
+ processPilotVehicle(myVehicle);
+ }
+ }
+
+ //debug
+ aiDebugStuff();
+ PROFILE_END();
+}
+
+// LH- changed this to not normalize when there's a zero length. Dividing by zero
+// results in interupts. Instead do normalization here checking lengths first.
+F32 AIConnection::get2DDot(const Point3F &vec1, const Point3F &vec2)
+{
+ //create the 2D vectors and check for zero length.
+ Point3F vec1_2D(vec1.x, vec1.y, 0);
+ F32 len1 = vec1_2D.len();
+ if (len1 < __EQUAL_CONST_F)
+ return 1;
+
+ Point3F vec2_2D(vec2.x, vec2.y, 0);
+ F32 len2 = vec2_2D.len();
+ if (len2 < __EQUAL_CONST_F)
+ return 1;
+
+ // Normalize and return the dot-
+ return mDot(vec1_2D /= len1, vec2_2D /= len2);
+}
+
+F32 AIConnection::get2DAngle(const Point3F &endPt, const Point3F &basePt)
+{
+ Point3F direction2D = endPt - basePt;
+ F32 angularDirection;
+ direction2D.z = 0;
+
+ if (! isZero(direction2D.y))
+ angularDirection = mAtan(direction2D.x, direction2D.y);
+ else if (direction2D.x < 0)
+ angularDirection = -M_PI / 2.0f;
+ else
+ angularDirection = M_PI / 2.0f;
+
+ if (angularDirection < 0)
+ angularDirection += M_2PI;
+ else if (angularDirection >= M_2PI)
+ angularDirection -= M_2PI;
+
+ //note: return value is always between 0 and (2 * Pi)
+ return angularDirection;
+}
+
+Point3F AIConnection::dopeAimLocation(const Point3F &startLocation, const Point3F &aimLocation)
+{
+ //find the "horizontal" orthogonal vector
+ Point3F horzOrth;
+ if (! findCrossVector(startLocation - aimLocation, Point3F(0, 0, 1), &horzOrth))
+ return aimLocation;
+
+ //now find the "vertical" orthogonal vector
+ Point3F vertOrth;
+ if (! findCrossVector(startLocation - aimLocation, horzOrth, &vertOrth))
+ return aimLocation;
+
+ //now we have the horizonal and vertical components of the plane which is perpendicular to
+ //the direction we are firing...
+
+ //determine the radius factor
+ F32 radiusFactor;
+ if (mSkillLevel == 1.0f)
+ radiusFactor = 0.0f;
+ else
+ radiusFactor = 0.04 + (1.0f - mSkillLevel) * 0.2;
+
+ //calculate the radius error
+ F32 radiusError = (startLocation - aimLocation).len() * radiusFactor * mWeaponErrorFactor;
+ if (! mProjectile->isBallistic && mTargStillTimeMS > 0 && radiusError > 0)
+ {
+ //begin honing in after 3 seconds, and over the next 3 seconds
+ S32 elapsedTime = Sim::getCurrentTime() - mTargStillTimeMS - 3000;
+ if (elapsedTime > 0)
+ radiusError = radiusError * (F32(3000 - getMin(elapsedTime, 3000)) / 3000.0f);
+ }
+
+ F32 horzError = gRandGen.randF() * radiusError * (gRandGen.randF() < 0.5f ? -1.0f : 1.0f);
+ F32 vertError = gRandGen.randF() * radiusError * (gRandGen.randF() < 0.5f ? -1.0f : 1.0f);
+
+ Point3F dopedAimLocation = aimLocation + (horzOrth * horzError) + (vertOrth * vertError);
+ return dopedAimLocation;
+}
+
+F32 AIConnection::getOutdoorRadius(const Point3F &location)
+{
+ F32 freedomRadius;
+ if (mPath.locationIsOutdoors(location, &freedomRadius))
+ return freedomRadius;
+ else
+ return -1;
+}
+
+void AIConnection::initProcessVars(Player *player)
+{
+ PROFILE_START(AI_initProcessVars);
+
+ //find my current location (global coords), velocity, energy, damage, etc...
+ MatrixF const& tempTransform = player->getTransform();
+ tempTransform.getColumn(3, &mLocation);
+ mVelocity = player->getVelocity();
+ mVelocity2D = mVelocity;
+ mVelocity2D.z = 0;
+ mRotation = player->getRotation();
+ mHeadRotation = player->getHeadRotation();
+ mEnergy = player->getEnergyValue();
+ mDamage = player->getDamageValue();
+
+ if (mEnergy < mEnergyReserve)
+ mEnergyRecharge = true;
+ else if (mEnergy > mEnergyReserve + mEnergyFloat)
+ mEnergyRecharge = false;
+
+ if (((mEnergy > mEnergyReserve + mEnergyFloat) || (mEnergy > mEnergyReserve && !mEnergyRecharge)) &&
+ (mEnergy > mWeaponEnergy - mEnergyFloat))
+ mEnergyAvailable = true;
+ else
+ mEnergyAvailable = false;
+
+ //find the muzzle point
+ //player->getMuzzlePoint(0, &mMuzzlePosition);
+ player->getMuzzlePointAI(0, &mMuzzlePosition);
+
+ //find the eye transform
+ MatrixF eyeTransform;
+ player->getEyeTransform(&eyeTransform);
+ eyeTransform.getColumn(3, &mEyePosition);
+
+ //find out far we have to go, and if we're heading in the right direction
+ mDistToNode2D = (Point3F(mNodeLocation.x, mNodeLocation.y, 0) -
+ Point3F(mLocation.x, mLocation.y, 0)).len();
+
+ //find out how far off course our velocity is taking us
+ mDotOffCourse = get2DDot(mNodeLocation - mLocation, mVelocity2D);
+ mDotOffCourse = mClampF(mDotOffCourse, -1.0f, 1.0f);
+
+ //find out the difference between them
+ F32 dummy;
+ bool outdoors = mPath.locationIsOutdoors(mLocation, &dummy);
+ mHeadingDownhill = false;
+ if (mVelocity2D.len() >= player->getMaxForwardVelocity() * 0.85f && outdoors && !mInWater)
+ {
+ //make sure we're heading in approximately the right direction (+- 30 deg or so)
+ if (mDotOffCourse > 0.85)
+ {
+ Point3F myDirection2D = mVelocity2D;
+ if (myDirection2D.len() < 0.001f)
+ myDirection2D.set(0, 1, 0);
+ myDirection2D.normalize();
+ U32 mask = TerrainObjectType | InteriorObjectType | WaterObjectType;
+ RayInfo ray1Info, ray2Info;
+
+ Point3F startPt = mLocation;
+ Point3F endPt = mLocation;
+ endPt.z -= 5.0f;
+
+ //if we're not within 1.0 m of the ground, we can't ski...
+ player->disableCollision();
+ if (! gServerContainer.castRay(startPt, endPt, mask, &ray1Info))
+ mHeadingDownhill = true;
+ else
+ {
+ //run another LOS to see if we are heading downhill
+ startPt += myDirection2D;
+ endPt += myDirection2D;
+
+ //if we don't hit anything, we're (on the edge of a cliff?) heading downhill
+ if (! gServerContainer.castRay(startPt, endPt, mask, &ray2Info))
+ mHeadingDownhill = true;
+ else if (ray1Info.point.z > ray2Info.point.z)
+ mHeadingDownhill = true;
+ }
+ player->enableCollision();
+ }
+ }
+
+ //see if we have a target object
+ if (bool(mTargetObject))
+ {
+ MatrixF const& objTransform = mTargetObject->getTransform();
+ objTransform.getColumn(3, &mObjectLocation);
+ mDistToObject2D = (Point3F(mLocation.x, mLocation.y, 0.0f) -
+ Point3F(mObjectLocation.x, mObjectLocation.y, 0.0f)).len();
+ }
+
+ //see if have someone to shoot at
+ mTargetPlayer = NULL;
+ if ((! bool(mEngageTarget)) || (! mEngageTarget->getControlObject()))
+ mEngageTarget = NULL;
+ else
+ mTargetPlayer = dynamic_cast(mEngageTarget->getControlObject());
+
+ if (mTargetPlayer)
+ {
+ //find the target location (global coords), velocity, energy, damage, etc...
+ MatrixF const& targTransform = mTargetPlayer->getTransform();
+ targTransform.getColumn(3, &mTargLocation);
+ mTargVelocity = mTargetPlayer->getVelocity();
+ mTargVelocity2D = mTargVelocity;
+ mTargVelocity2D.z = 0;
+ mTargRotation = mTargetPlayer->getRotation();
+ mTargEnergy = mTargetPlayer->getEnergyValue();
+ mTargDamage = mTargetPlayer->getDamageValue();
+
+ //see if the target is standing still
+ if (mTargVelocity.len() > 4.0f)
+ mTargStillTimeMS = 0;
+ else if (mTargStillTimeMS == 0)
+ mTargStillTimeMS = Sim::getCurrentTime();
+
+ //keep an array of prev locations to simulate a slower response time... (up to 600 ms)
+ if (Sim::getCurrentTime() - mTargPrevTimeMS > 300)
+ {
+ mTargPrevTimeMS = Sim::getCurrentTime();
+ mTargPrevLocation[3] = mTargPrevLocation[2];
+ mTargPrevLocation[2] = mTargPrevLocation[1];
+ mTargPrevLocation[1] = mTargPrevLocation[0];
+ mTargetPlayer->getWorldBox().getCenter(&mTargPrevLocation[0]);
+ }
+ }
+ else
+ mEngageTarget = NULL;
+
+ if (mTargetPlayer)
+ {
+ //see if the target is dead
+ if (! dStricmp(mTargetPlayer->getStateName(), "dead"))
+ {
+ mTargetPlayer = NULL;
+ mEngageTarget = NULL;
+ }
+ else
+ {
+ //find the 2D distance between you and the target
+ Point3F dist2DVector = mTargLocation - mLocation;
+ dist2DVector.z = 0;
+ mDistToTarg2D = dist2DVector.len();
+ }
+ }
+
+ //see if we're outdoors
+ mOutdoors = (getOutdoorRadius(mLocation) > 0);
+
+ //see if either we or the targ is in water
+ mInWater = false;
+ mTargInWater = false;
+ mObjectInWater = false;
+ SimpleQueryList sql;
+
+ gServerSceneGraph->getWaterObjectList(sql);
+
+// gServerContainer.findObjects(WaterObjectType, SimpleQueryList::insertionCallback, S32(&sql));
+
+ for (U32 i = 0; i < sql.mList.size(); i++)
+ {
+ WaterBlock* pBlock = dynamic_cast(sql.mList[i]);
+ if (pBlock)
+ {
+ if (pBlock->isPointSubmergedSimple(mLocation) || pBlock->isPointSubmergedSimple(mMuzzlePosition))
+ mInWater = true;
+
+ if (mTargetPlayer)
+ {
+ if (pBlock->isPointSubmergedSimple(mTargLocation))
+ mTargInWater = true;
+ }
+
+ if (bool(mTargetObject))
+ {
+ if (pBlock->isPointSubmergedSimple(mObjectLocation))
+ mObjectInWater = true;
+ }
+ }
+ }
+
+ //now see if we're within range of either the engageTarget, or the targetObject
+ if (--mCheckTargetLOSCounter <= 0)
+ {
+ mCheckTargetLOSCounter = 10;
+ mTargetInSight = false;
+ if (mTargetPlayer || bool(mTargetObject))
+ {
+ F32 rangeDist;
+ Point3F rangeLocation;
+ if (mTargetPlayer)
+ {
+ rangeDist = mDistToTarg2D;
+ mTargetPlayer->getWorldBox().getCenter(&rangeLocation);
+ }
+ else
+ {
+ rangeDist = mDistToObject2D;
+ mTargetObject->getWorldBox().getCenter(&rangeLocation);
+ }
+ if (rangeDist <= mRangeToTarget)
+ {
+ //see if we have line of site
+ RayInfo rayInfo;
+ Point3F startPt = mMuzzlePosition;
+ Point3F endPt = rangeLocation;
+ U32 mask = TerrainObjectType | InteriorObjectType;
+ if (! gServerContainer.castRay(startPt, endPt, mask, &rayInfo))
+ mTargetInSight = true;
+ }
+ }
+ }
+
+ //reset the weaponEnergy level if req'd
+ if (! bool(mTargetPlayer))
+ mWeaponEnergy = 0.0f;
+
+ //set the corpse vars
+ if (bool(mCorpse))
+ {
+ MatrixF const& corpseTransform = mCorpse->getTransform();
+ corpseTransform.getColumn(3, &mCorpseLocation);
+ }
+ PROFILE_END();
+}
+
+void AIConnection::updateDetectionTable(Player *player)
+{
+ //if the player has been blinded, no updates to the table can be made
+ if (Sim::getCurrentTime() < mBlindedTimer)
+ return;
+
+ //time slice the detection LOS calls...
+ mPlayerDetectionCounter--;
+ if (mPlayerDetectionCounter <= 0)
+ {
+ SimGroup *clientGroup = Sim::getClientGroup();
+ AssertFatal(clientGroup, "Unable to get the client group");
+
+ //make sure we have more than one client
+ if (clientGroup->size() <= 1)
+ return;
+
+ //find the next client index
+ mPlayerDetectionIndex++;
+ if (mPlayerDetectionIndex >= clientGroup->size())
+ mPlayerDetectionIndex = 0;
+
+ //get the client from the group
+ GameConnection *targClient = static_cast((*clientGroup)[mPlayerDetectionIndex]);
+
+ //make sure it's not me...
+ S32 targClientId = targClient->getId();
+ if (getId() == targClientId)
+ {
+ mPlayerDetectionIndex++;
+ if (mPlayerDetectionIndex >= clientGroup->size())
+ mPlayerDetectionIndex = 0;
+ targClient = static_cast((*clientGroup)[mPlayerDetectionIndex]);
+ targClientId = targClient->getId();
+ }
+
+ //now find this target in the table
+ PlayerDetectionEntry *targEntry = NULL;
+ for (S32 i = 0; i < mPlayerDetectionTable.size(); i++)
+ {
+ if (mPlayerDetectionTable[i].playerId == targClientId)
+ targEntry = &(mPlayerDetectionTable[i]);
+ }
+ //if the entry wasn't found, create one... (and yes, this table will never shrink)
+ if (!targEntry)
+ {
+ PlayerDetectionEntry tempEntry;
+ tempEntry.playerId = targClientId;
+ tempEntry.playerLOS = false;
+ tempEntry.playerLOSTime = 0;
+ tempEntry.playerLastPosition.set(0, 0, 0);
+ mPlayerDetectionTable.push_front(tempEntry);
+ targEntry = &(mPlayerDetectionTable.first());
+ }
+
+ //make sure the client has a player
+ Player *targPlayer = NULL;
+ if (! targClient->getControlObject())
+ {
+ if (targEntry->playerLOS)
+ {
+ targEntry->playerLOS = false;
+ targEntry->playerLOSTime = Sim::getCurrentTime();
+ }
+ return;
+ }
+ targPlayer = dynamic_cast(targClient->getControlObject());
+ if (! targPlayer)
+ {
+ if (targEntry->playerLOS)
+ {
+ targEntry->playerLOS = false;
+ targEntry->playerLOSTime = Sim::getCurrentTime();
+ }
+ return;
+ }
+
+ //now cast line of sight, see if we can see the player
+ MatrixF myEyeTransform, targEyeTransform;
+ Point3F myEyePosition, targEyePosition;
+ player->getEyeTransform(&myEyeTransform);
+ myEyeTransform.getColumn(3, &myEyePosition);
+ targPlayer->getEyeTransform(&targEyeTransform);
+ targEyeTransform.getColumn(3, &targEyePosition);
+
+ //if the target player is cloaked, they can't be detected
+ bool targPlayerIsCloaked = targPlayer->getCloakedState();
+
+ Point3F losVector = targEyePosition - myEyePosition;
+ F32 distToTarg = losVector.len();
+ bool clearLOSToTarg;
+ if (mSkillLevel >= 1.0f)
+ clearLOSToTarg = true;
+ else if (distToTarg < 0.5f)
+ clearLOSToTarg = true;
+ else if (targPlayerIsCloaked)
+ clearLOSToTarg = false;
+ else if (distToTarg > 300.0f)
+ clearLOSToTarg = false;
+ else
+ {
+ //first, see if we're facing the right way...
+ Point3F facingVector = mAimLocation - mLocation;
+ F32 visibleRange = 0.4 - (0.4 * mSkillLevel);
+ if (facingVector.len() < 0.5f)
+ clearLOSToTarg = false;
+ else if (get2DDot(facingVector, losVector) < visibleRange && distToTarg > 1.5f)
+ clearLOSToTarg = false;
+ else
+ {
+ //see if we have line of site
+ if (losVector.len() < 0.001f)
+ losVector.set(0, 1, 0);
+ losVector.normalize();
+ RayInfo rayInfo;
+ Point3F startPt = myEyePosition;
+ Point3F endPt = myEyePosition + (getMin(300.0f, distToTarg) * losVector);
+ U32 mask = TerrainObjectType | InteriorObjectType;
+ if (! gServerContainer.castRay(startPt, endPt, mask, &rayInfo))
+ clearLOSToTarg = true;
+ else
+ clearLOSToTarg = false;
+ }
+ }
+
+ //set the time if the status changed
+ if (clearLOSToTarg != targEntry->playerLOS)
+ targEntry->playerLOSTime = Sim::getCurrentTime();
+
+ //set the bool
+ targEntry->playerLOS = clearLOSToTarg;
+
+ //update the position if required
+ if (clearLOSToTarg)
+ targPlayer->getWorldBox().getCenter(&targEntry->playerLastPosition);
+
+ //don't forget to set the timeslice counter
+ mPlayerDetectionCounter = 30 / (clientGroup->size() - 1);
+ if (mPlayerDetectionCounter < 3)
+ mPlayerDetectionCounter = 3;
+ }
+}
+
+void AIConnection::setBlinded(S32 duration)
+{
+ //can't blind the Kidney Bot!!!
+ if (mSkillLevel >= 1.0f)
+ return;
+
+ //first, set the blinded timer
+ mBlindedTimer = Sim::getCurrentTime() + duration;
+
+ //now loop through the table, and anyone who he has LOS to, he now doesn't
+ PlayerDetectionEntry *targEntry = NULL;
+ for (S32 i = 0; i < mPlayerDetectionTable.size(); i++)
+ {
+ targEntry = &(mPlayerDetectionTable[i]);
+ if (targEntry->playerLOS)
+ {
+ targEntry->playerLOS = false;
+ targEntry->playerLOSTime = Sim::getCurrentTime();
+ }
+ }
+
+ //next, set the evade location to make us react...
+ Point3F dangerLocation = mLocation;
+ dangerLocation.x += -2.0f + (gRandGen.randF() * 4.0f);
+ dangerLocation.y += -2.0f + (gRandGen.randF() * 4.0f);
+ setEvadeLocation(dangerLocation);
+ mEvadingCounter = 30;
+}
+
+void AIConnection::clientDetected(S32 targId)
+{
+ //first, make sure we're not detecting ourself
+ if (getId() == targId)
+ return;
+
+ SimGroup *clientGroup = Sim::getClientGroup();
+ AssertFatal(clientGroup, "Unable to get the client group");
+
+ GameConnection *targClient = NULL;
+ for (SimGroup::iterator itr = clientGroup->begin(); itr != clientGroup->end(); itr++)
+ {
+ GameConnection *client = static_cast(*itr);
+ if (client->getId() == targId)
+ {
+ targClient = client;
+ break;
+ }
+ }
+ if (! targClient)
+ return;
+
+ //now find this target in the table
+ PlayerDetectionEntry *targEntry = NULL;
+ for (S32 i = 0; i < mPlayerDetectionTable.size(); i++)
+ {
+ if (mPlayerDetectionTable[i].playerId == targId)
+ targEntry = &(mPlayerDetectionTable[i]);
+ }
+ //if the entry wasn't found, create one... (and yes, this table will never shrink)
+ if (!targEntry)
+ {
+ PlayerDetectionEntry tempEntry;
+ tempEntry.playerId = targId;
+ tempEntry.playerLOS = false;
+ tempEntry.playerLOSTime = 0;
+ tempEntry.playerLastPosition.set(0, 0, 0);
+ mPlayerDetectionTable.push_front(tempEntry);
+ targEntry = &(mPlayerDetectionTable.first());
+ }
+
+ //make sure the client has a player
+ Player *targPlayer = NULL;
+ if (! targClient->getControlObject())
+ return;
+ targPlayer = dynamic_cast(targClient->getControlObject());
+ if (! targPlayer)
+ return;
+
+ //set the time if the status changed
+ if (!targEntry->playerLOS)
+ targEntry->playerLOSTime = Sim::getCurrentTime();
+
+ //set the bool
+ targEntry->playerLOS = true;
+
+ //update the position
+ targPlayer->getWorldBox().getCenter(&targEntry->playerLastPosition);
+}
+
+bool AIConnection::hasLOSToClient(S32 clientId, S32 &losTime, Point3F &lastLocation)
+{
+ //now find this target in the table
+ PlayerDetectionEntry *targEntry = NULL;
+ for (S32 i = 0; i < mPlayerDetectionTable.size(); i++)
+ {
+ if (mPlayerDetectionTable[i].playerId == clientId)
+ targEntry = &(mPlayerDetectionTable[i]);
+ }
+
+ if (! targEntry)
+ {
+ losTime = Sim::getCurrentTime();
+ lastLocation.set(0, 0, 0);
+ return false;
+ }
+ else
+ {
+ losTime = Sim::getCurrentTime() - targEntry->playerLOSTime;
+ lastLocation = targEntry->playerLastPosition;
+ return targEntry->playerLOS;
+ }
+}
+
+// LH- put script calls into separate methods so profiler can measure them.
+// Then added slicing since it was weighing in on profiles.
+void AIConnection::scriptProcessEngagement()
+{
+ if (!gScriptEngageSlicer.ready(mPackCheckCounter, 6))
+ return;
+
+ char idStr[32], targetIdStr[32], targetTypeStr[32], projectileStr[64];
+ dSprintf(idStr, sizeof(idStr), "%d", getId());
+ if (mTargetPlayer)
+ {
+ dSprintf(targetTypeStr, sizeof(targetTypeStr), "%s", "player");
+ dSprintf(targetIdStr, sizeof(targetIdStr), "%d", mEngageTarget->getId());
+ }
+ else if (bool(mTargetObject) && mObjectMode == AIConnection::DestroyObject)
+ {
+ dSprintf(targetTypeStr, sizeof(targetTypeStr), "%s", "object");
+ dSprintf(targetIdStr, sizeof(targetIdStr), "%d", mTargetObject->getId());
+ }
+ else
+ {
+ dSprintf(targetTypeStr, sizeof(targetTypeStr), "%s", "none");
+ dSprintf(targetIdStr, sizeof(targetIdStr), "%d", -1);
+ }
+ if (bool(mEnemyProjectile) && mEvadingCounter > 0 && mEvadingCounter < 45)
+ dSprintf(projectileStr, sizeof(projectileStr), "%d", mEnemyProjectile->getId());
+ else
+ dSprintf(projectileStr, sizeof(projectileStr), "%d", -1);
+
+ Con::executef(5, "AIProcessEngagement", idStr, targetIdStr, targetTypeStr, projectileStr);
+}
+
+void AIConnection::scriptChooseEngageWeapon(F32 distToTarg)
+{
+ char idStr[32], targetIdStr[32], distTotargStr[32];
+
+ dSprintf(idStr, sizeof(idStr), "%d", getId());
+ dSprintf(targetIdStr, sizeof(targetIdStr), "%d", mEngageTarget->getId());
+ dSprintf(distTotargStr, sizeof(distTotargStr), "%f", distToTarg);
+ const char* canUseEnergyStr = mNavUsingJet ? "false" : "true";
+ const char* environmentStr = (mInWater || mTargInWater ? "water" : (mOutdoors ? "outdoors" : "indoors"));
+ Con::executef(6, "AIChooseEngageWeapon", idStr, targetIdStr, distTotargStr, canUseEnergyStr, environmentStr);
+}
+
+void AIConnection::scriptChooseObjectWeapon(F32 distToTarg)
+{
+ char idStr[32], targetIdStr[32], distTotargStr[32];
+
+ dSprintf(idStr, sizeof(idStr), "%d", getId());
+ dSprintf(targetIdStr, sizeof(targetIdStr), "%d", mTargetObject->getId());
+ dSprintf(distTotargStr, sizeof(distTotargStr), "%f", distToTarg);
+ const char* canUseEnergyStr = mNavUsingJet ? "false" : "true";
+ const char* environmentStr = (mInWater || mTargInWater ? "water" : (mOutdoors ? "outdoors" : "indoors"));
+ Con::executef(7, "AIChooseObjectWeapon", idStr, targetIdStr, distTotargStr, gTargetObjectMode[mObjectMode], canUseEnergyStr, environmentStr);
+}
+
+void AIConnection::setEvadeLocation(const Point3F &dangerLocation, S32 durationTicks)
+{
+ //find a new location orthogonal to the danger location to head to
+ Point3F dangerVector = dangerLocation - mLocation;
+ Point3F myVector = mMoveLocation - mLocation;
+ if (dangerVector.len() < 0.001f)
+ dangerVector.set(0, 1, 0);
+ dangerVector.normalize();
+ if (myVector.len() < 0.001f)
+ myVector.set(0, 1, 0);
+ myVector.normalize();
+ Point3F orthDanger;
+ mCross(dangerVector, Point3F(0, 0, 1), &orthDanger);
+ if (orthDanger.len() < 0.001f)
+ orthDanger.set(0, 1, 0);
+ orthDanger.normalize();
+
+ //if we have a NAN vector, the danger location is us!
+ if (orthDanger.x != orthDanger.x || orthDanger.y != orthDanger.y)
+ {
+ dangerVector = mTargLocation - mLocation;
+ if (dangerVector.len() < 0.001f)
+ dangerVector.set(0, 1, 0);
+ dangerVector.normalize();
+ mCross(dangerVector, Point3F(0, 0, 1), &orthDanger);
+ orthDanger.normalize();
+
+ //if we have *STILL* have a NAN vector, head north!
+ if (orthDanger.x != orthDanger.x || orthDanger.y != orthDanger.y)
+ orthDanger.set(0, 1, 0);
+ }
+
+ //choose the point closest to the direction we're already moving...
+ F32 myAngle = get2DAngle(mLocation + mVelocity2D, mLocation);
+ F32 tempAngle1 = get2DAngle(mLocation, mTargLocation + orthDanger);
+ F32 tempAngle2 = get2DAngle(mLocation, mTargLocation - orthDanger);
+ F32 angleDiff1, angleDiff2;
+ if (myAngle < tempAngle1)
+ angleDiff1 = getMin(tempAngle1 - myAngle, 256 + myAngle - tempAngle1);
+ else
+ angleDiff1 = getMin(myAngle - tempAngle1, 256 + tempAngle1 - myAngle);
+ if (myAngle < tempAngle2)
+ angleDiff2 = getMin(tempAngle2 - myAngle, 256 + myAngle - tempAngle2);
+ else
+ tempAngle2 = getMin(myAngle - tempAngle2, 256 + tempAngle2 - myAngle);
+
+ if (angleDiff1 < angleDiff2)
+ mEvadeLocation = mLocation + (orthDanger * 30.0f);
+ else
+ mEvadeLocation = mLocation - (orthDanger * 30.0f);
+
+ if (durationTicks > 0)
+ mEvadingCounter = durationTicks;
+}
+
+void AIConnection::processEngagement(Player *player)
+{
+ //updatate the detection table
+ PROFILE_START(AI_updateDetectionTable);
+ updateDetectionTable(player);
+ PROFILE_END();
+
+ //call the script function once each frame to handle packs, grenades, health kits, etc...
+ scriptProcessEngagement();
+
+ //see if we need to reset the script trigger counter (if we changed weapons...)
+ ShapeBaseImageData* tempImage = player->getMountedImage(0);
+ if (tempImage != mMountedImage)
+ mScriptTriggerCounter = -1;
+ mMountedImage = tempImage;
+
+ //if the script wants us to fire (using the repair pack to self repair mainly)...
+ if (mScriptTriggerCounter-- >= 0)
+ pressFire(true);
+
+ //make sure we have someone to shoot at
+ if (! mTargetPlayer && ! bool(mTargetObject))
+ return;
+
+ //if we have something or someone to shoot at, let the engage code determine whether to fire
+ pressFire(false);
+
+ //reset the counter
+ mLookAtTargetTimeMS = Sim::getCurrentTime() + 1500;
+
+ //see if we're shooting a player, or a static object
+ bool engagingPlayer;
+ Point3F targetLocation;
+ F32 distToTarg;
+ if (mTargetPlayer)
+ {
+ engagingPlayer = true;
+
+ //if the player has been blinded, use the last known location only
+ if (Sim::getCurrentTime() < mBlindedTimer)
+ {
+ S32 dummyTime;
+ hasLOSToClient(mEngageTarget->getId(), dummyTime, targetLocation);
+ }
+ else
+ {
+ //see if we have LOS to the client
+ S32 losTime;
+ Point3F losLocation;
+ bool hasLOS = hasLOSToClient(mEngageTarget->getId(), losTime, losLocation);
+
+ //if we have just lost LOS to the target, assume he went around a corner, and fire at the last known location
+ if (!hasLOS && losTime < 2500)
+ targetLocation = losLocation;
+
+ //this simulates slower response - the bot doesn't quite keep up to the target's current location
+ else if (mSkillLevel >= 0.9f)
+ targetLocation = mTargPrevLocation[0];
+ else if (mSkillLevel >= 0.7f)
+ targetLocation = mTargPrevLocation[1];
+ else if (mSkillLevel >= 0.3f)
+ targetLocation = mTargPrevLocation[2];
+ else
+ targetLocation = mTargPrevLocation[3];
+ }
+
+ //now calculate the 2D distance
+ distToTarg = (Point3F(targetLocation.x, targetLocation.y, 0) - Point3F(mLocation.x, mLocation.y, 0)).len();
+ }
+ else
+ {
+ engagingPlayer = false;
+
+ //if a repair node has been added to the object, use that, otherwise, use the worldBoxCenter
+ targetLocation = mTargetObject->getAIRepairPoint();
+ if (targetLocation == Point3F(0, 0, 0))
+ mTargetObject->getWorldBox().getCenter(&targetLocation);
+ distToTarg = mDistToObject2D;
+ }
+
+ //detect the projectiles from the target
+ mProjectileCounter--;
+ if (engagingPlayer)
+ {
+ const char *incoming = mEngageTarget->getDataField(StringTable->insert("projectile"), NULL);
+ if (incoming && incoming[0])
+ {
+ Projectile *projectile;
+ if (Sim::findObject(incoming, projectile))
+ {
+ //see if it's a new threat, or time to re-evaluate the current one
+ if (projectile != (Projectile*)mEnemyProjectile || mProjectileCounter <= 0)
+ {
+ //reset the projectile counter
+ mProjectileCounter = 15;
+
+ F32 timeToImpact;
+ ProjectileData* projData = projectile->getDataBlock() != NULL ?
+ dynamic_cast(projectile->getDataBlock()) :
+ NULL;
+ if (projData && projectile->calculateImpact(4.0f, mImpactLocation, timeToImpact)) {
+ //see if the impact location is within range
+ Point3F predictMyLocation = mLocation + mVelocity * timeToImpact;
+ F32 distToDanger = (predictMyLocation - mImpactLocation).len();
+ if (distToDanger < getMax(projData->damageRadius, 2.0f))
+ {
+ setEvadeLocation(mImpactLocation);
+ mEnemyProjectile = projectile;
+
+ //set the evade counter - any value above 45 is simulated response time
+ S32 responseTime = S32(30 * (1.0f - mSkillLevel));
+ mEvadingCounter = 45 + responseTime;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ //if we're in the middle of a NavGraph Jet, we can't shoot...
+ Point3F dummyPoint;
+ if (mNavUsingJet && mJetting.shouldAimAt(dummyPoint))
+ return;
+
+ //here we only fire at someone if we've seen them recently enough
+ if (bool(mEngageTarget))
+ {
+ S32 detectLOSTime;
+ Point3F lastLOSLocation;
+ bool hasLOSDetect = hasLOSToClient(mEngageTarget->getId(), detectLOSTime, lastLOSLocation);
+ if (!hasLOSDetect && (detectLOSTime > mDetectHiddenPeriod))
+ return;
+ }
+
+ //process the engagement state machine
+ switch (mEngageState)
+ {
+ case ChooseWeapon:
+ {
+ //set the aim location
+ if (! mTargetInRange)
+ setAimLocation(targetLocation);
+ else
+ setAimLocation(Point3F(targetLocation.x, targetLocation.y, mAimLocation.z));
+
+ //set the bool
+ mFiring = false;
+
+ //limit changing the weapon too often for low skill
+ if (--mChangeWeaponCounter > 0)
+ {
+ mEngageState = ReloadWeapon;
+ mStateCounter = 45;
+
+ //set the delay counter
+ mDelayCounter = S32(20.0f * (1.0f - mSkillLevel));
+ }
+
+ //else reset the counter
+ else
+ mChangeWeaponCounter = S32((1.0f - mSkillLevel) / 0.10f);
+
+ //keep track of the previous weapon
+ StringTableEntry prevWeapon = mProjectileName;
+
+ //call the script function to set the weapon vars
+ if (engagingPlayer)
+ scriptChooseEngageWeapon(distToTarg);
+ else
+ scriptChooseObjectWeapon(distToTarg);
+
+ if (distToTarg > mEngageMaxDistance)
+ {
+ mEngageState = OutOfRange;
+ mStateCounter = 30;
+ }
+ else
+ {
+ mEngageState = ReloadWeapon;
+ mStateCounter = 60;
+
+ //set the delay counter
+ mDelayCounter = S32(20.0f * (1.0f - mSkillLevel));
+
+ //see if we've switched weapons
+ if (dStricmp(mProjectileName, prevWeapon))
+ mDelayCounter *= 3;
+ }
+ break;
+ }
+
+ case OutOfRange:
+ {
+ //set the aim location
+ if (! mTargetInRange)
+ setAimLocation(targetLocation);
+ else
+ setAimLocation(Point3F(targetLocation.x, targetLocation.y, mAimLocation.z));
+
+ if (distToTarg <= mEngageMaxDistance || (--mStateCounter <= 0))
+ mEngageState = ChooseWeapon;
+ break;
+ }
+
+ case ReloadWeapon:
+ {
+ //set the aim location
+ if (! mTargetInRange)
+ setAimLocation(targetLocation);
+ else
+ setAimLocation(Point3F(targetLocation.x, targetLocation.y, mAimLocation.z));
+
+ //make sure we're not stuck in this state
+ if (--mStateCounter <= 0)
+ {
+ mEngageState = ChooseWeapon;
+ break;
+ }
+
+ //make sure we're not trying to use an energy weapon while jetting
+ if (mWeaponEnergy > 0 && mNavUsingJet)
+ {
+ mEngageState = ChooseWeapon;
+ break;
+ }
+
+ //if the weapon is ready, time to aim
+ bool weaponReady = player->isImageReady(0);
+ bool hasEnergy = (mEnergy >= mWeaponEnergy);
+
+ //add in factor so they're not unloading on you at lower skill settings...
+ F32 skillVelocityFactor = 6.0f + (mSkillLevel * mSkillLevel * mSkillLevel * 300.0f);
+ bool mustSlowDown = (mVelocity.len() > skillVelocityFactor);
+
+ if (weaponReady && hasEnergy && !mustSlowDown)
+ {
+ mStateCounter = 15;
+ if (--mDelayCounter <= 0)
+ mEngageState = FindTargetPoint;
+ }
+
+ //if firing is sustained, keep firing
+ if (mFiring && mTriggerCounter > 0)
+ {
+ mTriggerCounter--;
+ pressFire();
+ }
+
+ break;
+ }
+
+ case FindTargetPoint:
+ {
+ //set the aim location
+ if (! mTargetInRange)
+ setAimLocation(targetLocation);
+ else
+ setAimLocation(Point3F(targetLocation.x, targetLocation.y, mAimLocation.z));
+
+ //make sure we're not stuck in this state
+ if (--mStateCounter <= 0)
+ {
+ mEngageState = ChooseWeapon;
+ break;
+ }
+
+ //make sure we're not trying to use an energy weapon while jetting
+ if (mWeaponEnergy > 0 && mNavUsingJet)
+ {
+ mEngageState = ChooseWeapon;
+ break;
+ }
+
+ //should we go for center mass, or splash damage, or are we shooting at a lazed target
+ Point3F aimAtTargetPoint;
+ mAimAtLazedTarget = false;
+
+ //only shoot at lazed targets if we're using a ballistic weapon
+ if (mProjectile && mProjectile->isBallistic)
+ {
+ //see if we have a lazed target point...
+ SimSet *targetSet = Sim::getServerTargetSet();
+ if (targetSet)
+ {
+ SimSet::iterator i;
+ for (i = targetSet->begin(); i != targetSet->end(); i++)
+ {
+ GameBase *target = static_cast(*i);
+ Point3F targetPoint;
+ U32 dummyTeam; //when the sensor network is finished, then the target's
+ //visibility can be properly determined.
+ target->getTarget(&targetPoint, &dummyTeam);
+ //see if the target is within 20m of what we're trying to shoot at...
+ if ((targetPoint - targetLocation).len() < 10.0f)
+ {
+ aimAtTargetPoint = targetPoint;
+ mAimAtLazedTarget = true;
+ }
+ }
+ }
+ }
+
+ if (! mAimAtLazedTarget)
+ {
+ //see if we should aim for the target's feet
+ if (engagingPlayer && mProjectile && mProjectile->hasDamageRadius && mProjectile->damageRadius > 5.0f)
+ aimAtTargetPoint = mTargLocation;
+
+ //else aim for the target's center mass
+ else
+ aimAtTargetPoint = targetLocation;
+ }
+
+ //find the aim vector
+ Point3F aimVectorMin, aimVectorMax;
+ F32 timeMin, timeMax;
+ if (mProjectile)
+ {
+ bool canShoot;
+ if (engagingPlayer)
+ canShoot = mProjectile->calculateAim(aimAtTargetPoint, mTargVelocity, mMuzzlePosition, mVelocity, &aimVectorMin, &timeMin, &aimVectorMax, &timeMax);
+ else
+ {
+ canShoot = false;
+ if (mObjectMode != MissileVehicle || player->getLockedTargetId() == mTargetObject->getId())
+ canShoot = mProjectile->calculateAim(aimAtTargetPoint, Point3F(0, 0, 0), mMuzzlePosition, mVelocity, &aimVectorMin, &timeMin, &aimVectorMax, &timeMax);
+ }
+ if (canShoot)
+ {
+ //find the aimlocation from the normalized aim vector
+ Point3F aimLocation = mMuzzlePosition + aimVectorMin * (aimAtTargetPoint - mMuzzlePosition).len();
+
+ //now add the tone down the accuracy for moving targets
+ bool needToDope = mVelocity.len() > 4.0f || engagingPlayer || (mProjectile->isBallistic && ! mAimAtLazedTarget);
+ if (needToDope)
+ aimLocation = dopeAimLocation(mMuzzlePosition, aimLocation);
+
+ //set the aim location
+ setAimLocation(aimLocation);
+
+ mEngageState = AimAtTarget;
+ mStateCounter = 15;
+
+ //call processEngagement again immediately
+ PROFILE_START(AI_EngagementInner);
+ processEngagement(player);
+ PROFILE_END();
+ }
+ else
+ {
+ //reset the bool
+ mTargetInRange = false;
+
+ //if firing is sustained, keep firing
+ if (mFiring && mTriggerCounter > 0)
+ {
+ mTriggerCounter--;
+ pressFire();
+ }
+ }
+ }
+
+ //if no projectile, we need to find one...
+ else
+ {
+ mEngageState = ChooseWeapon;
+ mTargetInRange = false;
+ }
+ break;
+ }
+
+ case AimAtTarget:
+ {
+ //make sure we're not trying to use an energy weapon while jetting
+ if (mWeaponEnergy > 0 && mNavUsingJet)
+ {
+ mEngageState = ChooseWeapon;
+ break;
+ }
+
+ //the process order is: fire, then move + aim, we need to aim as if we're
+ //shooting next frame
+ if (engagingPlayer)
+ {
+ Point3F nextFrameAimPoint = mAimLocation - mVelocity / 30.0f;
+ setAimLocation(nextFrameAimPoint);
+ }
+
+ //see if we have line of site
+ bool clearLOSToTarget = false;
+ RayInfo rayInfo;
+ Point3F startPt = mMuzzlePosition + mVelocity / 30.0f; //as if it's next frame already
+ Point3F endPt = mAimLocation;
+ U32 mask = TerrainObjectType | InteriorObjectType | PlayerObjectType | ForceFieldObjectType;
+
+ //if the player is mounted on a vehicle, mask in vehicle types as well...
+ if (player->isMounted())
+ mask |= VehicleObjectType;
+
+ player->disableCollision();
+ if (! gServerContainer.castRay(startPt, endPt, mask, &rayInfo))
+ clearLOSToTarget = true;
+ else if (engagingPlayer && bool(rayInfo.object) && bool(mTargetPlayer))
+ {
+ if (rayInfo.object->getId() == mTargetPlayer->getId())
+ clearLOSToTarget = true;
+ }
+ else if (!engagingPlayer && bool(rayInfo.object) && bool(mTargetObject))
+ {
+ if (rayInfo.object->getId() == mTargetObject->getId())
+ clearLOSToTarget = true;
+ }
+ player->enableCollision();
+
+ bool readyToFire = false;
+ //if we have a clear LOS, or if we're firing a ballistic weapon and have enough skill to shoot from behind hills
+ if (clearLOSToTarget || (mProjectile->isBallistic && mSkillLevel >= 0.7f))
+ readyToFire = true;
+
+ //else if we're firing a linear weapon with splash damage
+ else if (! clearLOSToTarget && ! mProjectile->isBallistic)
+ {
+ //see if the impact point is within the damage radius of the projectile
+ if (mProjectile->hasDamageRadius && (rayInfo.point - mAimLocation).len() < getMax(10.0f, mProjectile->damageRadius))
+ {
+ //need to see if the impact point has LOS to the target, otherwise, bot will try to shoot through walls...
+ //readyToFire = true;
+ readyToFire = false;
+ }
+ }
+ if (readyToFire)
+ {
+ mTargetInRange = true;
+ mEngageState = FireWeapon;
+ }
+ else
+ {
+ mTargetInRange = false;
+ mEngageState = FindTargetPoint;
+ }
+
+ //if firing is sustained, keep firing
+ if (mFiring && mTriggerCounter > 0)
+ {
+ mTriggerCounter--;
+ pressFire();
+ }
+
+ break;
+ }
+
+ case FireWeapon:
+ {
+ //make sure we're not trying to use an energy weapon while jetting
+ if (mWeaponEnergy > 0 && mNavUsingJet)
+ {
+ mEngageState = ChooseWeapon;
+ break;
+ }
+
+ //if we're firing, we must be in range
+ mTargetInRange = true;
+
+ //press fire and move to reload
+ pressFire();
+ mFiring = true;
+ mTriggerCounter--;
+
+ //see if firing is to be sustained (chaingun, elf gun, etc...)
+ if (mTriggerCounter > 0)
+ {
+ mEngageState = FindTargetPoint;
+ mStateCounter = 15;
+ }
+ else
+ {
+ mEngageState = ChooseWeapon;
+ mStateCounter = 60;
+ }
+
+ break;
+ }
+ }
+}
+
+F32 AIConnection::angleDifference(F32 angle1, F32 angle2)
+{
+ F32 tempAngle1 = getMin(angle1, angle2);
+ F32 tempAngle2 = getMax(angle1, angle2);
+ F32 difference = getMin(F32(tempAngle2 - tempAngle1),
+ F32(tempAngle1 + M_2PI - tempAngle2));
+ return difference;
+}
+
+Point3F AIConnection::correctHeading()
+{
+ Point3F newLocation = mNodeLocation;
+
+ //make sure we're heading in approximately the right direction (+- 30 deg or so)
+ //no need to overcorrect if we're not moving very fast
+ if (mVelocity2D.len() > 4)
+ {
+ //if we're heading more than 90 deg in the wrong direction, or we're heading *way* too fast
+ //to hit the node, choose the exact opposite direction of the velocity
+ if (mDotOffCourse <= 0)
+ {
+ //find out our current heading (sub 90 deg since our axis is rotated)
+ F32 heading = mAtan(mVelocity2D.x, mVelocity2D.y) - (M_PI / 2.0f);
+ F32 newHeading = heading + M_PI;
+
+ newLocation.x = mLocation.x + mDistToNode2D * mCos(newHeading);
+ newLocation.y = mLocation.y - mDistToNode2D * mSin(newHeading);
+ }
+
+ //else mirror the angle through the desired heading
+ else
+ {
+ //find out our current heading (sub 90 deg since our axis is rotated)
+ F32 heading = mAtan(mVelocity2D.x, mVelocity2D.y) - (M_PI / 2.0f);
+
+ //find out what angle we're currently off by
+ F32 headingDiff = mAcos(mDotOffCourse);
+
+ //determine the "overcompensate" factor
+ F32 overCompFactor;
+ if (mDotOffCourse > 0.85)
+ overCompFactor = 3.0f;
+ else
+ overCompFactor = 2.0f;
+
+ //now see find out which direction
+ F32 angle1 = heading + headingDiff;
+ F32 angle2 = heading - headingDiff;
+ Point3F newVec1(mCos(angle1), -mSin(angle1), 0);
+ Point3F newVec2(mCos(angle2), -mSin(angle2), 0);
+
+ //the dot with one of these angles should have a dot near 1 -
+ //create the "correcting" angle to overcompensate the headingDiff
+ F32 newAngle;
+ F32 angle1Dot = get2DDot(mNodeLocation - mLocation, newVec1);
+ F32 angle2Dot = get2DDot(mNodeLocation - mLocation, newVec2);
+ if (angle1Dot > angle2Dot)
+ newAngle = heading + overCompFactor * headingDiff;
+ else
+ newAngle = heading - overCompFactor * headingDiff;
+
+ //find the new point
+ newLocation.x = mLocation.x + mDistToNode2D * mCos(newAngle);
+ newLocation.y = mLocation.y - mDistToNode2D * mSin(newAngle);
+ }
+ }
+
+ return newLocation;
+}
+
+Point3F AIConnection::avoidPlayers(Player *player, const Point3F &desiredDestination, bool destIsFinal)
+{
+ F32 avoidObjectAngle = -1;
+ //see if we're near another player
+ Box3F queryBox;
+ queryBox.min = mLocation;
+ queryBox.max = mLocation;
+ queryBox.min -= Point3F(2.0f, 2.0f, 0.5f);
+ queryBox.max += Point3F(2.0f, 2.0f, 2.5f);
+
+ ShapeBase *closestObject = NULL;
+ F32 closestDist = 32767;
+ Point3F closestLocation;
+ SimpleQueryList result;
+ //U32 mask = PlayerObjectType;
+ U32 mask = ShapeBaseObjectType | StaticTSObjectType;
+ gServerContainer.findObjects(queryBox, mask, SimpleQueryList::insertionCallback, S32(&result));
+ if (result.mList.size() > 1)
+ {
+ //find out if the closest person is to the left or right...
+ for (S32 i = 0; i < result.mList.size(); i++)
+ {
+ ShapeBase *neighborObject = static_cast(result.mList[i]);
+
+ //can't bump into yourself
+ if (neighborObject->getId() == player->getId())
+ continue;
+
+ //if it's not a static TS object, see if it's a shape base with the aiAvoidThis flag set...
+ if (!(neighborObject->getType() & StaticTSObjectType))
+ {
+ ShapeBaseData *db = static_cast(neighborObject->getDataBlock());
+ if (!db || ! db->aiAvoidThis)
+ continue;
+ }
+
+ //make sure it's not a corpse!
+ if (neighborObject->getType() & CorpseObjectType)
+ continue;
+
+ //now we see if it's actually in our way, or if it's just the bounding box...
+ Point3F tempVector = neighborObject->getBoxCenter() - player->getBoxCenter();
+ if (tempVector.len() > 1.0f)
+ {
+ tempVector.normalize();
+ player->disableCollision();
+ RayInfo rayInfo;
+ Point3F startPt = player->getBoxCenter();
+ Point3F endPt = startPt + (tempVector * 2.0f);
+ bool losResult = gServerContainer.castRay(startPt, endPt, neighborObject->getType(), &rayInfo);
+ player->enableCollision();
+
+ //we disregard this object if we didn't intersect with it
+ if (!losResult || (rayInfo.object->getId() != neighborObject->getId()))
+ continue;
+ }
+
+ MatrixF const& tempTransform = neighborObject->getTransform();
+ Point3F tempLocation;
+ tempTransform.getColumn(3, &tempLocation);
+ F32 tempDist = (tempLocation - mLocation).len();
+ if (tempDist < closestDist)
+ {
+ closestDist = tempDist;
+ closestObject = neighborObject;
+ closestLocation = tempLocation;
+ }
+ }
+ }
+
+ //if we didn't find anyone, return the desired dest
+ if (! closestObject)
+ {
+ mAvoidingObject = NULL;
+ return desiredDestination;
+ }
+
+ //otherwise, we've got a neighbor...
+ else
+ {
+ //update the detection table for this bot
+ Player *closestPlayer = dynamic_cast(closestObject);
+ if (bool(closestPlayer))
+ {
+ GameConnection *closestClient = closestPlayer->getControllingClient();
+ if (closestClient)
+ clientDetected(closestClient->getId());
+ }
+
+ //find out how close we are to our destination
+ Point3F newLocation;
+ F32 newHeading;
+ F32 distToDest2D = (Point3F(desiredDestination.x, desiredDestination.y, 0.0f) -
+ Point3F(mLocation.x, mLocation.y, 0.0f)).len();
+
+ //create and normalize the vectors
+ Point3F directionVector = desiredDestination - mLocation;
+ Point3F neighborVector = closestLocation - mLocation;
+ directionVector.z = 0;
+
+ //normalize the vector
+ if (directionVector.len() < 0.001f)
+ directionVector.set(0, 1, 0);
+ directionVector.normalize();
+
+ //make sure we didn't get any -NAN values...
+ if (directionVector.x != directionVector.x || directionVector.y != directionVector.y)
+ directionVector.set(0, 1, 0);
+
+ neighborVector.z = 0;
+
+ //normalize the vector
+ if (neighborVector.len() < 0.001f)
+ neighborVector.set(0, 1, 0);
+ neighborVector.normalize();
+
+ //make sure we didn't get any -NAN values...
+ if (neighborVector.x != neighborVector.x || neighborVector.y != neighborVector.y)
+ neighborVector.set(0, 1, 0);
+
+ //dot the direction vector with the neighbor vector - if directly in our path, move at 90 deg...
+ F32 neighborDot = get2DDot(neighborVector, directionVector);
+
+ //if the neighbor is behind us, not at all in our way, and we're still moving
+ if (! destIsFinal && neighborDot < 0.5)
+ {
+ mAvoidingObject = NULL;
+ return desiredDestination;
+ }
+
+ //we've stopped - move at 45 away from target
+ else if (destIsFinal)
+ {
+ F32 neighborHeading = mAtan(neighborVector.x, neighborVector.y) - (M_PI / 2.0f);
+ F32 angle1 = neighborHeading + ((M_PI / 2.0f) + (M_PI / 4.0f));
+ F32 angle2 = neighborHeading - ((M_PI / 2.0f) + (M_PI / 4.0f));
+ Point3F newVec1(mCos(angle1), -mSin(angle1), 0);
+ Point3F newVec2(mCos(angle2), -mSin(angle2), 0);
+
+ //find out which newVec will take us furthest away from the neighbor (smallest dot product)
+ F32 angle1Dot = get2DDot(neighborVector, newVec1);
+ F32 angle2Dot = get2DDot(neighborVector, newVec2);
+ if (angle1Dot < angle2Dot)
+ newHeading = angle1;
+ else
+ newHeading = angle2;
+
+ //now that we've chosen the new heading, calculate the new destination location
+ newLocation.x = mLocation.x + getMax(6.0f, distToDest2D) * mCos(newHeading);
+ newLocation.y = mLocation.y - getMax(6.0f, distToDest2D) * mSin(newHeading);
+ newLocation.z = desiredDestination.z;
+
+ //note that since we're already at our final destination, all we need to do is
+ //get bumped out of the way, so no need to save the avoidance vars...
+ mAvoidingObject = NULL;
+ }
+
+ else if ((GameBase*)mAvoidingObject != closestObject || desiredDestination != mAvoidDestinationPoint)
+ {
+
+ F32 neighborHeading = mAtan(neighborVector.x, neighborVector.y) - (M_PI / 2.0f);
+ F32 angle1 = neighborHeading + (M_PI / 2.0f);
+ F32 angle2 = neighborHeading - (M_PI / 2.0f);
+ Point3F newVec1(mCos(angle1), -mSin(angle1), 0);
+ Point3F newVec2(mCos(angle2), -mSin(angle2), 0);
+ F32 angle1Dot = get2DDot(directionVector, newVec1);
+ F32 angle2Dot = get2DDot(directionVector, newVec2);
+ if (angle1Dot > angle2Dot)
+ newHeading = angle1;
+ else
+ newHeading = angle2;
+
+ //now that we've chosen the new heading, calculate the new destination location
+ newLocation.x = mLocation.x + getMax(6.0f, distToDest2D) * mCos(newHeading);
+ newLocation.y = mLocation.y - getMax(6.0f, distToDest2D) * mSin(newHeading);
+ newLocation.z = desiredDestination.z;
+
+ //now save off the avoidance vars
+ mAvoidingObject = closestObject;
+ mAvoidSourcePoint = mLocation;
+ mAvoidDestinationPoint = desiredDestination;
+ mAvoidMovePoint = newLocation;
+ mAvoidForcedPath = false;
+ }
+
+ //otherwise we hit this object the last time we were trying to move to our dest
+ else
+ {
+ if ((mAvoidMovePoint - mLocation).len() < 1.5f)
+ {
+ if (! mAvoidForcedPath)
+ {
+ mPath.forceSearch();
+ mAvoidForcedPath = true;
+ }
+ newLocation = desiredDestination;
+ }
+ else
+ newLocation = mAvoidMovePoint;
+ }
+
+ return newLocation;
+ }
+}
+
+void AIConnection::processVehicleMovement(Player *player)
+{
+ //first, kill the nav jetting state machine...
+ if (mNavUsingJet)
+ {
+ mNavUsingJet = false;
+ mJetting.reset();
+ mPath.forceSearch();
+ }
+
+ //now set the aim location if the bot is a passenger.
+ //note - if the passenger is in a fixed mounted position, this will be ignored anyways...
+ if (player->isMounted() && bool(player->getObjectMount()))
+ {
+ Point3F vec;
+ MatrixF vehicleMat;
+ player->getObjectMount()->getMountTransform(player->getMountNode(), &vehicleMat);
+ vehicleMat.getColumn(1,&vec);
+ F32 vehicleRot = mAtan(vec.x,vec.y) - (M_PI / 2.0f);
+
+ //choose a point so the bot will face the same direction as the vehicle...
+ Point3F aimVector(mCos(vehicleRot), -mSin(vehicleRot), 0);
+ Point3F aimLocation = mLocation + 30.0f * aimVector;
+ aimLocation.z += 2.0f;
+ setScriptAimLocation(aimLocation, 1000);
+ }
+
+ //finally, the script callback (mainly to handle vehicle firing, etc...)
+ char idStr[32];
+ dSprintf(idStr, sizeof(idStr), "%d", getId());
+ Con::executef(2, "AIProcessVehicle", idStr);
+}
+
+void AIConnection::processPilotVehicle(Vehicle *myVehicle)
+{
+ //eventually, I may move some of the pilot code from ::getMoveList() to here...
+ myVehicle;
+
+ //kill the nav jetting state machine...
+ if (mNavUsingJet)
+ {
+ mNavUsingJet = false;
+ mJetting.reset();
+ mPath.forceSearch();
+ }
+
+ //finally, the script callback (mainly to handle vehicle firing, etc...)
+ char idStr[32];
+ dSprintf(idStr, sizeof(idStr), "%d", getId());
+ Con::executef(2, "AIPilotVehicle", idStr);
+}
+
+void AIConnection::processMovement(Player *player)
+{
+ player->updateWorkingCollisionSet();
+
+ // Inform path machinery about our jetting ability.
+ setPathCapabilities(player);
+
+ //if we're avoiding danger, override the current destination and move mode...
+ //note, if mEvadingCounter > 45, we use this to slow down reaction time...
+ mEvadingCounter--;
+ if (mEvadingCounter > 0 && mEvadingCounter < 45)
+ {
+ //make sure we don't get stuckin in the nav jet state machine
+ mJetting.reset();
+ mNavUsingJet = false;
+
+ mIsEvading = true;
+ setMoveLocation(Point3F(mEvadeLocation.x, mEvadeLocation.y, 0));
+ setMoveSpeed(1.0f);
+
+ //jump if we can - should encourage skiing
+ if (player->canJump())
+ pressJump();
+ else
+ pressJet();
+ }
+
+ //regular navigation...
+ else
+ {
+ // For debugging movement code of single bot (lh)-
+ S32 focusOnBot = Con::getIntVariable("$AIFocusOnBot");
+ if (focusOnBot)
+ {
+ if (focusOnBot != getId())
+ return;
+ else
+ {
+ F32 E = mPath.jetWillNeedEnergy(12.0);
+ if (E > 0.0)
+ Con::printf("%d wants %f energy", focusOnBot, E);
+ }
+ }
+
+ //reset the nav jet stuff if we've just finished evading
+ if (mIsEvading)
+ {
+ mIsEvading = false;
+ mNavUsingJet = false;
+ mPath.forceSearch();
+ }
+
+ mPath.setDestMounted(false);
+ if (!mNavUsingJet)
+ mMoveMode = mMoveModePending;
+
+ switch (mMoveMode)
+ {
+ case ModeStop:
+ {
+ //update the path anyways - mainly to correct aiming probs...
+ mPath.updateLocations(mLocation, mMoveDestination);
+ mNodeLocation = mPath.getSeekLoc(mVelocity);
+
+ mNavUsingJet = false;
+ //even stopped, we need to move out of the way of teammates
+ Point3F moveLocation = avoidPlayers(player, mLocation, true);
+ if (moveLocation != mLocation)
+ {
+ setMoveSpeed(0.6f);
+ setMoveLocation(moveLocation);
+ }
+ else
+ {
+ setMoveSpeed(0.0f);
+ }
+ break;
+ }
+
+ case ModeGainHeight:
+ {
+ mNavUsingJet = false;
+ //jump if we can - should encourage skiing
+ if (player->canJump())
+ pressJump();
+ else
+ pressJet();
+ setMoveLocation(mLocation);
+ break;
+ }
+
+ case ModeMountVehicle:
+ //set the path var
+ mPath.setDestMounted(true);
+
+ //if we're already mounted, no need to keep moving...
+ if (player->isMounted())
+ {
+ setMoveSpeed(0.0f);
+ break;
+ }
+
+ //fall through to mode express...
+ case ModeWalk:
+ case ModeExpress:
+ {
+ //see if we have a corpse to walk to first
+ mPath.updateLocations(mLocation, mMoveDestination);
+ mNodeLocation = mPath.getSeekLoc(mVelocity);
+
+ //see if we have a brand new node location...
+ if (mNodeLocation != mPrevNodeLocation)
+ {
+ mPrevNodeLocation = mNodeLocation;
+ mInitialLocation = mLocation;
+ }
+
+ //else see if we've overshot the node and have to recalc the path
+ else
+ {
+ if (!mNavUsingJet)
+ {
+ Point3F vec1 = mInitialLocation - mNodeLocation;
+ Point3F vec2 = mLocation - mNodeLocation;
+ // Moved 2d check here - problems picking up packs with 3d dist check.
+ // Also, vectors don't need to be normalized if dot compare is with 0.
+ vec2.z = vec1.z = 0;
+ if (vec1.lenSquared() > 1.0f && vec2.lenSquared() > 1.0f)
+ if (mDot(vec1, vec2) < 0.0f)
+ {
+ mPrevNodeLocation.set(0, 0, 0);
+ mPath.forceSearch();
+ }
+ }
+ }
+
+
+ if (mPath.userMustJet() && !mInWater)
+ {
+ mNavUsingJet = true;
+ if (mJetting.status() == AIJetWorking)
+ {
+ mJetting.process(this, player);
+ if (mJetting.status() != AIJetWorking)
+ {
+ if (mJetting.status() == AIJetSuccess)
+ mPath.informJetDone();
+ else
+ {
+ if (mMoveMode == ModeMountVehicle)
+ pressJump();
+ mPath.forceSearch();
+ }
+ mJetting.reset();
+ }
+ else if (mJetting.badTimeToSearch())
+ mPath.informJetBusy();
+ }
+ else
+ mJetting.init(mNodeLocation, mPath.intoMount(), mPath.getJetInfo());
+ }
+ else
+ {
+ if (mNavUsingJet)
+ {
+ //force a path search after jetting is done...
+ mPath.forceSearch();
+ mNavUsingJet = false;
+ }
+ //jet if we're falling too quickly
+ if (mVelocity.z < -20.0f)
+ pressJet();
+
+ //see if we're getting near the end of the path
+ //F32 distToEnd = getMax(mPath.distRemaining(2 * mMoveTolerance), (mLocation - mMoveDestination).len());
+ bool pathIsCurrent = mPath.isPathCurrent();
+ F32 distToEnd = (pathIsCurrent ? mPath.distRemaining(2 * mMoveTolerance) : (2 * mMoveTolerance));
+ F32 tolerance = distToEnd > mMoveTolerance ? 0.25f : mMoveTolerance;
+ F32 jetEnergy = mPath.jetWillNeedEnergy(30);
+
+ //find out where we're really heading...
+ Point3F moveLocation = mNodeLocation;
+ if (mDistToNode2D > 0.25f)
+ moveLocation = correctHeading();
+
+ //calculate the distance to where we're moving
+ F32 distToMove2D = (Point3F(mLocation.x, mLocation.y, 0) -
+ Point3F(moveLocation.x, moveLocation.y, 0)).len();
+ F32 distToEnd2D = (Point3F(mLocation.x, mLocation.y, 0) -
+ Point3F(mMoveDestination.x, mMoveDestination.y, 0)).len();
+ distToEnd = getMax(distToEnd, distToEnd2D);
+
+ //now adjust the heading if we're bumping into other players
+ if (mMoveMode != ModeMountVehicle || distToEnd > 3.0f)
+ moveLocation = avoidPlayers(player, moveLocation, false);
+
+ //now move in the calculated direction
+ setMoveLocation(moveLocation);
+
+ //see if we're stuck...
+ if (distToEnd > tolerance && distToEnd > 1.0f)
+ {
+ Point3F checkStuck2D = mLocation - mStuckLocation;
+ checkStuck2D.z = 0.0f;
+ if ((mNodeLocation - mStuckDestination).len() > 1.0f || (checkStuck2D).len() > 1.0f)
+ {
+ mStuckLocation = mLocation;
+ mStuckDestination = mNodeLocation;
+ mStuckTimer = Sim::getCurrentTime();
+ }
+ else if (Sim::getCurrentTime() - mStuckTimer > 2000)
+ {
+ setMoveMode(ModeStuck);
+ mStuckInitialized = false;
+ mStuckTryJump = true;
+ mStuckJumpInitialized = false;
+ return;
+ }
+ else
+ {
+ // Know we want to be moving- inform Path of how well we're doing-
+ mPath.informProgress(mVelocity);
+ }
+ }
+
+ //look ahead in the path, and see if the next point is collinear with our current destination
+ Point3F nextNodeLocation;
+ bool isCollinearPath = false;
+ if (mPath.getPathNodeLoc(1, nextNodeLocation))
+ {
+ F32 tempDot = get2DDot(moveLocation - mLocation, nextNodeLocation - moveLocation);
+ if (tempDot > 0.9f)
+ isCollinearPath = true;
+ }
+
+ //add jumping and jetting to speed the player up...
+ if (distToMove2D > 30.0f)
+ {
+ //full speed with skiing
+ setMoveSpeed(1.0f);
+
+ //jump if we can - should encourage skiing
+ if (mHeadingDownhill)
+ {
+ if (player->canJump() && mSkillLevel >= 0.25f)
+ pressJump();
+ }
+ else
+ {
+ if (mEnergyAvailable && mEnergy > jetEnergy && mMoveMode != ModeWalk)
+ {
+ if (player->canJump())
+ pressJump();
+ else
+ pressJet();
+ }
+ }
+ }
+ else if (distToEnd > 10.0f && isCollinearPath)
+ {
+ //full speed - no skiing
+ setMoveSpeed(1.0f);
+
+ if (mEnergyAvailable && mEnergy > jetEnergy && mMoveMode != ModeWalk)
+ pressJet();
+ }
+
+ else if (distToMove2D > getMax(4.0f, tolerance))
+ {
+ //full speed - no skiing
+ setMoveSpeed(1.0f);
+ }
+ else if (distToMove2D > tolerance)
+ {
+ //slower speed with no skiing
+ setMoveSpeed(0.4f);
+ }
+ else if (distToEnd < tolerance)
+ {
+ //stopped - we've arrived
+ setMoveMode(ModeStop);
+ }
+ }
+ }
+ break;
+
+ case ModeStuck:
+ {
+ if (mStuckTryJump)
+ {
+ if (! mStuckJumpInitialized)
+ {
+ mStuckJumpInitialized = true;
+ mStuckJumpTimer = Sim::getCurrentTime();
+ setMoveSpeed(0.0f);
+ setMoveLocation(mLocation);
+ }
+ else if (Sim::getCurrentTime() - mStuckJumpTimer < 500)
+ {
+ if (mEnergy < 0.25f)
+ mStuckJumpTimer = Sim::getCurrentTime();
+ }
+ else if (Sim::getCurrentTime() - mStuckJumpTimer < 1000)
+ {
+ pressJump();
+ pressJet();
+ }
+ else if (Sim::getCurrentTime() - mStuckJumpTimer < 2000)
+ {
+ setMoveLocation(mStuckDestination);
+ setMoveSpeed(1.0f);
+ pressJet();
+ }
+ else
+ {
+ //set the bool so it'll try to back up if we're still stuck...
+ mStuckTryJump = false;
+
+ //see if that unstuck us...
+ if ((mLocation - mStuckLocation).len() > 1.0f)
+ {
+ mPath.forceSearch();
+ setMoveSpeed(1.0f);
+ setMoveMode(ModeExpress, true);
+ mStuckLocation.set(0, 0, 0);
+ return;
+ }
+ }
+ }
+
+ //else try backing up, and researching the path
+ else
+ {
+ //I guess we're still stuck... try backing up
+ if (! mStuckInitialized)
+ {
+ mStuckInitialized = true;
+
+ //find a new direction to move
+ Point3F newLocation;
+ F32 foundLength = 32767;
+ Point3F curHeading = mNodeLocation - mLocation;
+ F32 curAngle = mAtan(curHeading.x, curHeading.y) - (M_PI / 2.0f);
+
+ for (F32 i = 0.0f; i < 5.0f; i += 1.0f)
+ {
+ //run an LOS to see if the way is clear...
+ F32 testAngle = curAngle + ((i + 2.0) * M_PI / 4.0f);
+ Point3F newVec(mCos(testAngle), -mSin(testAngle), 0.0f);
+ U32 mask = TerrainObjectType | InteriorObjectType;
+ RayInfo rayInfo;
+ Point3F startPt = mLocation;
+ Point3F endPt = mLocation + (4.0f * newVec);
+ startPt.z += 0.3f;
+ endPt.z += 0.3f;
+ if (! gServerContainer.castRay(startPt, endPt, mask, &rayInfo))
+ {
+ newLocation = endPt;
+ break;
+ }
+ else if ((rayInfo.point - startPt).len() < foundLength)
+ {
+ foundLength = (rayInfo.point - startPt).len();
+ newLocation = endPt;
+ }
+ }
+
+ //set the move location, and set the time stamp
+ mStuckTimer = Sim::getCurrentTime();
+ setMoveLocation(newLocation);
+ setMoveSpeed(1.0f);
+ }
+
+ //see if our time has run out, or if we're close to our dest
+ if ((mLocation - mMoveLocation).len() < 1.0f || Sim::getCurrentTime() - mStuckTimer > 2000)
+ {
+ mStuckLocation.set(0, 0, 0);
+ mPath.informStuck(mStuckLocation, mStuckDestination);
+ setMoveMode(ModeExpress, true);
+ }
+ }
+ }
+ break;
+
+ }
+ }
+
+
+ //if we're in the middle of a nav jet, aim at the landing place...
+ Point3F jetAimLocation;
+ if (mNavUsingJet && mJetting.shouldAimAt(jetAimLocation))
+ {
+ setAimLocation(jetAimLocation);
+ }
+
+ //else set the aiming if we don't have a target to shoot at
+ else if (mMoveMode != ModeStop && mPath.isPathCurrent() && !bool(mTargetPlayer) && !bool(mTargetObject) && (Sim::getCurrentTime() > mLookAtTargetTimeMS))
+ {
+ //set the player to always look 10m ahead on the path
+ Point3F directionVector;
+ Point3F aimPoint;
+ F32 distToEnd = mPath.distRemaining(10);
+ if (distToEnd >= 10)
+ aimPoint = mPath.getLocOnPath(10);
+ else
+ aimPoint = mNodeLocation;
+
+ //now extend that point by 30m so we don't look down
+ directionVector = aimPoint - mLocation;
+ directionVector.z = 0;
+ F32 directionLen = directionVector.len();
+ // LH pulled apart the normalize() to avoid (interupt causing) divides by zero
+ if (directionLen < 0.001)
+ {
+ aimPoint.x = mLocation.x + 30.0f * mCos(mRotation.z);
+ aimPoint.y = mLocation.y - 30.0f * mSin(mRotation.z);
+ aimPoint.z = mMuzzlePosition.z;
+ }
+ else
+ { // LH- here's the normalize:
+ aimPoint.x = mLocation.x + 30.0f * (directionVector.x / directionLen);
+ aimPoint.y = mLocation.y + 30.0f * (directionVector.y / directionLen);
+ aimPoint.z = mMuzzlePosition.z;
+ }
+ setAimLocation(aimPoint);
+ }
+}
+
+
+void AIConnection::scriptSustainFire(S32 count)
+{
+ mScriptTriggerCounter = count;
+}
+
+static Point3F extractRotation(const MatrixF & matrix)
+{
+ const F32 * mat = (const F32*)matrix;
+
+ Point3F r;
+ r.x = mAsin(mat[MatrixF::idx(2,1)]);
+
+ if(mCos(r.x) != 0.f)
+ {
+ r.y = mAtan(-mat[MatrixF::idx(2,0)], mat[MatrixF::idx(2,2)]);
+ r.z = mAtan(-mat[MatrixF::idx(0,1)], mat[MatrixF::idx(1,1)]);
+ }
+ else
+ {
+ r.y = 0.f;
+ r.z = mAtan(mat[MatrixF::idx(1,0)], mat[MatrixF::idx(0,0)]);
+ }
+
+ return(r);
+}
+
+void AIConnection::getMoveList(Move** movePtr,U32* numMoves)
+{
+ //initialize the move structure and return pointers
+ mMove = NullMove;
+ *movePtr = &mMove;
+ *numMoves = 1;
+
+ //if the system is not enabled, return having set the movePtr to a NullMove
+ if (!gAISystemEnabled)
+ return;
+
+ //movement is done in the object's rotation - create a matrix
+ MatrixF moveMatrix;
+ moveMatrix.set(EulerF(0, 0, 0));
+ moveMatrix.setColumn(3, Point3F(0, 0, 0));
+ moveMatrix.transpose();
+
+ //aiming variables
+ Point3F curLocation;
+ F32 curYaw, curPitch;
+ F32 newYaw, newPitch;
+ Point3F rotation;
+ F32 xDiff, yDiff, zDiff;
+
+ //make sure we have a valid control object
+ ShapeBase *ctrlObject = getControlObject();
+ if (! ctrlObject)
+ return;
+
+ //get the control object
+ Player *myPlayer = NULL;
+ myPlayer = dynamic_cast(ctrlObject);
+
+ //make sure we're either controlling a player or a vehicle
+ if (myPlayer)
+ {
+ //if we're dead, no need to call any of the script functions...
+ if (! dStricmp(myPlayer->getStateName(), "dead"))
+ return;
+
+ //process the ai tasks and steps
+ process(myPlayer);
+
+ F32 vehicleRot = 0;
+ // Find the rotation around the Z axis from the mounted vehicle
+ if (myPlayer->isMounted() && bool(myPlayer->getObjectMount()))
+ {
+ Point3F vec, pos;
+ MatrixF vehicleMat;
+ myPlayer->getObjectMount()->getMountTransform(myPlayer->getMountNode(), &vehicleMat);
+ vehicleMat.getColumn(1,&vec);
+ vehicleMat.getColumn(3,&pos);
+ vehicleRot = -mAtan(-vec.x,vec.y);
+ }
+
+ //get the current location (global coords)
+ MatrixF const& myTransform = myPlayer->getTransform();
+ myTransform.getColumn(3, &curLocation);
+
+ //initialize the aiming variables
+ rotation = myPlayer->getRotation();
+ Point3F headRotation = myPlayer->getHeadRotation();
+ curYaw = rotation.z + vehicleRot;
+ curPitch = headRotation.x;
+ xDiff = mAimLocation.x - curLocation.x;
+ yDiff = mAimLocation.y - curLocation.y;
+
+ //first do Yaw
+ if (! isZero(xDiff) || ! isZero(yDiff))
+ {
+ //use the cur yaw between -Pi and Pi
+ while (curYaw > M_2PI)
+ curYaw -= M_2PI;
+ while (curYaw < -M_2PI)
+ curYaw += M_2PI;
+
+ //find the new yaw
+ newYaw = mAtan(xDiff, yDiff);
+
+ //find the yaw diff
+ F32 yawDiff = newYaw - curYaw;
+
+ //make it between 0 and 2PI
+ if (yawDiff < 0.0f)
+ yawDiff += M_2PI;
+ else if (yawDiff >= M_2PI)
+ yawDiff -= M_2PI;
+
+ //now make sure we take the short way around the circle
+ if (yawDiff > M_PI)
+ yawDiff -= M_2PI;
+ else if (yawDiff < -M_PI)
+ yawDiff += M_2PI;
+
+ mMove.yaw = yawDiff;
+
+ //set up the movement matrix
+ moveMatrix.set(EulerF(0, 0, newYaw));
+ }
+ else
+ moveMatrix.set(EulerF(0, 0, curYaw));
+
+ //next do pitch
+// F32 horzDist = Point2F(yDiff, xDiff).len();
+// F32 horzDist = Point2F(mAimLocation.x - mMuzzlePosition.x, mAimLocation.y - mMuzzlePosition.y).len();
+ F32 horzDist = Point2F(mAimLocation.x - mEyePosition.x, mAimLocation.y - mEyePosition.y).len();
+ if (! isZero(horzDist))
+ {
+ //we shoot from the gun, not the eye...
+// F32 vertDist = mAimLocation.z - mMuzzlePosition.z;
+ F32 vertDist = mAimLocation.z - mEyePosition.z;
+
+ newPitch = mAtan(horzDist, vertDist) - (M_PI / 2.0f);
+
+ F32 pitchDiff = newPitch - curPitch;
+ mMove.pitch = pitchDiff;
+ }
+
+ //finally, move towards mMoveLocation
+ xDiff = mMoveLocation.x - curLocation.x;
+ yDiff = mMoveLocation.y - curLocation.y;
+ if (((mFabs(xDiff) > 0) || (mFabs(yDiff) > 0)) && (! isZero(mMoveSpeed)))
+ {
+ if (isZero(xDiff))
+ mMove.y = (curLocation.y > mMoveLocation.y ? -mMoveSpeed : mMoveSpeed);
+ else if (isZero(yDiff))
+ mMove.x = (curLocation.x > mMoveLocation.x ? -mMoveSpeed : mMoveSpeed);
+ else if (mFabs(xDiff) > mFabs(yDiff))
+ {
+ F32 value = mFabs(yDiff / xDiff) * mMoveSpeed;
+ mMove.y = (curLocation.y > mMoveLocation.y ? -value : value);
+ mMove.x = (curLocation.x > mMoveLocation.x ? -mMoveSpeed : mMoveSpeed);
+ }
+ else
+ {
+ F32 value = mFabs(xDiff / yDiff) * mMoveSpeed;
+ mMove.x = (curLocation.x > mMoveLocation.x ? -value : value);
+ mMove.y = (curLocation.y > mMoveLocation.y ? -mMoveSpeed : mMoveSpeed);
+ }
+
+ //now multiply the move vector by the transpose of the object rotation matrix
+ moveMatrix.transpose();
+ Point3F newMove;
+ moveMatrix.mulP(Point3F(mMove.x, mMove.y, 0), &newMove);
+
+ //and sub the result back in the move structure
+ mMove.x = newMove.x;
+ mMove.y = newMove.y;
+ }
+ }
+
+ else
+ {
+ Vehicle *myVehicle;
+ myVehicle = dynamic_cast(ctrlObject);
+ if (myVehicle)
+ {
+ //process the ai tasks and steps (well, not steps get ignored for piloting vehicles...)
+ process(myVehicle);
+
+ //get the current location (global coords)
+ MatrixF const& myTransform = myVehicle->getTransform();
+ myTransform.getColumn(3, &curLocation);
+
+ //initialize the aiming variables
+ rotation = extractRotation(myTransform);
+ xDiff = mPilotAimLocation.x - curLocation.x;
+ yDiff = mPilotAimLocation.y - curLocation.y;
+ zDiff = mPilotAimLocation.z - curLocation.z;
+ F32 dist2D = Point2F(yDiff, xDiff).len();
+
+ //first do Yaw
+ curYaw = rotation.z;
+ F32 yawDiff = 0;
+ Point3F velocity = myVehicle->getVelocity();
+ Point3F velocity2D(velocity.x, velocity.y, 0.0f);
+ F32 velocityDot = get2DDot(velocity2D, (mPilotDestination - curLocation));
+
+ //if ((mFabs(xDiff) > 5.0f || mFabs(yDiff) > 5.0f) && (mPilotSpeed == 0.0f || (velocityDot < 0.8f && velocity2D.len() > 5.0f)))
+ if (mFabs(xDiff) > 5.0f || mFabs(yDiff) > 5.0f)
+ {
+ //use the cur yaw between -Pi and Pi
+ while (curYaw > M_2PI)
+ curYaw -= M_2PI;
+ while (curYaw < -M_2PI)
+ curYaw += M_2PI;
+
+ //find the new yaw
+ newYaw = mAtan(xDiff, yDiff);
+
+ //find the yaw diff
+ yawDiff = newYaw - curYaw;
+
+ //make it between 0 and 2PI
+ if (yawDiff < 0.0f)
+ yawDiff += M_2PI;
+ else if (yawDiff >= M_2PI)
+ yawDiff -= M_2PI;
+
+ //now make sure we take the short way around the circle
+ if (yawDiff > M_PI)
+ yawDiff -= M_2PI;
+ else if (yawDiff < -M_PI)
+ yawDiff += M_2PI;
+
+ //set up the movement matrix
+ mMove.yaw = yawDiff;
+ moveMatrix.set(EulerF(0, 0, newYaw));
+ }
+ else
+ moveMatrix.set(EulerF(0, 0, curYaw));
+
+ //both pitch and destination should use the pilotDestination, not the aim location
+ xDiff = mPilotDestination.x - curLocation.x;
+ yDiff = mPilotDestination.y - curLocation.y;
+ zDiff = mPilotDestination.z - curLocation.z;
+ dist2D = Point2F(yDiff, xDiff).len();
+
+ //get the yAxis - the vertical slope of which is our current pitch
+ Point3F yAxis;
+ myTransform.mulV(Point3F(0, 1, 0), &yAxis);
+ yAxis.normalize();
+ curPitch = mAtan(1.0f, yAxis.z) - (M_PI / 2.0f);
+
+ //if we're below our destination, and don't have enough vertical velocity, pitch up...
+ newPitch = mAtan(dist2D, zDiff) - (M_PI / 2.0f);
+ newPitch = getMax(mPitchUpMax, getMin(mPitchDownMax, newPitch));
+
+ //use a pitch delta to prevent from pitching too fast...
+ S32 pitchDir = 0;
+ F32 pitchDelta = curPitch - mPreviousPitch;
+ mPreviousPitch = curPitch;
+
+ //make sure we've got some horizontal speed
+ if (mPilotSpeed > 0.0f && velocity2D.len() > 5.0f)
+ {
+
+ //at our current rate of ascent/descent,
+ F32 timeToDest = dist2D / velocity2D.len();
+ F32 vertDist = velocity.z * timeToDest;
+
+ //if we're below our dest and our vertical lift is too low, pitch up
+ if (zDiff > 5.0f && (vertDist < zDiff - 2.0f || curPitch > 0.0f))
+ pitchDir = 1;
+
+ //else if we're higher than our dest, and our vertical drop is too low, pitch down
+ else if (zDiff < -5.0f && (vertDist > zDiff + 2.0f || curPitch < 0.0f))
+ pitchDir = -1;
+
+ //make sure we're within our pitch limits, and that we're not pitching too fast
+ if (pitchDir > 0 && (curPitch < mPitchUpMax || pitchDelta < -mPitchIncMax))
+ pitchDir = -1;
+ else if (pitchDir < 0 && (curPitch > mPitchDownMax || pitchDelta > mPitchIncMax))
+ pitchDir = 1;
+
+ //if we didn't meet the above conditions, our velocity is on track, and we should level out
+ if (pitchDir == 0)
+ {
+ //if we're pitched up, and our change in pitch is also in the up direction
+ if (curPitch < 0.0f && pitchDelta < 0)
+ pitchDir = -1;
+ else if (curPitch > 0.0f && pitchDelta > 0)
+ pitchDir = 1;
+ }
+
+ //now put the pitch into the pitch move struct
+ if (pitchDir > 0)
+ mMove.pitch = mPitchUpMax;
+ else if (pitchDir < 0)
+ mMove.pitch = mPitchDownMax;
+ }
+
+ //now calculate the actual movement towards mPilotDestination
+ //first calculate speed - scale by whether we're already heading in the right direction...
+ F32 speed = 0.0f;
+ if ((mFabs(xDiff) > 5.0f) || (mFabs(yDiff) > 5.0f))
+ {
+ //if we're heading within a 45 deg cone, add some speed
+ if (mFabs(yawDiff < 0.8f))
+ speed = 1.0f - (mFabs(yawDiff) / M_PI);
+ }
+
+ //slow our movespeed down according to how far we are from our target...
+ if (speed > 0.0f && dist2D >= 5.0f && dist2D <= 20.0f)
+ speed *= 0.3f * getMin(0.7f, dist2D / 20.0f);
+
+ //see if we need to reverse thrust...
+ if (dist2D < 6.0f && velocity2D.len() > 5.0f && mFabs(velocityDot) >= 0.78f)
+ speed = getMax(-1.0f, -2.0f * speed);
+
+ //now factor in our pilot speed
+ speed *= mPilotSpeed;
+
+ //see if we should add some jetting
+ if (velocity2D.len() < 8.0f && curPitch <= 0.05f && zDiff > 5.0f)
+ mTriggers[JetTrigger] = true;
+
+ //set some debugging vars
+ mVehicleLocation = curLocation;
+ mCurrentPitch = curPitch;
+ mDesiredPitch = newPitch;
+ mPitchIncrement = mMove.pitch;
+ mPilotDistToDest2D = dist2D;
+ mPilotCurVelocity = myVehicle->getVelocity().len();
+ mPilotCurThrust = speed;
+ mCurrentYaw = curYaw;
+
+ //fill in the move struction
+ if (((mFabs(xDiff) > 5.0f) || (mFabs(yDiff) > 5.0f)) && (! isZero(speed)))
+ {
+ if (mFabs(xDiff) <= 5.0f)
+ mMove.y = (curLocation.y > mPilotDestination.y ? -speed : speed);
+ else if (mFabs(yDiff) <= 5.0f)
+ mMove.x = (curLocation.x > mPilotDestination.x ? -speed : speed);
+ else if (mFabs(xDiff) > mFabs(yDiff))
+ {
+ F32 value = mFabs(yDiff / xDiff) * speed;
+ mMove.y = (curLocation.y > mPilotDestination.y ? -value : value);
+ mMove.x = (curLocation.x > mPilotDestination.x ? -speed : speed);
+ }
+ else
+ {
+ F32 value = mFabs(xDiff / yDiff) * speed;
+ mMove.x = (curLocation.x > mPilotDestination.x ? -value : value);
+ mMove.y = (curLocation.y > mPilotDestination.y ? -speed : speed);
+ }
+
+ //now multiply the move vector by the transpose of the object rotation matrix
+ moveMatrix.transpose();
+ Point3F newMove;
+ moveMatrix.mulP(Point3F(mMove.x, mMove.y, 0), &newMove);
+
+ //and sub the result back in the move structure
+ mMove.x = newMove.x;
+ mMove.y = newMove.y;
+ }
+ }
+ }
+
+ //copy the triggers into the move
+ for (int i = 0; i < MaxTriggerKeys; i++)
+ {
+ mMove.trigger[i] = mTriggers[i];
+ mTriggers[i] = false;
+ }
+}
+
+//-----------------------------------------------------------------------------
+//STEP AND TASK FUNCTIONS
+
+void AIConnection::clearStep()
+{
+ if (mStep)
+ mStep->deleteObject();
+ mStep = NULL;
+}
+
+void AIConnection::setStep(AIStep *step)
+{
+ clearStep();
+ mStep = step;
+}
+
+const char *AIConnection::getStepStatus()
+{
+ if (! mStep)
+ return "Finished";
+ else
+ {
+ int status = mStep->getStatus();
+ switch (status)
+ {
+ case AIStep::InProgress:
+ return "InProgress";
+
+ case AIStep::Failed:
+ return "Failed";
+
+ case AIStep::Finished:
+ default:
+ return "Finished";
+ }
+ }
+}
+
+const char *AIConnection::getStepName()
+{
+ if (! mStep)
+ return "NONE";
+ else
+ {
+ const char *stepName = mStep->getName();
+ if (!stepName || !stepName[0])
+ return "NONE";
+ else
+ return stepName;
+ }
+}
+
+void AIConnection::clearTasks()
+{
+ while (mTaskList.size())
+ {
+ AITask *tempPtr = mTaskList[0];
+ mTaskList.pop_front();
+ tempPtr->deleteObject();
+ }
+ mCurrentTask = NULL;
+}
+
+void AIConnection::addTask(AITask *task)
+{
+ if (! task)
+ return;
+
+ mTaskList.push_back(task);
+}
+
+void AIConnection::removeTask(S32 id)
+{
+ for (S32 i = 0; i < mTaskList.size(); i++)
+ {
+ if (mTaskList[i]->getId() == id)
+ {
+ if (mCurrentTask == mTaskList[i])
+ {
+ mCurrentTask->retire(this);
+ mCurrentTask = NULL;
+ }
+ mTaskList[i]->deleteObject();
+ mTaskList.erase(i);
+ break;
+ }
+ }
+}
+
+void AIConnection::listTasks()
+{
+ for (S32 i = 0; i < mTaskList.size(); i++)
+ {
+ Con::printf("%d: %s", mTaskList[i]->getId(), mTaskList[i]->getName());
+ }
+}
+
+void AIConnection::missionCycleCleanup()
+{
+ setMoveMode(ModeStop);
+ clearTasks();
+ clearStep();
+ mPath.forceSearch();
+ mPath.missionCycleCleanup();
+}
+
diff --git a/ai/aiConnection.h b/ai/aiConnection.h
new file mode 100644
index 0000000..8ac37d9
--- /dev/null
+++ b/ai/aiConnection.h
@@ -0,0 +1,403 @@
+//-----------------------------------------------------------------------------
+// V12 Engine
+//
+// Copyright (c) 2001 GarageGames.Com
+// Portions Copyright (c) 2001 by Sierra Online, Inc.
+//-----------------------------------------------------------------------------
+
+#ifndef _AICONNECTION_H_
+#define _AICONNECTION_H_
+
+#ifndef _MPOINT_H_
+#include "math/mPoint.h"
+#endif
+#ifndef _SIMBASE_H_
+#include "console/simBase.h"
+#endif
+#ifndef _GAMECONNECTION_H_
+#include "game/gameConnection.h"
+#endif
+#ifndef _MOVEMANAGER_H_
+#include "game/moveManager.h"
+#endif
+#ifndef _PLAYER_H_
+#include "game/player.h"
+#endif
+#ifndef _PROJECTILE_H_
+#include "game/projectile.h"
+#endif
+#ifndef _GRAPH_H_
+#include "ai/graph.h"
+#endif
+#ifndef _AINAVJETTING_H_
+#include "ai/aiNavJetting.h"
+#endif
+
+//#define DEBUG_AI 1
+
+class AIStep;
+class AITask;
+class AIConnection : public GameConnection
+{
+ typedef GameConnection Parent;
+
+ //moves for the AI are not queued up, since they are moved on the server
+ Move mMove;
+
+ F32 mMoveSpeed;
+ S32 mMoveMode;
+ S32 mMoveModePending;
+ F32 mMoveTolerance; //how close to the destination before we stop
+ Point3F mMoveDestination; //this is the desired final destination
+ Point3F mNodeLocation; //this is a node along the path towards our final destination
+ Point3F mMoveLocation; //this is direction to take to arrive at the node, or the destination
+ Point3F mEvadeLocation; //if mEvadingCounter > 0, this location will override the above two
+ Point3F mAimLocation;
+ S32 mLookAtTargetTimeMS; //used to slow down the aim twitching...
+
+ //vars used to determine when to force a path recalc
+ Point3F mPrevNodeLocation;
+ Point3F mInitialLocation;
+
+ //Engagement vars - should be able to shoot at anyone regardless of step/task
+ SimObjectPtr mEngageTarget;
+ enum EngageStates
+ {
+ ChooseWeapon,
+ OutOfRange,
+ ReloadWeapon,
+ FindTargetPoint,
+ AimAtTarget,
+ FireWeapon,
+ Finished
+ };
+ S32 mEngageState;
+ S32 mStateCounter;
+ S32 mDelayCounter;
+ S32 mPackCheckCounter;
+ bool mAimAtLazedTarget;
+ S32 mTurretMountedId;
+
+ //vehicle piloting vars
+ Point3F mPilotDestination;
+ Point3F mPilotAimLocation;
+ F32 mPilotSpeed;
+ F32 mPitchUpMax;
+ F32 mPitchDownMax;
+ F32 mPitchIncMax;
+
+ //piloting debug vars
+ Point3F mVehicleLocation;
+ F32 mPilotDistToDest2D;
+ F32 mPilotCurVelocity;
+ F32 mPilotCurThrust;
+ F32 mCurrentPitch;
+ F32 mPreviousPitch;
+ F32 mDesiredPitch;
+ F32 mPitchIncrement;
+ F32 mCurrentYaw;
+
+ StringTableEntry mProjectileName;
+ ProjectileData *mProjectile;
+
+ SimObjectPtr mEnemyProjectile;
+ S32 mProjectileCounter;
+ S32 mEvadingCounter;
+ bool mIsEvading;
+ Point3F mImpactLocation; //not really used for anything but debugging...
+
+ SimObjectPtr mVictim;
+ SimObjectPtr mCorpse;
+ Point3F mCorpseLocation;
+ S32 mVictimTime;
+
+ //if no engagement target, we may have a gamebase object to destroy/repair
+ SimObjectPtr mTargetObject;
+ S32 mObjectMode;
+
+ //these vars are used for both engagetarget and targetobject
+ F32 mRangeToTarget;
+ S32 mCheckTargetLOSCounter;
+ bool mTargetInRange;
+ bool mTargetInSight;
+
+public:
+ enum ObjectModes
+ {
+ DestroyObject = 0,
+ RepairObject,
+ LazeObject,
+ MortarObject,
+ MissileVehicle,
+ MissileNoLock,
+ AttackMode1,
+ AttackMode2,
+ AttackMode3,
+ AttackMode4,
+ NumObjectModes
+ };
+
+private:
+ //NAV Graph vars
+ NavigationPath mPath;
+ AIJetting mJetting;
+ Point3F mPathDest;
+ bool mNewPath;
+ bool mNavUsingJet;
+
+public:
+ S32 mEngageMinDistance;
+ S32 mEngageMaxDistance;
+ S32 mTriggerCounter;
+ S32 mScriptTriggerCounter;
+ F32 mWeaponEnergy;
+ F32 mWeaponErrorFactor;
+ bool mFiring;
+ ShapeBaseImageData* mMountedImage;
+
+ //temp vars recalculated and used each process loop
+ Point3F mLocation;
+ Point3F mVelocity;
+ Point3F mVelocity2D;
+ Point3F mRotation;
+ Point3F mHeadRotation;
+ Point3F mMuzzlePosition;
+ Point3F mEyePosition;
+ F32 mDamage;
+ F32 mEnergy;
+ F32 mEnergyReserve; //navigation must not use energy below the reserve
+ F32 mEnergyFloat; //used to determine whether we are recharging
+ bool mEnergyRecharge;
+ bool mEnergyAvailable;
+ bool mHeadingDownhill;
+ bool mInWater;
+ bool mOutdoors;
+ F32 mDotOffCourse;
+
+ Player *mTargetPlayer;
+ Point3F mTargLocation;
+ Point3F mTargVelocity;
+ Point3F mTargVelocity2D;
+ Point3F mTargRotation;
+ F32 mTargEnergy;
+ F32 mTargDamage;
+ bool mTargInWater;
+
+ Point3F mObjectLocation;
+ bool mObjectInWater;
+
+ //skill related vars
+ F32 mSkillLevel;
+ S32 mTargStillTimeMS;
+ F32 mTargPrevTimeMS;
+ Point3F mTargPrevLocation[4];
+ S32 mChangeWeaponCounter;
+
+ F32 mDistToTarg2D;
+ F32 mDistToNode2D;
+ F32 mDistToObject2D;
+
+ enum
+ {
+ ModeStop = 0,
+ ModeWalk,
+ ModeGainHeight,
+ ModeExpress,
+ ModeMountVehicle,
+ ModeStuck,
+ ModeCount
+ };
+
+ //vars to determine/handle if we're stuck
+ Point3F mStuckLocation;
+ Point3F mStuckDestination;
+ bool mStuckInitialized;
+ S32 mStuckTimer;
+ bool mStuckTryJump;
+ bool mStuckJumpInitialized;
+ S32 mStuckJumpTimer;
+
+ SimObjectPtr mAvoidingObject;
+ Point3F mAvoidSourcePoint;
+ Point3F mAvoidDestinationPoint;
+ Point3F mAvoidMovePoint;
+ bool mAvoidForcedPath;
+
+protected:
+ void scriptProcessEngagement();
+ void scriptChooseEngageWeapon(F32 distToTarg);
+ void scriptChooseObjectWeapon(F32 distToTarg);
+ void aiDebugStuff();
+
+private:
+ enum
+ {
+ FireTrigger = 0,
+ JumpTrigger = 2,
+ JetTrigger = 3,
+ GrenadeTrigger = 4,
+ MineTrigger = 5
+ };
+ bool mTriggers[MaxTriggerKeys];
+
+ //Step and Task members
+ AIStep *mStep;
+
+ Vector mTaskList;
+ AITask *mCurrentTask;
+ S32 mCurrentTaskTime;
+
+ //DETECTION MEMBERS
+ struct PlayerDetectionEntry
+ {
+ S32 playerId;
+ bool playerLOS;
+ S32 playerLOSTime;
+ Point3F playerLastPosition;
+ };
+ Vector mPlayerDetectionTable;
+ S32 mPlayerDetectionIndex;
+ S32 mPlayerDetectionCounter;
+ S32 mDetectHiddenPeriod; //even without LOS, the player will know where the enemy is until
+ //this detectHiddenPeriod is over... after that, they'll only use
+ //the last known location...
+ S32 mBlindedTimer; //used
+
+public:
+ AIConnection();
+ ~AIConnection();
+
+public:
+ DECLARE_CONOBJECT(AIConnection);
+ static void consoleInit();
+
+ F32 fixRadAngle(F32 angle);
+ static F32 get2DDot(const Point3F &vec1, const Point3F &vec2);
+ static F32 get2DAngle(const Point3F &endPt, const Point3F &basePt);
+ F32 getOutdoorRadius(const Point3F &location); //returns the outdoor freedom radius, or -1 if indoors
+
+ void setSkillLevel(F32 level);
+ F32 getSkillLevel() { return mSkillLevel; }
+
+ void setMoveSpeed(F32 speed);
+ void setMoveMode(S32 mode, bool abortStuckCode = false);
+ void setMoveTolerance(F32 tolerance);
+ void setMoveLocation(const Point3F &location);
+ void setMoveDestination(const Point3F &location);
+ void setTurretMounted(S32 turretId) { mTurretMountedId = turretId; }
+ void setAimLocation(const Point3F &location);
+ void setWeaponInfo(StringTableEntry projectile, S32 minDist, S32 maxDist, S32 triggerCount, F32 energyRequired, F32 errorFactor = 1.0f);
+ void setEnergyLevels(F32 eReserve, F32 eFloat = 0.10f);
+
+ bool setScriptAimLocation(const Point3F &location, S32 duration); //returns false if we have a target object/player
+
+ //here are the vehicle piloting methods
+ void setPilotPitchRange(F32 pitchUpMax, F32 pitchDownMax, F32 pitchIncMax);
+ void setPilotDestination(const Point3F &dest, F32 maxSpeed);
+ void setPilotAimLocation(const Point3F &dest);
+
+ //bool findCrossVector(const Point3F &v1, const Point3F &v2, Point3F *result);
+ Point3F dopeAimLocation(const Point3F &startLocation, const Point3F &aimLocation);
+
+ F32 angleDifference(F32 angle1, F32 angle2);
+ Point3F correctHeading();
+ Point3F avoidPlayers(Player *player, const Point3F &desiredDestination, bool destIsFinal);
+
+ void setEngageTarget(GameConnection *target);
+ S32 getEngageTarget();
+ void setTargetObject(ShapeBase *targetObject, F32 range = 35.0f, S32 objectMode = DestroyObject);
+ S32 getTargetObject();
+ bool TargetInRange() { return mTargetInRange || mTargetInSight; }
+ bool TargetInSight() { return mTargetInSight; }
+
+ void setVictim(GameConnection *victim, Player *corpse);
+ S32 getVictimCorpse();
+ S32 getVictimTime();
+
+ F32 getMoveSpeed() { return mMoveSpeed; }
+ S32 getMoveMode() { return mMoveMode; }
+ F32 getMoveTolerance() { return mMoveTolerance; }
+ Point3F getMoveDestination() { return mMoveDestination; }
+ Point3F getMoveLocation() { return mMoveLocation; }
+ Point3F getAimLocation() { return mAimLocation; }
+ bool scriptIsAiming() { return mLookAtTargetTimeMS > Sim::getCurrentTime(); }
+
+ void setPathDest(const Point3F * dest = NULL);
+ const Point3F * getPathDest();
+ void setPathCapabilities(Player * player);
+ F32 getPathDistance(const Point3F &destination, const Point3F &source);
+ F32 getPathDistRemaining(F32 maxDist);
+ Point3F getLOSLocation(const Point3F &targetPoint, F32 minDistance, F32 maxDistance, const Point3F &nearPoint);
+ Point3F getHideLocation(const Point3F &targetPoint, F32 range, const Point3F &nearPoint, F32 hideLength);
+
+ void scriptSustainFire(S32 count = 1);
+ void pressFire(bool value = true) { mTriggers[FireTrigger] = value; }
+ void pressJump() { mTriggers[JumpTrigger] = true; }
+ void pressJet() { mTriggers[JetTrigger] = true; }
+ void pressGrenade() { mTriggers[GrenadeTrigger] = true; }
+ void pressMine() { mTriggers[MineTrigger] = true; }
+
+ bool isFiring() { return mTriggers[FireTrigger]; }
+ bool isJumping() { return mTriggers[JumpTrigger]; }
+ bool isJetting() { return mTriggers[JetTrigger]; }
+
+ void getMoveList(Move**,U32* numMoves);
+ void clearMoves(U32) { }
+ bool areMovesPending() { return true; }
+
+ //function to update tasks, etc...
+ void process(ShapeBase *ctrlObject);
+ void processMovement(Player *player);
+ void processVehicleMovement(Player *player);
+ void processPilotVehicle(Vehicle *myVehicle);
+ void processEngagement(Player *player);
+ void setEvadeLocation(const Point3F &dangerLocation, S32 durationTicks = 0);
+ void initProcessVars(Player *player);
+ void updateDetectionTable(Player *player);
+ bool hasLOSToClient(S32 clientId, S32 &losTime, Point3F &lastLocation);
+ void clientDetected(S32 targId);
+ void setDetectPeriod(S32 value) { mDetectHiddenPeriod = value; }
+ S32 getDetectPeriod() { return mDetectHiddenPeriod; }
+ void setBlinded(S32 duration);
+
+ //step and task methods
+ void setStep(AIStep *step);
+ void clearStep();
+ const char *getStepStatus();
+ const char *getStepName();
+
+ void clearTasks();
+ void addTask(AITask *task);
+ void removeTask(S32 id);
+ AITask *getCurrentTask() { return mCurrentTask; }
+ S32 getTaskTime() { return mCurrentTaskTime; }
+ void listTasks();
+
+ void missionCycleCleanup();
+};
+
+extern const char *gTargetObjectMode[AIConnection::NumObjectModes];
+
+class AISlicer
+{
+ // MRandomLCG mRand;
+ // bool mEnabled;
+ U32 mDelay;
+ U32 mLastTime;
+ // U32 mCapPileUp;
+ U32 mDebugTotal;
+ F32 mBudget;
+
+ public:
+ AISlicer();
+ void init(U32 delay = 22, S32 maxPerTick = 1);
+ void reset();
+ bool ready(S32 &counter, S32 resetVal);
+};
+
+extern AISlicer gCalcWeightSlicer;
+extern AISlicer gScriptEngageSlicer;
+extern bool gAISystemEnabled;
+
+
+#endif
diff --git a/ai/aiConsole.cc b/ai/aiConsole.cc
new file mode 100644
index 0000000..a2cad2b
--- /dev/null
+++ b/ai/aiConsole.cc
@@ -0,0 +1,766 @@
+//-----------------------------------------------------------------------------
+// V12 Engine
+//
+// Copyright (c) 2001 GarageGames.Com
+// Portions Copyright (c) 2001 by Sierra Online, Inc.
+//-----------------------------------------------------------------------------
+
+#include "math/mMatrix.h"
+#include "console/console.h"
+#include "console/simBase.h"
+#include "game/gameBase.h"
+#include "game/moveManager.h"
+#include "game/player.h"
+#include "ai/aiConnection.h"
+#include "ai/aiStep.h"
+#include "ai/aiNavStep.h"
+#include "ai/aiTask.h"
+#include "core/idGenerator.h"
+
+static S32 cAIConnect(SimObject *, S32 argc, const char** argv)
+{
+ //create the connection
+ AIConnection *aiConnection = new AIConnection();
+ aiConnection->registerObject();
+
+ //add the AI to the client group
+ SimGroup *g = Sim::getClientGroup();
+ g->addObject(aiConnection);
+
+ //execute the console function
+ const char *teamStr = "-2";
+ const char *skillStr = "0.5";
+ const char *offenseStr = "1";
+ const char *voicePitchStr = "0";
+ const char *voiceStr = "";
+ if (argc >= 3)
+ teamStr = argv[2];
+ if (argc >= 4)
+ skillStr = argv[3];
+ if (argc >= 5)
+ {
+ if (!dStricmp(argv[4], "true") || dAtoi(argv[4]) > 0)
+ offenseStr = "1";
+ else
+ offenseStr = "0";
+ }
+ if (argc >= 6)
+ voiceStr = argv[5];
+ if (argc >= 7)
+ voicePitchStr = argv[6];
+ Con::executef(aiConnection, 7, "onAIConnect", argv[1], teamStr, skillStr, offenseStr, voiceStr, voicePitchStr);
+
+ return aiConnection->getId();
+}
+
+static void cAIDrop(SimObject *obj, S32, const char**)
+{
+ //call the script function
+ AIConnection *ai = static_cast(obj);
+ Con::executef(ai, 1, "onAIDrop");
+
+ //next, call the game disconnection code...
+ Con::printf("AI Client %d is disconnected.", ai->getId());
+ ai->setDisconnectReason( "Removed" );
+
+ //finally, delete the object
+ ai->deleteObject();
+}
+
+static F32 cAIGetGraphDistance(SimObject *, S32, const char** argv)
+{
+ Point3F dest(0, 0, 0);
+ dSscanf(argv[1], "%f %f %f", &dest.x, &dest.y, &dest.z);
+ Point3F source(0, 0, 0);
+ dSscanf(argv[2], "%f %f %f", &source.x, &source.y, &source.z);
+ return NavigationGraph::fastDistance(source, dest);
+}
+
+// Intended for initialization of whatever management is central to all bots.
+AISlicer gCalcWeightSlicer;
+AISlicer gScriptEngageSlicer;
+
+static bool cAISlicerInit(SimObject *, S32, const char**)
+{
+ gCalcWeightSlicer.init(30, 1);
+ gScriptEngageSlicer.init(15, 2);
+ return true;
+}
+
+static bool cAISlicerReset(SimObject *, S32, const char**)
+{
+ gCalcWeightSlicer.reset();
+ gScriptEngageSlicer.reset();
+ return true;
+}
+
+static void cAISystemEnabled(SimObject *, S32 argc, const char **argv)
+{
+ bool status = true;
+ if (argc == 2)
+ status = ((!dStricmp(argv[1], "true")) || (dAtoi(argv[1]) != 0));
+ gAISystemEnabled = status;
+}
+
+bool gAISystemEnabled = false;
+
+void AIInit()
+{
+ Con::addCommand("aiConnect", cAIConnect, "aiConnect(name [, team , skill, offense, voice, voicePitch]);", 2, 7);
+ Con::addCommand("AIGetPathDistance", cAIGetGraphDistance, "AIGetPathDistance(fromPoint, toPoint);", 3, 3);
+ Con::addCommand("AISlicerInit", cAISlicerInit, "AISlicerInit();", 1, 1);
+ Con::addCommand("AISlicerReset", cAISlicerReset, "AISlicerReset();", 1, 1);
+ Con::addCommand("AISystemEnabled", cAISystemEnabled, "AISystemEnabled([bool]);", 1, 2);
+}
+
+static void cAISetSkillLevel(SimObject *obj, S32, const char** argv)
+{
+ AIConnection *ai = static_cast(obj);
+ ai->setSkillLevel(dAtof(argv[2]));
+}
+
+static F32 cAIGetSkillLevel(SimObject *obj, S32, const char**)
+{
+ AIConnection *ai = static_cast(obj);
+ return ai->getSkillLevel();
+}
+
+static void cAISetMoveSpeed(SimObject *obj, S32, const char** argv)
+{
+ AIConnection *ai = static_cast(obj);
+ ai->setMoveSpeed(dAtoi(argv[2]));
+}
+
+static void cAIStop(SimObject *obj, S32, const char**)
+{
+ AIConnection *ai = static_cast(obj);
+ ai->setMoveMode(AIConnection::ModeStop);
+}
+
+static void cAIStepMove(SimObject *obj, S32 argc, const char** argv)
+{
+ AIConnection *ai = static_cast(obj);
+
+ //first, clear the current step
+ ai->clearStep();
+
+ //set the "move to" destination
+ VectorF v(0,0,0);
+ dSscanf(argv[2], "%f %f %f", &v.x, &v.y, &v.z);
+ ai->setMoveDestination(Point3F(v.x, v.y, v.z));
+
+ //set the move mode if given
+ if (argc >= 4)
+ ai->setMoveTolerance(dAtof(argv[3]));
+ else
+ ai->setMoveTolerance(0.25f);
+
+ //don't change the move mode if we're stuck...
+ if (ai->getMoveMode() != AIConnection::ModeStuck)
+ {
+ if (argc >= 5)
+ ai->setMoveMode(dAtoi(argv[4]));
+ else
+ ai->setMoveMode(AIConnection::ModeExpress);
+ }
+}
+
+static bool cAIAimAtLocation(SimObject *obj, S32 argc, const char** argv)
+{
+ AIConnection *ai = static_cast(obj);
+ VectorF v(0,0,0);
+ dSscanf(argv[2], "%f %f %f", &v.x, &v.y, &v.z);
+ S32 duration = 1500;
+ if (argc == 4)
+ duration = getMax(0, dAtoi(argv[3]));
+ return ai->setScriptAimLocation(Point3F(v.x, v.y, v.z), duration);
+}
+
+static const char* cAIGetAimLocation(SimObject *obj, S32, const char**)
+{
+ AIConnection *ai = static_cast(obj);
+ Point3F aimPoint = ai->getAimLocation();
+
+ char* returnBuffer = Con::getReturnBuffer(256);
+ dSprintf(returnBuffer, 256, "%f %f %f", aimPoint.x, aimPoint.y, aimPoint.z);
+ return returnBuffer;
+}
+
+static bool cAIIsMountingVehicle(SimObject *obj, S32, const char**)
+{
+ AIConnection *ai = static_cast(obj);
+ return (ai->getMoveMode() == AIConnection::ModeMountVehicle);
+}
+
+static void cAISetTurretMounted(SimObject *obj, S32, const char**argv)
+{
+ AIConnection *ai = static_cast(obj);
+ ai->setTurretMounted(dAtoi(argv[2]));
+}
+
+static void cAISetPilotDestination(SimObject *obj, S32 argc, const char**argv)
+{
+ AIConnection *ai = static_cast(obj);
+
+ VectorF v(0,0,0);
+ dSscanf(argv[2], "%f %f %f", &v.x, &v.y, &v.z);
+
+ F32 speed = 1.0f;
+ if (argc >= 4)
+ speed = dAtof(argv[3]);
+ ai->setPilotDestination(v, speed);
+}
+
+static void cAISetPilotAimLocation(SimObject *obj, S32, const char**argv)
+{
+ AIConnection *ai = static_cast(obj);
+ VectorF v(0,0,0);
+ dSscanf(argv[2], "%f %f %f", &v.x, &v.y, &v.z);
+ ai->setPilotAimLocation(v);
+}
+
+static void cAISetPilotPitchRange(SimObject *obj, S32, const char**argv)
+{
+ AIConnection *ai = static_cast(obj);
+ ai->setPilotPitchRange(dAtof(argv[2]), dAtof(argv[3]), dAtof(argv[4]));
+}
+
+static F32 cAIGetPathDistance(SimObject *obj, S32 argc, const char** argv)
+{
+ AIConnection *ai = static_cast(obj);
+ Point3F dest(0, 0, 0);
+ dSscanf(argv[2], "%f %f %f", &dest.x, &dest.y, &dest.z);
+ Point3F source(-1, -1, -1);
+ if (argc == 4)
+ dSscanf(argv[3], "%f %f %f", &source.x, &source.y, &source.z);
+ return ai->getPathDistance(dest, source);
+}
+
+static F32 cAIPathDistRemaining(SimObject *obj, S32, const char** argv)
+{
+ AIConnection *ai = static_cast(obj);
+ return ai->getPathDistRemaining(dAtof(argv[2]));
+}
+
+static const char* cAIGetLOSLocation(SimObject *obj, S32 argc, const char** argv)
+{
+ AIConnection *ai = static_cast(obj);
+ Point3F dest(0, 0, 0);
+ dSscanf(argv[2], "%f %f %f", &dest.x, &dest.y, &dest.z);
+ F32 minDist = -1;
+ if (argc >= 4)
+ minDist = dAtof(argv[3]);
+ F32 maxDist = 1e6;
+ if (argc >= 5)
+ maxDist = dAtof(argv[4]);
+ Point3F nearPoint(-1, -1, -1);
+ if (argc >= 6)
+ dSscanf(argv[5], "%f %f %f", &nearPoint.x, &nearPoint.y, &nearPoint.z);
+
+ Point3F graphPoint = ai->getLOSLocation(dest, minDist, maxDist, nearPoint);
+
+ char* returnBuffer = Con::getReturnBuffer(256);
+ dSprintf(returnBuffer, 256, "%f %f %f", graphPoint.x, graphPoint.y, graphPoint.z);
+ return returnBuffer;
+}
+
+static const char* cAIGetHideLocation(SimObject *obj, S32, const char** argv)
+{
+ AIConnection *ai = static_cast(obj);
+ Point3F dest(0, 0, 0);
+ dSscanf(argv[2], "%f %f %f", &dest.x, &dest.y, &dest.z);
+ F32 range = dAtof(argv[3]);
+ Point3F nearPoint(0, 0, 0);
+ dSscanf(argv[4], "%f %f %f", &nearPoint.x, &nearPoint.y, &nearPoint.z);
+ F32 hideLength = dAtof(argv[5]);
+
+ Point3F graphPoint = ai->getHideLocation(dest, range, nearPoint, hideLength);
+
+ char* returnBuffer = Con::getReturnBuffer(256);
+ dSprintf(returnBuffer, 256, "%f %f %f", graphPoint.x, graphPoint.y, graphPoint.z);
+ return returnBuffer;
+}
+
+static void cAISetEngageTarget(SimObject *obj, S32, const char **argv)
+{
+ AIConnection *ai = static_cast(obj);
+
+ //find the target
+ GameConnection *target;
+ if (Sim::findObject(argv[2], target))
+ ai->setEngageTarget(target);
+ else
+ ai->setEngageTarget(NULL);
+}
+
+static const char* cAIGetEngageTarget(SimObject *obj, S32, const char **)
+{
+ AIConnection *ai = static_cast(obj);
+ S32 targetId = ai->getEngageTarget();
+
+ char* returnBuffer = Con::getReturnBuffer(256);
+ dSprintf(returnBuffer, 256, "%d", targetId);
+ return returnBuffer;
+}
+
+static void cAISetVictim(SimObject *obj, S32, const char **argv)
+{
+ AIConnection *ai = static_cast(obj);
+
+ //find the victim client
+ GameConnection *victim;
+ if (! Sim::findObject(argv[2], victim))
+ return;
+
+ //find the corpse
+ Player *corpse;
+ if (! Sim::findObject(argv[3], corpse))
+ return;
+
+ ai->setVictim(victim, corpse);
+}
+
+static S32 cAIGetVictimCorpse(SimObject *obj, S32, const char **)
+{
+ AIConnection *ai = static_cast(obj);
+ return ai->getVictimCorpse();
+}
+
+static S32 cAIGetVictimTime(SimObject *obj, S32, const char **)
+{
+ AIConnection *ai = static_cast(obj);
+ return ai->getVictimTime();
+}
+
+static bool cAIHasLOSToClient(SimObject *obj, S32, const char **argv)
+{
+ AIConnection *ai = static_cast(obj);
+ S32 losTime;
+ Point3F losLocation;
+ return ai->hasLOSToClient(dAtoi(argv[2]), losTime, losLocation);
+}
+
+static S32 cAIGetClientLOSTime(SimObject *obj, S32, const char **argv)
+{
+ AIConnection *ai = static_cast(obj);
+ S32 losTime;
+ Point3F losLocation;
+ ai->hasLOSToClient(dAtoi(argv[2]), losTime, losLocation);
+ return losTime;
+}
+
+static const char* cAIGetDetectLocation(SimObject *obj, S32, const char **argv)
+{
+ AIConnection *ai = static_cast(obj);
+ S32 losTime;
+ Point3F losLocation(0, 0, 0);
+ ai->hasLOSToClient(dAtoi(argv[2]), losTime, losLocation);
+
+ char* returnBuffer = Con::getReturnBuffer(256);
+ dSprintf(returnBuffer, 256, "%f %f %f", losLocation.x, losLocation.y, losLocation.z);
+ return returnBuffer;
+}
+
+static void cAIClientDetected(SimObject *obj, S32, const char **argv)
+{
+ AIConnection *ai = static_cast(obj);
+ ai->clientDetected(dAtoi(argv[2]));
+}
+
+static void cAISetDetectPeriod(SimObject *obj, S32, const char **argv)
+{
+ AIConnection *ai = static_cast(obj);
+ ai->setDetectPeriod(dAtoi(argv[2]));
+}
+
+static S32 cAIGetDetectPeriod(SimObject *obj, S32, const char **)
+{
+ AIConnection *ai = static_cast(obj);
+ return ai->getDetectPeriod();
+}
+
+static void cAISetBlinded(SimObject *obj, S32, const char **argv)
+{
+ AIConnection *ai = static_cast(obj);
+ ai->setBlinded(dAtoi(argv[2]));
+}
+
+static void cAISetDangerLocation(SimObject *obj, S32 argc, const char **argv)
+{
+ AIConnection *ai = static_cast(obj);
+ Point3F dest(0, 0, 0);
+ dSscanf(argv[2], "%f %f %f", &dest.x, &dest.y, &dest.z);
+ S32 duration = 0;
+ if (argc == 4)
+ duration = dAtoi(argv[3]);
+ ai->setEvadeLocation(dest, duration);
+}
+
+const char *gTargetObjectMode[AIConnection::NumObjectModes] =
+{
+ "Destroy",
+ "Repair",
+ "Laze",
+ "Mortar",
+ "Missile",
+ "MissileNoLock",
+ "AttackMode1",
+ "AttackMode2",
+ "AttackMode3",
+ "AttackMode4"
+};
+
+static void cAISetTargetObject(SimObject *obj, S32 argc, const char **argv)
+{
+ AIConnection *ai = static_cast(obj);
+
+ //find the target
+ ShapeBase *targetObject;
+ if (Sim::findObject(argv[2], targetObject))
+ {
+ F32 range = 30;
+ if (argc >= 4)
+ range = dAtof(argv[3]);
+ S32 objectMode = AIConnection::DestroyObject;
+ if (argc == 5)
+ {
+ for (int i = 0; i < AIConnection::NumObjectModes; i++)
+ {
+ if (! dStricmp(argv[4], gTargetObjectMode[i]))
+ {
+ objectMode = i;
+ break;
+ }
+ }
+ }
+ ai->setTargetObject(targetObject, range, objectMode);
+ }
+ else
+ ai->setTargetObject(NULL);
+}
+
+static const char* cAIGetTargetObject(SimObject *obj, S32, const char **)
+{
+ AIConnection *ai = static_cast(obj);
+ S32 targetId = ai->getTargetObject();
+ char* returnBuffer = Con::getReturnBuffer(256);
+ dSprintf(returnBuffer, 256, "%d", targetId);
+ return returnBuffer;
+}
+
+static void cAIStepRangeObject(SimObject *obj, S32 argc, const char **argv)
+{
+ AIConnection *ai = static_cast(obj);
+
+ //find the target
+ GameBase *targetObject;
+ if (Sim::findObject(argv[2], targetObject))
+ {
+ F32 minDist, maxDist;
+ minDist = dAtof(argv[4]);
+ maxDist = dAtof(argv[5]);
+ Point3F fromLocation(0, 0, 0);
+ AIStep *step;
+ if (argc == 7)
+ {
+ dSscanf(argv[6], "%f %f %f", &fromLocation.x, &fromLocation.y, &fromLocation.z);
+ step = new AIStepRangeObject(targetObject, argv[3], &minDist, &maxDist, &fromLocation);
+ }
+ else
+ {
+ step = new AIStepRangeObject(targetObject, argv[3], &minDist, &maxDist, NULL);
+ }
+ step->registerObject("AIStepRangeObject");
+ ai->addObject(step);
+ ai->setStep(step);
+ }
+}
+
+static bool cAITargetInSight(SimObject *obj, S32, const char **)
+{
+ AIConnection *ai = static_cast(obj);
+ return ai->TargetInSight();
+}
+
+static bool cAITargetInRange(SimObject *obj, S32, const char **)
+{
+ AIConnection *ai = static_cast(obj);
+ return ai->TargetInRange();
+}
+
+static void cAIPressFire(SimObject *obj, S32 argc, const char** argv)
+{
+ AIConnection *ai = static_cast(obj);
+ S32 count = 1;
+ if (argc == 3)
+ count = dAtoi(argv[2]);
+ ai->scriptSustainFire(count);
+}
+
+static void cAIPressJump(SimObject *obj, S32, const char**)
+{
+ AIConnection *ai = static_cast(obj);
+ ai->pressJump();
+}
+
+static void cAIPressJet(SimObject *obj, S32, const char**)
+{
+ AIConnection *ai = static_cast(obj);
+ ai->pressJet();
+}
+
+static void cAIPressGrenade(SimObject *obj, S32, const char**)
+{
+ AIConnection *ai = static_cast(obj);
+ ai->pressGrenade();
+}
+
+static void cAIPressMine(SimObject *obj, S32, const char**)
+{
+ AIConnection *ai = static_cast(obj);
+ ai->pressMine();
+}
+
+static void cAISetWeaponInfo(SimObject *obj, S32 argc, const char **argv)
+{
+ AIConnection *ai = static_cast(obj);
+ S32 triggerCount = (argc >= 6 ? dAtoi(argv[5]) : 1);
+ F32 requiredEnergy = (argc >= 7 ? dAtof(argv[6]) : 0.0);
+ F32 errorFactor = (argc >= 8 ? dAtof(argv[7]) : 1.0);
+ ai->setWeaponInfo(argv[2], dAtoi(argv[3]), dAtoi(argv[4]), triggerCount, requiredEnergy, errorFactor);
+}
+
+static void cAIClearStep(SimObject *obj, S32, const char**)
+{
+ AIConnection *ai = static_cast(obj);
+ ai->clearStep();
+}
+
+static void cAIStepEscort(SimObject *obj, S32, const char **argv)
+{
+ AIConnection *ai = static_cast(obj);
+ ai->clearStep();
+
+ //find the target
+ GameConnection *target;
+ if (Sim::findObject(argv[2], target))
+ {
+ AIStep *step = new AIStepEscort(target);
+ step->registerObject("AIStepEscort");
+ ai->addObject(step);
+ ai->setStep(step);
+ }
+}
+
+static void cAIStepJet(SimObject *obj, S32, const char **argv)
+{
+ AIConnection *ai = static_cast(obj);
+
+ Point3F dest(0,0,0);
+ dSscanf(argv[2], "%f %f %f", &dest.x, &dest.y, &dest.z);
+
+ if (AIStep * step = new AIStepJet(dest)) {
+ ai->clearStep();
+ step->registerObject("AIStepJet");
+ ai->addObject(step);
+ ai->setStep(step);
+ }
+}
+
+static void cAISetPath(SimObject *obj, S32 argc, const char **argv)
+{
+ if (AIConnection * ai = dynamic_cast(obj)) {
+ if (argc == 3) {
+ Point3F dest(0,0,0);
+ dSscanf(argv[2], "%f %f %f", &dest.x, &dest.y, &dest.z);
+ ai->setPathDest(&dest);
+ }
+ else
+ ai->setPathDest();
+ }
+}
+
+static void cAIStepEngage(SimObject *obj, S32, const char **argv)
+{
+ AIConnection *ai = static_cast(obj);
+
+ //the engage step functions better if it can be left to run uninterrupted -
+ //find out if we're already engaging this target before resetting the step...
+ GameConnection *target;
+ if (Sim::findObject(argv[2], target))
+ {
+ S32 curTarget = ai->getEngageTarget();
+ const char *curStepName = ai->getStepName();
+ if (!dStricmp(curStepName, "AIStepEngage") && (curTarget == target->getId()))
+ return;
+
+ //must be someone new we're to engage...
+ AIStep *step = new AIStepEngage(target);
+ step->registerObject("AIStepEngage");
+ ai->addObject(step);
+ ai->setStep(step);
+ }
+ else
+ ai->clearStep();
+}
+
+static void cAIStepIdle(SimObject *obj, S32, const char **argv)
+{
+ AIConnection *ai = static_cast(obj);
+ ai->clearStep();
+
+ Point3F idleLocation(0,0,0);
+ dSscanf(argv[2], "%f %f %f", &idleLocation.x, &idleLocation.y, &idleLocation.z);
+
+ AIStep *step = new AIStepIdlePatrol(&idleLocation);
+ step->registerObject("AIStepIdlePatrol");
+ ai->addObject(step);
+ ai->setStep(step);
+}
+
+static const char* cAIStepGetStatus(SimObject *obj, S32, const char **)
+{
+ AIConnection *ai = static_cast(obj);
+ return ai->getStepStatus();
+}
+
+static const char* cAIStepGetName(SimObject *obj, S32, const char **)
+{
+ AIConnection *ai = static_cast(obj);
+ return ai->getStepName();
+}
+
+static void cAIClearTasks(SimObject *obj, S32, const char**)
+{
+ AIConnection *ai = static_cast(obj);
+ ai->clearTasks();
+}
+
+static const char* cAIAddTask(SimObject *obj, S32, const char **argv)
+{
+ AIConnection *ai = static_cast(obj);
+ AITask *newTask = new AITask();
+ newTask->registerObject(argv[2]);
+ ai->addObject(newTask);
+ ai->addTask(newTask);
+ return avar("%d", newTask->getId());
+}
+
+static void cAIRemoveTask(SimObject *obj, S32, const char **argv)
+{
+ AIConnection *ai = static_cast(obj);
+ ai->removeTask(dAtoi(argv[2]));
+}
+
+static void cAIListTasks(SimObject *obj, S32, const char **)
+{
+ AIConnection *ai = static_cast(obj);
+ ai->listTasks();
+}
+
+static S32 cAIGetTaskId(SimObject *obj, S32, const char **)
+{
+ AIConnection *ai = static_cast(obj);
+ AITask *curTask = ai->getCurrentTask();
+ if (bool(curTask))
+ return curTask->getId();
+ else
+ return -1;
+}
+
+static const char* cAIGetTaskName(SimObject *obj, S32, const char **)
+{
+ AIConnection *ai = static_cast(obj);
+ AITask *curTask = ai->getCurrentTask();
+ if (bool(curTask))
+ return curTask->getName();
+ else
+ return "";
+}
+
+static S32 cAIGetTaskTime(SimObject *obj, S32, const char **)
+{
+ AIConnection *ai = static_cast(obj);
+ return ai->getTaskTime();
+}
+
+static void cAIMissionCycleCleanup(SimObject *obj, S32, const char **)
+{
+ AIConnection *ai = static_cast(obj);
+ ai->missionCycleCleanup();
+}
+
+void AIConnection::consoleInit()
+{
+ Con::addCommand("AIConnection", "drop", cAIDrop, "ai.drop()", 2, 2);
+
+ Con::addCommand("AIConnection", "setSkillLevel", cAISetSkillLevel, "ai.setSkillLevel(float)", 3, 3);
+ Con::addCommand("AIConnection", "getSkillLevel", cAIGetSkillLevel, "ai.getSkillLevel()", 2, 2);
+
+ Con::addCommand("AIConnection", "setEngageTarget", cAISetEngageTarget, "ai.setEngageTarget(client)", 3, 3);
+ Con::addCommand("AIConnection", "getEngageTarget", cAIGetEngageTarget, "ai.getEngageTarget()", 2, 2);
+ Con::addCommand("AIConnection", "setVictim", cAISetVictim, "ai.setVictim(client, corpseObject)", 4, 4);
+ Con::addCommand("AIConnection", "getVictimCorpse", cAIGetVictimCorpse, "ai.getVictimCorpse()", 2, 2);
+ Con::addCommand("AIConnection", "getVictimTime", cAIGetVictimTime, "ai.getVictimTime()", 2, 2);
+
+ Con::addCommand("AIConnection", "hasLOSToClient", cAIHasLOSToClient, "ai.hasLOSToClient(client)", 3, 3);
+ Con::addCommand("AIConnection", "getClientLOSTime", cAIGetClientLOSTime, "ai.getClientLOSTime(client)", 3, 3);
+ Con::addCommand("AIConnection", "getDetectLocation", cAIGetDetectLocation, "ai.getDetectLocation(client)", 3, 3);
+ Con::addCommand("AIConnection", "clientDetected", cAIClientDetected, "ai.clientDetected(client)", 3, 3);
+ Con::addCommand("AIConnection", "setDetectPeriod", cAISetDetectPeriod, "ai.setDetectPeriod()", 3, 3);
+ Con::addCommand("AIConnection", "getDetectPeriod", cAIGetDetectPeriod, "ai.getDetectPeriod()", 2, 2);
+ Con::addCommand("AIConnection", "setBlinded", cAISetBlinded, "ai.setBlinded(durationMS)", 3, 3);
+ Con::addCommand("AIConnection", "setDangerLocation", cAISetDangerLocation, "ai.setDangerLocation(point3F [, durationTicks])", 3, 4);
+
+ Con::addCommand("AIConnection", "setTargetObject", cAISetTargetObject, "ai.setTargetObject(object [, range, mode: destroy/repair/laze])", 3, 5);
+ Con::addCommand("AIConnection", "getTargetObject", cAIGetTargetObject, "ai.getTargetObject()", 2, 2);
+ Con::addCommand("AIConnection", "targetInSight", cAITargetInSight, "ai.targetInSight()", 2, 2);
+ Con::addCommand("AIConnection", "targetInRange", cAITargetInRange, "ai.targetInRange()", 2, 2);
+
+ Con::addCommand("AIConnection", "pressFire", cAIPressFire, "ai.pressFire([sustain count])", 2, 3);
+ Con::addCommand("AIConnection", "pressJump", cAIPressJump, "ai.pressJump()", 2, 2);
+ Con::addCommand("AIConnection", "pressJet", cAIPressJet, "ai.pressJet()", 2, 2);
+ Con::addCommand("AIConnection", "pressGrenade", cAIPressGrenade, "ai.pressGrenade()", 2, 2);
+ Con::addCommand("AIConnection", "pressMine", cAIPressMine, "ai.pressMine()", 2, 2);
+
+ Con::addCommand("AIConnection", "setWeaponInfo", cAISetWeaponInfo, "ai.setWeaponInfo(projectile, minDist, maxDist [, triggerCount, requiredEnergy, errorFactor]);", 5, 8);
+
+ Con::addCommand("AIConnection", "aimAt", cAIAimAtLocation, "bool ai.aimAt(point [, duration MS])", 3, 4);
+ Con::addCommand("AIConnection", "getAimLocation", cAIGetAimLocation, "ai.getAimLocation()", 2, 2);
+
+ Con::addCommand("AIConnection", "isMountingVehicle", cAIIsMountingVehicle, "ai.isMountingVehicle()", 2, 2);
+ Con::addCommand("AIConnection", "setTurretMounted", cAISetTurretMounted, "ai.setTurretMounted(turretId)", 3, 3);
+
+ Con::addCommand("AIConnection", "setPilotDestination", cAISetPilotDestination, "ai.setPilotDestination(point3F [, maxSpeed])", 3, 4);
+ Con::addCommand("AIConnection", "setPilotAim", cAISetPilotAimLocation, "ai.setPilotAim(point3F)", 3, 3);
+ Con::addCommand("AIConnection", "setPilotPitchRange", cAISetPilotPitchRange, "ai.setPilotPitchRange(pitchUpMax, pitchDownMax, pitchIncMax)", 5, 5);
+
+ Con::addCommand("AIConnection", "getPathDistance", cAIGetPathDistance, "ai.getPathDistance(destination [, source])", 3, 4);
+ Con::addCommand("AIConnection", "pathDistRemaining", cAIPathDistRemaining, "ai.pathDistRemaining(maxDist)", 3, 3);
+ Con::addCommand("AIConnection", "getLOSLocation", cAIGetLOSLocation, "ai.getLOSLocation(targetPoint [, minDist, maxDist, nearPoint])", 3, 6);
+ Con::addCommand("AIConnection", "getHideLocation", cAIGetHideLocation, "ai.getHideLocation(targetPoint, range, nearPoint, hideLength)", 6, 6);
+
+ //Step script fuctions
+ Con::addCommand("AIConnection", "clearStep", cAIClearStep, "ai.clearStep()", 2, 2);
+ Con::addCommand("AIConnection", "getStepStatus", cAIStepGetStatus, "ai.getStepStatus()", 2, 2);
+ Con::addCommand("AIConnection", "getStepName", cAIStepGetName, "ai.getStepName()", 2, 2);
+
+ Con::addCommand("AIConnection", "stop", cAIStop, "ai.stop()", 2, 2);
+ Con::addCommand("AIConnection", "stepMove", cAIStepMove, "ai.stepMove(point3 [, tolerance, mode])", 3, 5);
+ Con::addCommand("AIConnection", "stepEscort", cAIStepEscort, "ai.stepEscort(client)", 3, 3);
+ Con::addCommand("AIConnection", "stepEngage", cAIStepEngage, "ai.stepEngage(client)", 3, 3);
+ Con::addCommand("AIConnection", "stepRangeObject", cAIStepRangeObject, "ai.stepRangeObject(object, weapon, minDist, maxDist [, nearLocation])", 6, 7);
+ Con::addCommand("AIConnection", "stepIdle", cAIStepIdle, "ai.stepIdle(point3)", 3, 3);
+ Con::addCommand("AIConnection", "stepJet", cAIStepJet, "ai.stepJet(toLoc)", 3, 3);
+ Con::addCommand("AIConnection", "setPath", cAISetPath, "ai.setPath([toLoc])", 2, 3);
+
+ Con::addCommand("AIConnection", "clearTasks", cAIClearTasks, "ai.clearTasks()", 2, 2);
+ Con::addCommand("AIConnection", "addTask", cAIAddTask, "ai.addTask(taskName)", 3, 3);
+ Con::addCommand("AIConnection", "removeTask", cAIRemoveTask, "ai.removeTask(id)", 3, 3);
+ Con::addCommand("AIConnection", "listTasks", cAIListTasks, "ai.listTasks()", 2, 2);
+ Con::addCommand("AIConnection", "getTaskId", cAIGetTaskId, "ai.getTaskId()", 2, 2);
+ Con::addCommand("AIConnection", "getTaskName", cAIGetTaskName, "ai.getTaskName()", 2, 2);
+ Con::addCommand("AIConnection", "getTaskTime", cAIGetTaskTime, "ai.getTaskTime()", 2, 2);
+
+ Con::addCommand("AIConnection", "missionCycleCleanup", cAIMissionCycleCleanup, "ai.missionCycleCleanup()", 2, 2);
+}
diff --git a/ai/aiDebug.cc b/ai/aiDebug.cc
new file mode 100644
index 0000000..7e8d313
--- /dev/null
+++ b/ai/aiDebug.cc
@@ -0,0 +1,246 @@
+//-----------------------------------------------------------------------------
+// V12 Engine
+//
+// Copyright (c) 2001 GarageGames.Com
+// Portions Copyright (c) 2001 by Sierra Online, Inc.
+//-----------------------------------------------------------------------------
+
+#include "ai/aiConnection.h"
+#include "ai/aiTask.h"
+#include "ai/aiStep.h"
+#include "game/vehicle.h"
+
+void AIConnection::aiDebugStuff()
+{
+ //DEBUG - setup
+ S32 lineNum = 16;
+ char idBuf[16];
+ dSprintf(idBuf, sizeof(idBuf), "%d", getId());
+
+ //DEBUG - check if we're debugging this client
+ if (Con::getIntVariable("$AIDebugClient") != getId())
+ return;
+
+ //make sure we have a valid control object
+ ShapeBase *ctrlObject = getControlObject();
+ if (! ctrlObject)
+ {
+ Con::executef(4, "aiDebugText", idBuf, "15", "CLIENT IS DEAD!");
+ return;
+ }
+
+ //get the control object
+ Player *myPlayer = NULL;
+ myPlayer = dynamic_cast(ctrlObject);
+
+ Vehicle *myVehicle = NULL;
+ myVehicle = dynamic_cast(ctrlObject);
+
+ //DEBUG - Clear text lines
+ char textBuf[256];
+ for (S32 i = lineNum; i < 64; i++)
+ Con::executef(4, "aiDebugText", idBuf, avar("%d", i), "");
+
+ //DEBUG - current task
+ if (mCurrentTask)
+ dSprintf(textBuf, sizeof(textBuf), "Current task: %d: %s %d", mCurrentTask->getId(), mCurrentTask->getName(), mCurrentTask->getWeight());
+ else
+ dSprintf(textBuf, sizeof(textBuf), "NO TASK!");
+ Con::executef(4, "aiDebugText", idBuf, avar("%d", lineNum++), textBuf);
+
+ //DEBUG - current step
+ if (mStep)
+ dSprintf(textBuf, sizeof(textBuf), "Current step: %s", mStep->getName());
+ else
+ dSprintf(textBuf, sizeof(textBuf), "NO STEP!");
+ Con::executef(4, "aiDebugText", idBuf, avar("%d", lineNum++), textBuf);
+
+ //find out if the control object is a player
+ if (myPlayer)
+ {
+ //Debug - Move Mode
+ switch (mMoveMode)
+ {
+ case ModeStop: dSprintf(textBuf, sizeof(textBuf), "Move Mode = %d: Stop", mMoveMode); break;
+ case ModeWalk: dSprintf(textBuf, sizeof(textBuf), "Move Mode = %d: Walk", mMoveMode); break;
+ case ModeGainHeight: dSprintf(textBuf, sizeof(textBuf), "Move Mode = %d: GainHeight", mMoveMode); break;
+ case ModeExpress: dSprintf(textBuf, sizeof(textBuf), "Move Mode = %d: Express", mMoveMode); break;
+ case ModeMountVehicle: dSprintf(textBuf, sizeof(textBuf), "Move Mode = %d: MountVehicle", mMoveMode); break;
+ case ModeStuck: dSprintf(textBuf, sizeof(textBuf), "Move Mode = %d: Stuck", mMoveMode); break;
+ default: dSprintf(textBuf, sizeof(textBuf), "Move Mode unknown"); break;
+ }
+ //if we're using the jet state machine, indicate this as well
+ if (mNavUsingJet)
+ dStrcpy(&textBuf[dStrlen(textBuf)], " USING JET STATE MACHINE");
+
+ Con::executef(4, "aiDebugText", idBuf, avar("%d", lineNum++), textBuf);
+
+ //DEBUG - distance to destination
+ dSprintf(textBuf, sizeof(textBuf), "Dist to node = %.3f", mDistToNode2D);
+ Con::executef(4, "aiDebugText", idBuf, avar("%d", lineNum++), textBuf);
+
+ //DEBUG - final destination
+ dSprintf(textBuf, sizeof(textBuf), "Destination is %.3f %.3f %.3f", mMoveDestination.x, mMoveDestination.y, mMoveDestination.z);
+ Con::executef(4, "aiDebugText", idBuf, avar("%d", lineNum++), textBuf);
+
+ //DEBUG - velocity
+ dSprintf(textBuf, sizeof(textBuf), "Velocity = %.3f", mVelocity.len());
+ Con::executef(4, "aiDebugText", idBuf, avar("%d", lineNum++), textBuf);
+
+ //DEBUG - energy
+ F32 jetEnergy = mPath.jetWillNeedEnergy(4);
+ dSprintf(textBuf, sizeof(textBuf), "Energy = %.3f, jet will require: %.3f", mEnergy, jetEnergy);
+ Con::executef(4, "aiDebugText", idBuf, avar("%d", lineNum++), textBuf);
+
+ //DEBUG - damage
+ dSprintf(textBuf, sizeof(textBuf), "Damage = %.3f", mDamage);
+ Con::executef(4, "aiDebugText", idBuf, avar("%d", lineNum++), textBuf);
+
+ //DEBUG - downhill
+ Con::executef(4, "aiDebugText", idBuf, avar("%d", lineNum++), mHeadingDownhill ? "heading DOWNHILL" : "heading UPHILL");
+
+ //DEBUG - outdoors
+ F32 freedomRadius;
+ if (mPath.locationIsOutdoors(mLocation, &freedomRadius))
+ dSprintf(textBuf, sizeof(textBuf), "OUTDOORS: %.3f", freedomRadius);
+ else
+ dSprintf(textBuf, sizeof(textBuf), "Indoors");
+ Con::executef(4, "aiDebugText", idBuf, avar("%d", lineNum++), textBuf);
+
+ //DEBUG - me/targ in water
+ dSprintf(textBuf, sizeof(textBuf), "%s | %s", mInWater ? "IN WATER" : "not in water", mTargInWater ? "TARGET IN WATER" : " targ not in water");
+ Con::executef(4, "aiDebugText", idBuf, avar("%d", lineNum++), textBuf);
+
+ //DEBUG - jetting
+ Con::executef(4, "aiDebugText", idBuf, avar("%d", lineNum++), mTriggers[JetTrigger] ? "JETTING" : "");
+
+ //DEBUG - is stuck
+ Con::executef(4, "aiDebugText", idBuf, avar("%d", lineNum++), mMoveMode == ModeStuck ? "STUCK!!!" : "");
+
+ //DEBUG - engage target
+ dSprintf(textBuf, sizeof(textBuf), "Engage Target: %d", bool(mEngageTarget) ? mEngageTarget->getId() : -1);
+ Con::executef(4, "aiDebugText", idBuf, avar("%d", lineNum++), textBuf);
+
+ //DEBUG - target object
+ dSprintf(textBuf, sizeof(textBuf), "Target Object: %d", bool(mTargetObject) ? mTargetObject->getId() : -1);
+ Con::executef(4, "aiDebugText", idBuf, avar("%d", lineNum++), textBuf);
+
+ //DEBUG - target object
+ dSprintf(textBuf, sizeof(textBuf), "%s %s", (mTargetInSight ? "TARG IN SIGHT" : "targ not in sight"),
+ (mTargetInRange ? "TARG IN RANGE" : "targ not in range"));
+ Con::executef(4, "aiDebugText", idBuf, avar("%d", lineNum++), textBuf);
+
+ //DEBUG - engage state
+ switch (mEngageState)
+ {
+ case ChooseWeapon: dSprintf(textBuf, sizeof(textBuf), "Engage State = %d: ChooseWeapon", mEngageState); break;
+ case OutOfRange: dSprintf(textBuf, sizeof(textBuf), "Engage State = %d: OutOfRange", mEngageState); break;
+ case ReloadWeapon: dSprintf(textBuf, sizeof(textBuf), "Engage State = %d: ReloadWeapon", mEngageState); break;
+ case FindTargetPoint: dSprintf(textBuf, sizeof(textBuf), "Engage State = %d: FindTargetPoint", mEngageState); break;
+ case AimAtTarget: dSprintf(textBuf, sizeof(textBuf), "Engage State = %d: AimAtTarget", mEngageState); break;
+ case FireWeapon: dSprintf(textBuf, sizeof(textBuf), "Engage State = %d: FireWeapon", mEngageState); break;
+ default: dSprintf(textBuf, sizeof(textBuf), "Engage State unknown"); break;
+ }
+ Con::executef(4, "aiDebugText", idBuf, avar("%d", lineNum++), textBuf);
+
+ //DEBUG - current weapon
+ dSprintf(textBuf, sizeof(textBuf), "Current weapon: %s", mProjectileName);
+ Con::executef(4, "aiDebugText", idBuf, avar("%d", lineNum++), textBuf);
+
+ //DEBUG - firing
+ Con::executef(4, "aiDebugText", idBuf, avar("%d", lineNum++), mTriggers[FireTrigger] ? "FIRING" : "");
+
+ //DEBUG - distance to target
+ if (bool(mTargetPlayer))
+ dSprintf(textBuf, sizeof(textBuf), "Dist to target = %.3f", mDistToTarg2D);
+ else if (bool(mTargetObject))
+ dSprintf(textBuf, sizeof(textBuf), "Dist to target = %.3f", mDistToObject2D);
+ else
+ dSprintf(textBuf, sizeof(textBuf), "Dist to target = NO TARGET");
+ Con::executef(4, "aiDebugText", idBuf, avar("%d", lineNum++), textBuf);
+
+ //DEBUG - clear lines
+ Con::executef(2, "aiDebugClearLines", idBuf);
+
+ //DEBUG - draw the projectile path
+ char startPt[64], endPt[64];
+ if (bool(mEnemyProjectile))
+ {
+ Point3F projLocation;
+ MatrixF const& projTransform = mEnemyProjectile->getTransform();
+ projTransform.getColumn(3, &projLocation);
+ dSprintf(startPt, sizeof(startPt), "%.3f %.3f %.3f", projLocation.x, projLocation.y, projLocation.z);
+ dSprintf(endPt, sizeof(endPt), "%.3f %.3f %.3f", mImpactLocation.x, mImpactLocation.y, mImpactLocation.z);
+ Con::executef(5, "aiDebugLine", idBuf, startPt, endPt, "1.0 0.0 0.0");
+ }
+
+ //Debug - draw the node location
+ dSprintf(startPt, sizeof(startPt), "%.3f %.3f %.3f", mLocation.x, mLocation.y, mLocation.z + 0.1f);
+ dSprintf(endPt, sizeof(endPt), "%.3f %.3f %.3f", mNodeLocation.x, mNodeLocation.y, mNodeLocation.z + 0.1f);
+ Con::executef(5, "aiDebugLine", idBuf, startPt, endPt, "1.0 0.0 1.0");
+
+ //DEBUG - draw the move location
+ dSprintf(startPt, sizeof(startPt), "%.3f %.3f %.3f", mLocation.x, mLocation.y, mLocation.z + 0.2f);
+ dSprintf(endPt, sizeof(endPt), "%.3f %.3f %.3f", mMoveLocation.x, mMoveLocation.y, mMoveLocation.z + 0.2f);
+ Con::executef(5, "aiDebugLine", idBuf, startPt, endPt, "0.0 1.0 1.0");
+
+ //Debug - draw the move destination
+ dSprintf(startPt, sizeof(startPt), "%.3f %.3f %.3f", mLocation.x, mLocation.y, mLocation.z);
+ dSprintf(endPt, sizeof(endPt), "%.3f %.3f %.3f", mMoveDestination.x, mMoveDestination.y, mMoveDestination.z);
+ Con::executef(5, "aiDebugLine", idBuf, startPt, endPt, "0.0 0.0 1.0");
+
+ //Debug - draw the aim location
+ dSprintf(startPt, sizeof(startPt), "%.3f %.3f %.3f", mMuzzlePosition.x, mMuzzlePosition.y, mMuzzlePosition.z);
+ dSprintf(endPt, sizeof(endPt), "%.3f %.3f %.3f", mAimLocation.x, mAimLocation.y, mAimLocation.z);
+ Con::executef(5, "aiDebugLine", idBuf, startPt, endPt, "1.0 0.0 0.0");
+ }
+
+ //debugging code for if the bot is piloting a vehicle
+ else if (myVehicle)
+ {
+ //Debug - destination
+ dSprintf(textBuf, sizeof(textBuf), "Destination: %.3f %.3f %.3f", mPilotDestination.x, mPilotDestination.y, mPilotDestination.z);
+ Con::executef(4, "aiDebugText", idBuf, avar("%d", lineNum++), textBuf);
+
+ //Debug - distance to dest
+ dSprintf(textBuf, sizeof(textBuf), "Distance to dest2D: %.3f", mPilotDistToDest2D);
+ Con::executef(4, "aiDebugText", idBuf, avar("%d", lineNum++), textBuf);
+
+ //Debug - aim location
+ dSprintf(textBuf, sizeof(textBuf), "Destination: %.3f %.3f %.3f", mPilotAimLocation.x, mPilotAimLocation.y, mPilotAimLocation.z);
+ Con::executef(4, "aiDebugText", idBuf, avar("%d", lineNum++), textBuf);
+
+ //Debug - current speed/thrust
+ dSprintf(textBuf, sizeof(textBuf), "Velocity: %.3f, Thrust: %.3f", mPilotCurVelocity, mPilotCurThrust);
+ Con::executef(4, "aiDebugText", idBuf, avar("%d", lineNum++), textBuf);
+
+ //Debug - current pitch, desired pitch, pitch inc
+ dSprintf(textBuf, sizeof(textBuf), "current pitch: %.3f, desired pitch: %.3f, pitch inc: %.3f", mCurrentPitch, mDesiredPitch, mPitchIncrement);
+ Con::executef(4, "aiDebugText", idBuf, avar("%d", lineNum++), textBuf);
+
+ //Debug - current pitch, desired pitch, pitch inc
+ dSprintf(textBuf, sizeof(textBuf), "pitchUpMax: %.3f, pitchDownMax: %.3f", mPitchUpMax, mPitchDownMax);
+ Con::executef(4, "aiDebugText", idBuf, avar("%d", lineNum++), textBuf);
+
+ //Debug - current yaw, yaw diff
+ dSprintf(textBuf, sizeof(textBuf), "curYaw: %.3f", mCurrentYaw);
+ Con::executef(4, "aiDebugText", idBuf, avar("%d", lineNum++), textBuf);
+
+ //DEBUG - jetting
+ Con::executef(4, "aiDebugText", idBuf, avar("%d", lineNum++), mTriggers[JetTrigger] ? "JETTING" : "");
+
+ //DEBUG - clear lines
+ char startPt[64], endPt[64];
+ Con::executef(2, "aiDebugClearLines", idBuf);
+
+ //Debug - draw the vehicle destination
+ dSprintf(startPt, sizeof(startPt), "%.3f %.3f %.3f", mVehicleLocation.x, mVehicleLocation.y, mVehicleLocation.z);
+ dSprintf(endPt, sizeof(endPt), "%.3f %.3f %.3f", mPilotDestination.x, mPilotDestination.y, mPilotDestination.z);
+ Con::executef(5, "aiDebugLine", idBuf, startPt, endPt, "0.0 0.0 1.0");
+
+ //Debug - draw the vehicle aim location
+ dSprintf(startPt, sizeof(startPt), "%.3f %.3f %.3f", mVehicleLocation.x, mVehicleLocation.y, mVehicleLocation.z + 0.1f);
+ dSprintf(endPt, sizeof(endPt), "%.3f %.3f %.3f", mPilotAimLocation.x, mPilotAimLocation.y, mPilotAimLocation.z + 0.1f);
+ Con::executef(5, "aiDebugLine", idBuf, startPt, endPt, "1.0 0.0 0.0");
+ }
+}
diff --git a/ai/aiMath.cc b/ai/aiMath.cc
new file mode 100644
index 0000000..f45b95f
--- /dev/null
+++ b/ai/aiMath.cc
@@ -0,0 +1,341 @@
+//-----------------------------------------------------------------------------
+// V12 Engine
+//
+// Copyright (c) 2001 GarageGames.Com
+// Portions Copyright (c) 2001 by Sierra Online, Inc.
+//-----------------------------------------------------------------------------
+
+#include "ai/aiMath.h"
+#include "ai/tBinHeap.h"
+#include "core/realComp.h"
+
+//-------------------------------------------------------------------------------------
+
+F32 solveForZ(const PlaneF& plane, const Point3F& point)
+{
+ AssertFatal(mFabs(plane.z) > 0.0001, "solveForZ() expects non-vertical plane");
+ return (-plane.d - plane.x * point.x - plane.y * point.y) / plane.z;
+}
+
+//-------------------------------------------------------------------------------------
+
+// Simple "iterator", used as in-
+// for (mArea.start(step); mArea.pointInRect(step); mArea.step(step))
+// ...
+bool GridArea::start(Point2I& steppingPoint) const
+{
+ if (isValidRect())
+ {
+ steppingPoint = point;
+ return true;
+ }
+ return false;
+}
+bool GridArea::step(Point2I& steppingPoint) const
+{
+ if (++steppingPoint.x >= (point.x + extent.x))
+ {
+ steppingPoint.x = point.x;
+ if (++steppingPoint.y >= (point.y + extent.y))
+ return false;
+ }
+ return true;
+}
+
+//-------------------------------------------------------------------------------------
+
+GridVisitor::GridVisitor(const GridArea & area)
+ : mArea(area.point, area.extent)
+{
+ mPostCheck = mPreCheck = true;
+}
+
+// Default versions of virtuals. The Before and After callbacks just remove themselves:
+bool GridVisitor::beforeDivide(const GridArea&,S32) {return !(mPreCheck = false);}
+bool GridVisitor::atLevelZero(const GridArea&) {return true; }
+bool GridVisitor::afterDivide(const GridArea&,S32,bool) {return !(mPostCheck= false);}
+
+// Recursive region traversal.
+// R is a box of width 2^L, and aligned on like boundary (L low bits all zero)
+bool GridVisitor::recurse(GridArea R, S32 L)
+{
+ bool success = true;
+
+ if (! R.overlaps(mArea)) {
+ success = false;
+ }
+ else if (L == 0) {
+ success = atLevelZero(R);
+ }
+ else if (mPreCheck && mArea.contains(R) && !beforeDivide(R, L)) { // early out?
+ success = false;
+ }
+ else {
+ // Made it to sub-division!
+ S32 half = 1 << (L - 1);
+ for (S32 y = half; y >= 0; y -= half)
+ for (S32 x = half; x >= 0; x -= half)
+ if (!recurse(GridArea(R.point.x + x, R.point.y + y, half, half), L - 1))
+ success = false;
+
+ // Post order stuff-
+ if (mPostCheck && mArea.contains(R) && !afterDivide(R, L, success))
+ success = false;
+ }
+ return success;
+}
+
+bool GridVisitor::traverse()
+{
+ S32 level = 1;
+ GridArea powerTwoRect (-1, -1, 2, 2);
+
+ // Find the power-of-two-sized rect that encloses user-supplied grid.
+ while (!powerTwoRect.contains(mArea))
+ if (++level < 31) {
+ powerTwoRect.point *= 2;
+ powerTwoRect.extent *= 2;
+ }
+ else
+ return false;
+
+ // We have our rect and starting level, perform the recursive traversal:
+ return recurse(powerTwoRect, level);
+}
+
+
+//-------------------------------------------------------------------------------------
+// Find distance of point from the segment. If the point's projection onto
+// line isn't within the segment, then take distance from the endpoints.
+//
+// This also leaves the closest point set in soln.
+//
+F32 LineSegment::distance(const Point3F & p)
+{
+ VectorF vec1 = b - a, vec2 = p - a;
+ F32 len = vec1.len(), dist = vec2.len();
+
+ soln = a; // good starting default.
+
+ // First check for case where A and B are basically same (avoid overflow) -
+ // can return distance from either point.
+ if( len > 0.001 )
+ {
+ // normalize vector from A to B and get projection of AP along it.
+ F32 dot = mDot( vec1 *= (1/len), vec2 );
+ if(dot >= 0)
+ {
+ if(dot <= len)
+ {
+ F32 sideSquared = (dist * dist) - (dot * dot);
+ if (sideSquared <= 0) // slight imprecision led to NaN
+ dist = sideSquared = 0;
+ else
+ dist = mSqrt(sideSquared);
+ soln += (vec1 * dot);
+ }
+ else
+ {
+ soln = b;
+ dist = (b - p).len();
+ }
+ }
+ }
+
+ return dist;
+}
+
+// Special check which does different math in 3D and 2D. If the test is passed
+// in 3d, then do a 2d check.
+bool LineSegment::botDistCheck(const Point3F& p, F32 dist3, F32 dist2)
+{
+ F32 d3 = distance(p);
+ if (d3 < dist3)
+ {
+ Point2F proj2d(soln.x - p.x, soln.y - p.y);
+ F32 d2 = proj2d.lenSquared();
+ dist2 *= dist2;
+ return (d2 < dist2);
+ }
+ return false;
+}
+
+
+//-------------------------------------------------------------------------------------
+// Get a list of grid offsets that "spirals" out in order of closeness.
+// This is used by the navigation (preprocess) code to find nearest
+// obstructed grid location, and it uses this for its search order.
+
+S32 getGridSpiral(Vector & spiral, Vector & dists, S32 radius)
+{
+ S32 x, y, i;
+ Vector pool;
+ BinHeap heap;
+
+ // Build all the points that we'll want to consider. We're going to
+ // leave off those points that are outside of circle.
+ for( y = -radius; y <= radius; y++ )
+ for( x = -radius; x <= radius; x++ )
+ {
+ F32 dist = Point2F( F32(x), F32(y) ).len();
+ if( dist <= F32(radius) + 0.00001 )
+ {
+ pool.push_back( Point2I(x,y) );
+ heap.insert( -dist ); // negate for sort order
+ }
+ }
+
+ // Get the elements out in order.
+ heap.buildHeap();
+ while( (i = heap.headIndex()) >= 0 )
+ {
+ spiral.push_back( pool[i] );
+ dists.push_back( -heap[i] ); // get the (sign-restored) distance
+ heap.removeHead();
+ }
+
+ return spiral.size();
+}
+
+
+//-----------------------------------------------------------------
+// Line Stepper class.
+
+void LineStepper::init(const Point3F & a, const Point3F & b)
+{
+ solution = A = a, B = b;
+ total = (dir1 = B - A).len();
+ soFar = advance = 0.0f;
+
+ if( total > 0.00001 )
+ {
+ dir1 *= (1 / total);
+ }
+ else
+ {
+ total = 0.0f;
+ dir1.set(0,0,0);
+ }
+}
+
+//
+// Finds the intersection of the line with the sphere on the way out. If there
+// are two intersections, and we're starting outside, this should find the
+// second.
+//
+F32 LineStepper::getOutboundIntersection(const SphereF & S)
+{
+ // get projection of sphere center along our line
+ F32 project = mDot( S.center - A, dir1 );
+
+ // find point nearest to center of sphere
+ Point3F nearPoint = A + (dir1 * project);
+
+ // if this isn't in the sphere, then there's no intersection. negative return flags this.
+ if( ! S.isContained(nearPoint) )
+ return -1.0f;
+
+ // there is an intersection, figure how far from nearest point it is
+ F32 length = mSqrt( S.radius*S.radius - (nearPoint-S.center).lenSquared() );
+
+ // find the solution point and advance amount (which is return value)
+ solution = nearPoint + dir1 * length;
+ return( advance = length + project );
+}
+
+const Point3F& LineStepper::advanceToSolution()
+{
+ if( advance != 0.0f )
+ {
+ soFar += advance;
+ A = solution;
+ advance = 0.0f;
+ }
+ return solution;
+}
+
+//---------------------------------------------------------------
+// Finds the center of Mass of a CONVEX polygon.
+// Verts should contain an array of Point2F's, in order,
+// and of size n, that will represent the polygon.
+//---------------------------------------------------------------
+bool polygonCM(S32 n, Point2F *verts, Point2F *CM)
+{
+ if(!verts || n < 3)
+ return false;
+
+ F32 area, area2 = 0.0;
+ Point2F cent;
+ CM->x = 0;
+ CM->y = 0;
+
+ U32 i;
+ for(i = 1; i < (n-1); i++)
+ {
+ triCenter(verts[0], verts[i], verts[i+1], cent);
+ area = triArea(verts[0], verts[i], verts[i+1]);
+ CM->x += area * cent.x;
+ CM->y += area * cent.y;
+ area2 += area;
+ }
+
+ CM->x /= 3 * area2;
+ CM->y /= 3 * area2;
+ return true;
+}
+
+//-------------------------------------------------------------------------------------
+
+// Lifted from tools/morian/CSGBrush.cc. This converts to doubles for insurance, which
+// shouldn't pose a speed problem since this is mainly used in graph preprocess.
+bool intersectPlanes(const PlaneF& p, const PlaneF& q, const PlaneF& r, Point3F* pOut)
+{
+ Point3D p1(p.x, p.y, p.z);
+ Point3D p2(q.x, q.y, q.z);
+ Point3D p3(r.x, r.y, r.z);
+ F64 d1 = p.d, d2 = q.d, d3 = r.d;
+
+ F64 bc = (p2.y * p3.z) - (p3.y * p2.z);
+ F64 ac = (p2.x * p3.z) - (p3.x * p2.z);
+ F64 ab = (p2.x * p3.y) - (p3.x * p2.y);
+ F64 det = (p1.x * bc) - (p1.y * ac) + (p1.z * ab);
+
+ // Parallel planes
+ if (mFabsD(det) < 1e-5)
+ return false;
+
+ F64 dc = (d2 * p3.z) - (d3 * p2.z);
+ F64 db = (d2 * p3.y) - (d3 * p2.y);
+ F64 ad = (d3 * p2.x) - (d2 * p3.x);
+ F64 detInv = 1.0 / det;
+
+ pOut->x = ((p1.y * dc) - (d1 * bc) - (p1.z * db)) * detInv;
+ pOut->y = ((d1 * ac) - (p1.x * dc) - (p1.z * ad)) * detInv;
+ pOut->z = ((p1.y * ad) + (p1.x * db) - (d1 * ab)) * detInv;
+
+ return true;
+}
+
+bool findCrossVector(const Point3F &v1, const Point3F &v2, Point3F *result)
+{
+ if (v1.isZero() || v2.isZero() || (result == NULL))
+ return false;
+
+ Point3F v1Norm = v1, v2Norm = v2;
+ v1Norm.normalize();
+ v2Norm.normalize();
+
+ if ((! isZero(1.0f - v1Norm.len())) || (! isZero(1.0f - v2Norm.len())))
+ return false;
+
+ //make sure the vectors are non-colinear
+ F32 dot = mDot(v1Norm, v2Norm);
+ if (dot > 0.999f || dot < -0.999f)
+ return false;
+
+ //find the cross product, and normalize the result
+ mCross(v1Norm, v2Norm, result);
+ result->normalize();
+
+ return true;
+}
diff --git a/ai/aiMath.h b/ai/aiMath.h
new file mode 100644
index 0000000..b0108d5
--- /dev/null
+++ b/ai/aiMath.h
@@ -0,0 +1,349 @@
+//-----------------------------------------------------------------------------
+// V12 Engine
+//
+// Copyright (c) 2001 GarageGames.Com
+// Portions Copyright (c) 2001 by Sierra Online, Inc.
+//-----------------------------------------------------------------------------
+
+#ifndef _GRAPHMATH_H_
+#define _GRAPHMATH_H_
+
+#ifndef _TVECTOR_H_
+#include "core/tVector.h"
+#endif
+#ifndef _MSPHERE_H_
+#include "math/mSphere.h"
+#endif
+#ifndef _MATHIO_H_
+#include "math/mathIO.h"
+#endif
+
+//-------------------------------------------------------------------------------------
+// One-line convenience functions:
+
+inline bool validArrayIndex(S32 index, S32 arrayLength)
+{
+ return U32(index) < U32(arrayLength); // (Unsigned compare handles < 0 case too)
+}
+
+inline F32 triArea(const Point2F& a, const Point2F& b, const Point2F& c)
+{
+ return mFabs((b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y));
+}
+
+inline void triCenter(const Point2F& p1, const Point2F& p2, const Point2F& p3, Point2F &c)
+{
+ c.x = p1.x + p2.x + p3.x;
+ c.y = p1.y + p2.y + p3.y;
+}
+
+// prototype for finding the CM(center of mass) of a convex poly
+bool polygonCM(S32 n, Point2F *verts, Point2F *CM);
+
+// Find point at intersection of three planes if such exists (returns true if so).
+bool intersectPlanes(const PlaneF& p, const PlaneF& q, const PlaneF& r, Point3F* pOut);
+
+// Lookup into NxN table in which one entry is stored for each pair of unique indices.
+inline S32 triangularTableIndex(S32 i1, S32 i2) {
+ if (i1 > i2)
+ return (((i1 - 1) * i1) >> 1) + i2;
+ else
+ return (((i2 - 1) * i2) >> 1) + i1;
+}
+
+//-------------------------------------------------------------------------------------
+
+// Where vertical line through point hits plane (whose normal.z != 0):
+F32 solveForZ(const PlaneF& plane, const Point3F& point);
+
+// Cross product, but with a check for bad input (parallel vectors):
+bool findCrossVector(const Point3F &v1, const Point3F &v2, Point3F *result);
+
+//-------------------------------------------------------------------------------------
+// Encapsulate some common operations on the grid rectangles
+class GridArea : public RectI
+{
+ public:
+ GridArea() { }
+ GridArea(const Point2I& pt,const Point2I& ext) : RectI(pt,ext) {}
+ GridArea(S32 L, S32 T, S32 W, S32 H) : RectI(L,T,W,H) {}
+ GridArea(const GridArea & g) : RectI(g.point,g.extent) {}
+
+ // grid to index, and vice versa:
+ S32 getIndex(Point2I pos) const;
+ Point2I getPos(S32 index) const;
+
+ // step through grid in index order (Y outer loop):
+ bool start(Point2I &p) const;
+ bool step(Point2I &p) const;
+};
+
+inline S32 GridArea::getIndex(Point2I p) const
+{
+ p -= point;
+ if( validArrayIndex(p.x, extent.x) )
+ if( validArrayIndex(p.y, extent.y) )
+ return p.y * extent.x + p.x;
+ return -1;
+}
+
+inline Point2I GridArea::getPos(S32 index) const
+{
+ Point2I P;
+ P.x = point.x + index % extent.x;
+ P.y = point.y + index / extent.x;
+ return P;
+}
+
+//-------------------------------------------------------------------------------------
+// Linear range mapping.
+
+// Get value that is pct way between first and last. Note this is probably a better
+// way of getting midpoints than (A+B)*.5 due to rounding.
+template T scaleBetween(T first, T last, F32 pct)
+{
+ return first + (pct * (last - first));
+}
+
+// Get percent of way of value between min and max, clamping return to range [0,1]
+template F32 getPercentBetween(T value, T min, T max)
+{
+ if(mFabs((max - min)) < 0.0001)
+ return 0.0f;
+ else if(min < max){
+ if (value <= min) return 0.0f;
+ else if (value >= max) return 1.0f;
+ }
+ else if (min > max){
+ if (value >= min) return 0.0f;
+ else if (value <= max) return 1.0f;
+ }
+ return F32((value - min) / (max - min));
+}
+
+// Map value from domain into the given range, capping on ends-
+template T mapValueLinear(T value, T dmin, T dmax, T rmin, T rmax)
+{
+ return scaleBetween(rmin, rmax, getPercentBetween(value, dmin, dmax));
+}
+
+template T mapValueQuadratic(T value, T dmin, T dmax, T rmin, T rmax)
+{
+ F32 pct = getPercentBetween(value, dmin, dmax);
+ return scaleBetween(rmin, rmax, pct * pct);
+}
+
+template T mapValueSqrt(T value, T dmin, T dmax, T rmin, T rmax)
+{
+ F32 pct = getPercentBetween(value, dmin, dmax);
+ return scaleBetween(rmin, rmax, mSqrt(pct));
+}
+
+//-------------------------------------------------------------------------------------
+// An object to do a quad-tree traversal of a grid region.
+
+class GridVisitor
+{
+ protected:
+ bool mPreCheck;
+ bool mPostCheck;
+
+ bool recurse(GridArea rect, S32 level);
+
+ public:
+ const GridArea mArea;
+ GridVisitor(const GridArea & area);
+
+ // virtuals - how you get visited:
+ virtual bool beforeDivide(const GridArea& R, S32 level);
+ virtual bool atLevelZero(const GridArea& R);
+ virtual bool afterDivide(const GridArea& R, S32 level, bool success);
+
+ // call the traversal:
+ bool traverse();
+};
+
+//-------------------------------------------------------------------------------------
+// Return if the two points are within the given threshold distance.
+
+template
+bool within(PointType p1, const PointType & p2, DistType thresh)
+{
+ return (p1 -= p2).lenSquared() <= (thresh * thresh);
+}
+
+inline bool within_2D(const Point3F & A, const Point3F & B, F32 D)
+{
+ Point2F A2d( A.x, A.y );
+ Point2F B2d( B.x, B.y );
+ return within( A2d, B2d, D );
+}
+
+//-------------------------------------------------------------------------------------
+// Get a list of grid offsets that "spirals" out in order of closeness.
+
+S32 getGridSpiral(Vector & spiral, Vector & dists, S32 radius);
+
+//-------------------------------------------------------------------------------------
+// Given a point, used to find closest distance/point on segment.
+
+class LineSegment
+{
+ Point3F a, b;
+ Point3F soln;
+ public:
+ LineSegment(const Point3F& _a, const Point3F& _b) {soln=a =_a;b =_b;}
+ LineSegment() {soln=a=b=Point3F(0,0,0);}
+ public:
+ void set(const Point3F& _a, const Point3F& _b) {soln=a =_a;b =_b;}
+ F32 distance(const Point3F& p);
+ bool botDistCheck(const Point3F& p, F32 d3, F32 d2);
+ Point3F solution() {return soln;}
+ Point3F getEnd(bool which) {return which ? b : a;}
+};
+
+//-------------------------------------------------------------------------------------
+
+class LineStepper
+{
+ Point3F A, B;
+ Point3F dir1; // unit direction vector from A to B.
+ F32 total; // total dist from A to B.
+ F32 soFar; // how far we've come.
+
+ F32 advance; // solutions to queries are kept until the user
+ Point3F solution; // tells us to "officially" advance.
+
+ public:
+ LineStepper(const Point3F & a, const Point3F & b) { init(a,b); }
+
+ const Point3F & getSolution() { return solution; }
+ F32 distSoFar() { return soFar; }
+ F32 totalDist() { return total; }
+ F32 remainingDist() { return total-soFar; }
+
+ // Methods that actually do stuff:
+ void init(const Point3F & a, const Point3F & b);
+ F32 getOutboundIntersection(const SphereF & s);
+ const Point3F & advanceToSolution();
+};
+
+//-------------------------------------------------------------------------------------
+// reverse elements in place
+
+template Vector & reverseVec( Vector & vector )
+{
+ for(S32 halfWay = (vector.size() >> 1); halfWay; /* dec'd in loop */ )
+ {
+ T & farOne = * (vector.end() - halfWay--);
+ T & nearOne = * (vector.begin() + halfWay);
+ T tmp=farOne; farOne=nearOne; nearOne=tmp; // swap
+ }
+ return vector;
+}
+
+//--------------------------------------------------------------------------
+// Read/Write a vector of items, using T.read(stream), T.write(stream).
+// Skip function reads same amount as readVector1(), bypassing data.
+
+template bool readVector1(Stream & stream, Vector & vec)
+{
+ S32 num, i;
+ bool Ok = stream.read( & num );
+ for( i = 0, vec.setSize( num ); i < num && Ok; i++ )
+ Ok = vec[i].read( stream );
+ return Ok;
+}
+template bool writeVector1(Stream & stream, const Vector & vec)
+{
+ bool Ok = stream.write( vec.size() );
+ for( U32 i = 0; i < vec.size() && Ok; i++ )
+ Ok = vec[i].write( stream );
+ return Ok;
+}
+
+//-------------------------------------------------------------------------------------
+// Read/Write a vector of items, using stream.read(T), stream.write(T).
+
+template bool readVector2(Stream & stream, Vector & vec)
+{
+ S32 num, i;
+ bool Ok = stream.read( & num );
+ for( i = 0, vec.setSize( num ); i < num && Ok; i++ )
+ Ok = stream.read(&vec[i]);
+ return Ok;
+}
+template bool writeVector2(Stream & stream, const Vector & vec)
+{
+ bool Ok = stream.write( vec.size() );
+ for( S32 i = 0; i < vec.size() && Ok; i++ )
+ Ok = stream.write(vec[i]);
+ return Ok;
+}
+
+//-------------------------------------------------------------------------------------
+// Functions to read / write a vector of items that define mathRead() / mathWrite().
+
+template bool mathReadVector(Vector& vec, Stream& stream)
+{
+ S32 num, i;
+ bool Ok = stream.read(&num);
+ for (i = 0, vec.setSize(num); i < num && Ok; i++ )
+ Ok = mathRead(stream, &vec[i]);
+ return Ok;
+}
+
+template bool mathWriteVector(const Vector& vec, Stream& stream)
+{
+ bool Ok = stream.write(vec.size());
+ for (S32 i = 0; i < vec.size() && Ok; i++)
+ Ok = mathWrite(stream, vec[i]);
+ return Ok;
+}
+
+//-------------------------------------------------------------------------------------
+// Perform setSize() and force construction of all the elements. Done to address some
+// difficulty experienced in getting virtual function table pointer constructed.
+
+template
+void setSizeAndConstruct(Vector & vector, S32 size)
+{
+ vector.setSize(size);
+ T * tList = new T [size];
+ S32 memSize = size * sizeof(T);
+ dMemcpy(vector.address(), tList, memSize);
+ delete [] tList;
+}
+
+template
+void destructAndClear(Vector & vector)
+{
+ for (S32 i = vector.size() - 1; i >= 0; i--)
+ vector[i].~T();
+ vector.clear();
+}
+
+template
+void setSizeAndClear(Vector & vector, S32 size, S32 value=0)
+{
+ vector.setSize( size );
+ dMemset(vector.address(), value, vector.memSize());
+}
+
+//--------------------------------------------------------------------------
+// Read a vector with construction
+
+template bool constructVector(Stream & stream, Vector & vec)
+{
+ S32 num, i;
+ bool Ok = stream.read(&num);
+ if(num && Ok)
+ {
+ setSizeAndConstruct(vec, num);
+ for (i = 0; i < num && Ok; i++)
+ Ok = vec[i].read(stream);
+ }
+ return Ok;
+}
+
+#endif
diff --git a/ai/aiNavJetting.cc b/ai/aiNavJetting.cc
new file mode 100644
index 0000000..0c8c8fb
--- /dev/null
+++ b/ai/aiNavJetting.cc
@@ -0,0 +1,485 @@
+//-----------------------------------------------------------------------------
+// V12 Engine
+//
+// Copyright (c) 2001 GarageGames.Com
+// Portions Copyright (c) 2001 by Sierra Online, Inc.
+//-----------------------------------------------------------------------------
+
+#include "ai/aiConnection.h"
+#include "ai/aiNavJetting.h"
+#include "ai/graphLOS.h"
+
+//-------------------------------------------------------------------------------------
+
+#define GravityConstant 20.0
+#define AmountInFront 0.37
+#define FastVelocity 10.0
+#define PullInPercent 0.63
+#define HitPointThresh 1.2
+#define JumpWaitCount 3
+#define SeekAboveChute 17.0f
+
+//-------------------------------------------------------------------------------------
+
+bool AIJetting::init(const Point3F& dest, bool intoMount, NavJetting * jetInfo)
+{
+ mSeekDest = dest;
+ mFirstTime = true;
+ mWillBonk = false;
+ mLaunchSpeed = 0.1;
+ mIntoMount = intoMount;
+ mStatus = AIJetWorking;
+ mJetInfo = jetInfo;
+ return true;
+}
+
+//-------------------------------------------------------------------------------------
+// Run LOS to see if we can safely jump to our destination.
+
+static const U32 scBonkMask = InteriorObjectType|StaticShapeObjectType
+ |StaticObjectType|TerrainObjectType;
+
+// We don't worry about this if we're jetting up more than 2 meters (in that case
+// there shouldn't be a chance of bonking). Also, we don't want to suppress
+// take-off jump for long hops (these edges should already be well checked). Mainly
+// this check is for those chute connections which had the bonk check suppressed...
+bool AIJetting::willBonk(Point3F src, Point3F dst)
+{
+ if (dst.z - src.z < 2.0)
+ {
+ Point2F vec2D(src.x-dst.x, src.y-dst.y);
+ if (vec2D.lenSquared() < (LiberalBonkXY * LiberalBonkXY))
+ {
+ src.z = dst.z = (getMax(src.z, dst.z) + 3.7);
+
+ Loser los(scBonkMask);
+
+ if (!los.haveLOS(src, dst))
+ return false; // Disable temporarily...
+ }
+ }
+ return false;
+}
+
+//-------------------------------------------------------------------------------------
+// Called whenever we move the state along.
+
+void AIJetting::newState(S16 state, S16 counter /*=0*/)
+{
+ mCounter = counter;
+ mState = state;
+}
+
+//-------------------------------------------------------------------------------------
+
+// Get distance from landing "wall" - with (dist < 0) meaning we're that much beyond it
+F32 AIJetting::distFromWall(const Point3F& loc)
+{
+ VectorF vecToWall = (mWallPoint - loc);
+ vecToWall.z = 0;
+ return mDot(vecToWall, mWallNormal);
+}
+
+// Assuming we're jetting up - see if it looks like the top of a chute up there.
+static bool upChute(const Point3F& from, F32 top, const VectorF& normal, Point3F& soln)
+{
+ RayInfo coll;
+ Point3F to(from.x, from.y, top + 20.0f);
+
+ if (gServerContainer.castRay(from, to, InteriorObjectType, &coll)) {
+ if (mDot(normal, coll.normal) > 0.05) {
+ coll.normal.z = 0;
+ coll.normal.normalize();
+ if (mDot(normal, coll.normal) > 0.9) { // ~ 25 deg
+ soln = coll.point;
+ // return true;
+ return false; // this has problems with larger chutes... oops.
+ }
+ }
+ }
+ return false;
+}
+
+bool AIJetting::figureLandingDist(AIConnection* ai, F32& dist)
+{
+ F32 A = -(GravityConstant * 0.5);
+ F32 B = ai->mVelocity.z;
+ F32 C = (ai->mLocation.z - mLandPoint.z);
+ F32 solutions[2];
+ U32 N = mSolveQuadratic(A, B, C, solutions);
+
+ if (N > 0)
+ {
+ // use the larger time solution (first will be negative, or coming up on soln)
+ F32 T = solutions[N-1];
+
+ // see where this will put us in XY -
+ dist = T * ai->mVelocity2D.len();
+ return true;
+ }
+ return false;
+}
+
+// Do first time setup, plus handle other processing on each frame.
+bool AIJetting::firstTimeStuff(AIConnection* ai, Player * player)
+{
+ if (mFirstTime)
+ {
+ mFirstTime = false;
+ mCanInterupt = true;
+ mWillBonk = false;
+ mLandPoint = mSeekDest;
+ mWallNormal = (mSeekDest - ai->mLocation);
+ mWallNormal.z = 0;
+ if ((mTotal2D = mWallNormal.len()) < GraphJetFailXY) {
+ mStatus = AIJetFail;
+ return false;
+ }
+
+ F32 zDiff = (mSeekDest.z - ai->mLocation.z);
+
+ mSlope = (zDiff / mTotal2D);
+
+ // Set our desired launch speed based on slope. Zero slope -> full speed,
+ // Steep slope -> Zero speed. Maps negative slopes to full speed.
+ mLaunchSpeed = mapValueQuadratic(mSlope, 1.3f, 0.0f, 0.0f, 1.0f);
+
+ // Wall normal points along our path in XY plane.
+ mWallNormal /= mTotal2D;
+
+ mUpChute = (mJetInfo && mJetInfo->mChuteUp);
+
+ // our dest will be a unit or so beyond for sake of aiming
+ mWallPoint = mLandPoint;
+ mSeekDest += (mWallNormal * 1.1);
+
+ // Hop over walls-
+ if (mJetInfo)
+ mSeekDest.z += mJetInfo->mHopOver;
+
+ newState(AssureClear);
+ }
+
+ // Variable that is set when they should look where their heading for right effect-
+ mShouldAim = false;
+
+ if (mIntoMount && player->isMounted()) {
+ mStatus = AIJetSuccess;
+ return false;
+ }
+
+ return true;
+}
+
+//-------------------------------------------------------------------------------------
+
+// Local function - call it when you should aim - bool variable is always cleared
+// unless this is called.
+void AIJetting::setAim(const Point3F& /*always mSeekDest now*/)
+{
+ mShouldAim = true;
+}
+
+// Public function - find out if should and where at.
+bool AIJetting::shouldAimAt(Point3F& atWhere)
+{
+ if (mShouldAim)
+ atWhere = mSeekDest;
+ return mShouldAim;
+}
+
+//-------------------------------------------------------------------------------------
+// We may still need to nudge a little bit to assure we have clearance (the generated
+// jetting edges need to sometimes hug a bit close (else some makeable connections get
+// get thrown out) so this is how we handle the occasional problem).
+
+static const U32 sLOSMask = InteriorObjectType | StaticShapeObjectType |
+ StaticObjectType | TerrainObjectType;
+static const F32 sClearDistance = 1.4f;
+
+bool AIJetting::assureClear(AIConnection* ai, Player* )
+{
+ // Get loc a little bit above the feet-
+ Point3F botLoc(ai->mLocation.x, ai->mLocation.y, ai->mLocation.z + 0.2);
+
+ if (botLoc.z < mSeekDest.z - 2.0)
+ {
+ // This shouldn't go on very long, just need a nudge if anything...
+ if (++mCounter < 32)
+ {
+ Point3F vec = botLoc;
+ (vec -= mSeekDest).z = 0.0f;
+
+ // Usual checks just in case....
+ F32 len = vec.len();
+ if (len > 0.1)
+ {
+ Loser loser(sLOSMask);
+ Point3F clearLoc(botLoc.x, botLoc.y, mSeekDest.z);
+
+ // Compute vector to come in by-
+ vec *= (sClearDistance / len);
+ clearLoc -= vec;
+
+ if (!loser.haveLOS(botLoc, clearLoc))
+ {
+ // Seek away, our vec contains which way to go...
+ ai->setMoveLocation(botLoc += vec);
+ ai->setMoveSpeed(0.6);
+ return false;
+ }
+ }
+ }
+ }
+
+ mWillBonk = willBonk(ai->mLocation, mSeekDest);
+
+ newState(AwaitEnergy);
+ return false;
+}
+
+//-------------------------------------------------------------------------------------
+
+// Wait for amount of energy we think we need.
+bool AIJetting::awaitEnergy(AIConnection* ai, Player* player)
+{
+ ai->setMoveLocation(mSeekDest);
+ if (ai->mVelocity.lenSquared() < 0.2)
+ {
+ if (player->getEnergyValue() > 0.99)
+ {
+ newState(PrepareToJump);
+ }
+ else
+ {
+ // Run the energy calculation every frame. We need to use the same methods
+ // that the graph uses to decide that a given hop is makeable with a certain
+ // amount of energy ability configuration.
+ F32 ratings[2];
+ JetManager::Ability ability;
+
+// Tribes player jetting was remove from the player class.
+#if 0
+ ability.dur = player->getJetAbility(ability.acc, ability.dur, ability.v0);
+#else
+ ability.acc = 0;
+ ability.dur = 0;
+ ability.v0 = 0;
+#endif
+ gNavGraph->jetManager().calcJetRatings(ratings, ability);
+
+ F32 jetD = gNavGraph->jetManager().jetDistance(ai->mLocation, mSeekDest);
+
+ if (jetD < ratings[!mWillBonk && player->canJump()])
+ newState(PrepareToJump);
+ }
+ }
+ else
+ ai->setMoveSpeed(0);
+ return false;
+}
+
+//-------------------------------------------------------------------------------------
+
+bool AIJetting::prepareToJump(AIConnection* ai, Player* player)
+{
+ ai->setMoveLocation(mSeekDest);
+ ai->setMoveSpeed(0);
+ if (++mCounter >= JumpWaitCount)
+ {
+ // Can't check if can jump so often right now - so just do it once...
+ bool jumpReady = (mCounter==JumpWaitCount) && (player->haveContact() || player->isMounted());
+
+ // HACK to remedy problem with never finding a contact surface sometimes.
+ if (!jumpReady && (mCounter > JumpWaitCount * 8))
+ jumpReady = (ai->mVelocity.lenSquared() < 0.04);
+
+ if (jumpReady)
+ {
+ mJumpPoint = ai->mLocation;
+ Point3F here = ai->mLocation;
+ here.z += 100;
+ ai->setMoveLocation(here);
+ if (!mWillBonk || player->isMounted())
+ ai->pressJump();
+ ai->pressJet();
+ newState(InTheAir);
+ mCanInterupt = false;
+ }
+ }
+ return false;
+}
+
+//-------------------------------------------------------------------------------------
+
+bool AIJetting::inTheAir(AIConnection* ai, Player* player)
+{
+ bool advanceState = false;
+
+ if (player->haveContact()) {
+ if (++mCounter >= 2) {
+ mStatus = AIJetFail;
+ return true;
+ }
+ }
+ else
+ mCounter = 0;
+
+ ai->setMoveLocation(mSeekDest);
+
+ if (mUpChute) {
+ // Basically jet until bonk as long as we're going vertically
+ if (ai->mVelocity2D.lenSquared() > 0.04 || ai->mVelocity.z < -0.1)
+ mUpChute = false;
+ else {
+ ai->setMoveSpeed(0.0f);
+ ai->pressJet();
+ }
+ }
+
+ if (!mUpChute) {
+ if (ai->mLocation.z > mSeekDest.z) {
+ // Must monitor our Z velocity-
+ if (ai->mVelocity.z < 0)
+ ai->setMoveSpeed(0.0f);
+ else
+ ai->setMoveSpeed(1.0f);
+ ai->pressJet();
+ }
+ else {
+ ai->setMoveSpeed(0.0f);
+ F32 zSpeed = ai->mVelocity.z;
+ F32 howHigh = zSpeed * (zSpeed / GravityConstant);
+ if ((zSpeed < 0.01) || (ai->mLocation.z + howHigh) < (mSeekDest.z + 1.2))
+ ai->pressJet();
+ }
+ }
+
+ // get 2D distance to wall:
+ F32 wallD = distFromWall(ai->mLocation);
+
+ if (wallD < mTotal2D * 0.5)
+ setAim(mSeekDest);
+
+ if (wallD < 0.1)
+ advanceState = true;
+
+ // We need a good check for failure. One simple measure for failure might be to
+ // look at component of lateral velocity along the vector to the destination.
+ // Also, if we run out of energy and are far from destination...
+ if (!advanceState)
+ {
+ F32 landD;
+ if (AIJetting::figureLandingDist(ai, landD))
+ if( landD > (wallD - AmountInFront))
+ advanceState = true;
+ }
+
+ if (advanceState)
+ newState(SlowToLand);
+
+ return false;
+}
+
+//-------------------------------------------------------------------------------------
+
+bool AIJetting::slowToLand(AIConnection* ai, Player* player)
+{
+ if (player->haveContact())
+ {
+ // First clause meant to handle case where we didn't get off the ledge-
+ if (ai->mLocation.z - mLandPoint.z > 1.3 && distFromWall(ai->mLocation) > 1.0) {
+ newState(PrepareToJump, JumpWaitCount-1);
+ }
+ //==> We need to use some volume information from the destination.
+ else {
+ if (!within(ai->mLocation, mLandPoint, 4.0))
+ {
+ mStatus = AIJetFail;
+ return true;
+ }
+ else {
+ mCanInterupt = true;
+ newState(WalkToPoint);
+ }
+ }
+ }
+ else
+ {
+ Point2F vel2(ai->mVelocity.x, ai->mVelocity.y);
+ F32 speed2 = vel2.len();
+
+ setAim(mSeekDest);
+
+ // Use velocity checks to finish it up-
+ F32 zvel = mFabs(ai->mVelocity.z);
+ if (speed2 < 0.1 && zvel < 0.1)
+ return true;
+
+ F32 wallD = distFromWall(ai->mLocation), landD;
+ bool beyond = figureLandingDist(ai, landD) && (landD > wallD- AmountInFront);
+ bool pullIn = (speed2 * PullInPercent) > wallD && speed2 > 0.7;
+
+ if (beyond && pullIn) // try to slow down
+ {
+ ai->setMoveLocation(mJumpPoint);
+ ai->setMoveSpeed(1.0f);
+ ai->pressJet();
+ }
+ else
+ {
+ ai->setMoveLocation(ai->mLocation);
+ ai->setMoveSpeed(0.0f);
+ if(! beyond)
+ ai->pressJet();
+ }
+ }
+ return false;
+}
+
+//-------------------------------------------------------------------------------------
+
+bool AIJetting::walkToPoint(AIConnection* ai, Player*)
+{
+#if 0
+ mStatus = AIJetSuccess;
+ ai->setMoveSpeed(0.0f);
+ return true;
+#else
+ ai->setMoveLocation(mLandPoint);
+ ai->setMoveTolerance(HitPointThresh * 0.6);
+ ai->setMoveSpeed(0.3f);
+ if (within_2D(ai->mLocation, mLandPoint, HitPointThresh) || ++mCounter > 10)
+ {
+ // wound up under or over the point...
+ if (mFabs(ai->mLocation.z - mLandPoint.z) > HitPointThresh)
+ mStatus = AIJetFail;
+ else
+ mStatus = AIJetSuccess;
+ ai->setMoveSpeed(0.0f);
+ return true;
+ }
+ return false;
+#endif
+}
+
+//-------------------------------------------------------------------------------------
+
+// Returns true when done.
+bool AIJetting::process(AIConnection* ai, Player* player)
+{
+ if (!firstTimeStuff(ai, player))
+ return true;
+
+ switch(mState)
+ {
+ case AssureClear: return assureClear(ai, player);
+ case AwaitEnergy: return awaitEnergy(ai, player);
+ case PrepareToJump: return prepareToJump(ai, player);
+ case InTheAir: return inTheAir(ai, player);
+ case SlowToLand: return slowToLand(ai, player);
+ case WalkToPoint: return walkToPoint(ai, player);
+ }
+ return true;
+}
+
diff --git a/ai/aiNavJetting.h b/ai/aiNavJetting.h
new file mode 100644
index 0000000..a9d1564
--- /dev/null
+++ b/ai/aiNavJetting.h
@@ -0,0 +1,79 @@
+//-----------------------------------------------------------------------------
+// V12 Engine
+//
+// Copyright (c) 2001 GarageGames.Com
+// Portions Copyright (c) 2001 by Sierra Online, Inc.
+//-----------------------------------------------------------------------------
+
+#ifndef _AINAVJETTING_H_
+#define _AINAVJETTING_H_
+
+enum AIJetStatus
+{
+ AIJetInactive,
+ AIJetWorking,
+ AIJetFail,
+ AIJetSuccess
+};
+
+class AIConnection;
+
+class AIJetting
+{
+ protected:
+ enum JettingStates{
+ AssureClear,
+ AwaitEnergy,
+ PrepareToJump,
+ InTheAir,
+ SlowToLand,
+ WalkToPoint,
+ };
+
+ bool mFirstTime;
+ bool mUpChute;
+ bool mIntoMount;
+ bool mShouldAim;
+ bool mCanInterupt;
+ bool mWillBonk;
+ Point3F mSeekDest;
+ Point3F mLandPoint;
+ VectorF mWallNormal;
+ Point3F mJumpPoint;
+ Point3F mWallPoint;
+ Point3F mTopOfChute;
+ F32 mTotal2D;
+ F32 mLaunchSpeed;
+ F32 mSlope;
+ S32 mState;
+ S32 mCounter;
+ AIJetStatus mStatus;
+ NavJetting * mJetInfo;
+
+ void newState(S16 state, S16 counter = 0);
+ F32 distFromWall(const Point3F& loc);
+ void setAim(const Point3F& at);
+ bool willBonk(Point3F src, Point3F dst);
+ bool figureLandingDist(AIConnection* ai, F32& dist);
+ bool firstTimeStuff(AIConnection* ai, Player* player);
+
+ // States:
+ bool assureClear(AIConnection* ai, Player* player);
+ bool awaitEnergy(AIConnection* ai, Player* player);
+ bool prepareToJump(AIConnection* ai, Player* player);
+ bool inTheAir(AIConnection* ai, Player* player);
+ bool slowToLand(AIConnection* ai, Player* player);
+ bool walkToPoint(AIConnection* ai, Player* player);
+
+ public:
+ AIJetting() {reset();}
+ AIJetStatus status() {return mStatus;}
+ void reset() {mStatus = AIJetInactive;}
+
+ bool init(const Point3F& dest, bool intoMount = false, NavJetting * inf = 0);
+ bool process(AIConnection* ai, Player* player);
+ bool shouldAimAt(Point3F& atWhere);
+ bool badTimeToSearch() const {return true;} // {return !mCanInterupt;}
+};
+
+#endif
diff --git a/ai/aiNavStep.cc b/ai/aiNavStep.cc
new file mode 100644
index 0000000..ed61dfc
--- /dev/null
+++ b/ai/aiNavStep.cc
@@ -0,0 +1,28 @@
+//-----------------------------------------------------------------------------
+// V12 Engine
+//
+// Copyright (c) 2001 GarageGames.Com
+// Portions Copyright (c) 2001 by Sierra Online, Inc.
+//-----------------------------------------------------------------------------
+
+#include "ai/aiConnection.h"
+#include "ai/aiNavStep.h"
+
+
+AIStepJet::AIStepJet(const Point3F& dest)
+{
+ mJetting.init(dest);
+}
+
+void AIStepJet::process(AIConnection* ai, Player* player)
+{
+ Parent::process(ai, player);
+ if (mStatus != InProgress)
+ return;
+
+ ai->setMoveMode(AIConnection::ModeWalk);
+
+ if( mJetting.process(ai, player) )
+ mStatus = (mJetting.status() == AIJetSuccess ? Finished : Failed);
+}
+
diff --git a/ai/aiNavStep.h b/ai/aiNavStep.h
new file mode 100644
index 0000000..6e71b67
--- /dev/null
+++ b/ai/aiNavStep.h
@@ -0,0 +1,32 @@
+//-----------------------------------------------------------------------------
+// V12 Engine
+//
+// Copyright (c) 2001 GarageGames.Com
+// Portions Copyright (c) 2001 by Sierra Online, Inc.
+//-----------------------------------------------------------------------------
+
+#ifndef _AINAVSTEP_H_
+#define _AINAVSTEP_H_
+
+#ifndef _AISTEP_H_
+#include "ai/aiStep.h"
+#endif
+#ifndef _GRAPH_H_
+#include "ai/graph.h"
+#endif
+#ifndef _AINAVJETTING_H_
+#include "ai/aiNavJetting.h"
+#endif
+
+class AIStepJet : public AIStep
+{
+ protected:
+ typedef AIStep Parent;
+
+ AIJetting mJetting;
+ public:
+ AIStepJet(const Point3F& dest);
+ void process(AIConnection* ai, Player* player);
+};
+
+#endif
diff --git a/ai/aiObjective.cc b/ai/aiObjective.cc
new file mode 100644
index 0000000..8aaf90c
--- /dev/null
+++ b/ai/aiObjective.cc
@@ -0,0 +1,183 @@
+//-----------------------------------------------------------------------------
+// V12 Engine
+//
+// Copyright (c) 2001 GarageGames.Com
+// Portions Copyright (c) 2001 by Sierra Online, Inc.
+//-----------------------------------------------------------------------------
+
+#include "console/console.h"
+#include "console/consoleTypes.h"
+#include "console/consoleInternal.h"
+#include "Core/bitStream.h"
+#include "ai/aiObjective.h"
+
+IMPLEMENT_CO_NETOBJECT_V1(AIObjective);
+
+Sphere AIObjective::smSphere(Sphere::Octahedron);
+
+//---------------------------------------------------------------------------//
+//AIObjective Methods
+
+AIObjective::AIObjective()
+{
+ mTypeMask = StaticObjectType;
+ mDescription = StringTable->insert("");
+ mMode = StringTable->insert("");
+ mTargetClient = StringTable->insert("");
+ mTargetObject = StringTable->insert("");
+ mTargetClientId = -1;
+ mTargetObjectId = -1;
+ mLocation.set(0, 0, 0);
+
+ mWeightLevel1 = 0;
+ mWeightLevel2 = 0;
+ mWeightLevel3 = 0;
+ mWeightLevel4 = 0;
+ mOffense = false;
+ mDefense = false;
+
+ mRequiredEquipment = StringTable->insert("");
+ mDesiredEquipment = StringTable->insert("");
+ mBuyEquipmentSets = StringTable->insert("");
+ mCannedChat = StringTable->insert("");
+
+ mIssuedByHuman = false;
+ mIssuedByClientId = -1;
+ mForceClientId = -1;
+
+ mLocked = false;
+
+ //this will allow us to access the persist fields from script
+ setModStaticFields(true);
+}
+
+//----------------------------------------------------------------------------
+
+bool AIObjective::onAdd()
+{
+ if(!Parent::onAdd())
+ return(false);
+
+ //set the task namespace
+ const char *name = getName();
+ if(name && name[0] && getClassRep())
+ {
+ Namespace *parent = getClassRep()->getNameSpace();
+ Con::linkNamespaces(parent->mName, name);
+ mNameSpace = Con::lookupNamespace(name);
+ }
+
+ if(!isClientObject())
+ setMaskBits(UpdateSphereMask);
+ return(true);
+}
+
+//----------------------------------------------------------------------------
+
+void AIObjective::inspectPostApply()
+{
+ Parent::inspectPostApply();
+ setMaskBits(UpdateSphereMask);
+}
+
+void AIObjective::consoleInit()
+{
+ Parent::consoleInit();
+}
+
+//----------------------------------------------------------------------------
+
+void AIObjective::initPersistFields()
+{
+ Parent::initPersistFields();
+ addField("description", TypeString, Offset(mDescription, AIObjective));
+ addField("mode", TypeString, Offset(mMode, AIObjective));
+ addField("targetClient", TypeString, Offset(mTargetClient, AIObjective));
+ addField("targetObject", TypeString, Offset(mTargetObject, AIObjective));
+ addField("targetClientId", TypeS32, Offset(mTargetClientId, AIObjective));
+ addField("targetObjectId", TypeS32, Offset(mTargetObjectId, AIObjective));
+ addField("location", TypePoint3F, Offset(mLocation, AIObjective));
+
+ addField("weightLevel1", TypeS32, Offset(mWeightLevel1, AIObjective));
+ addField("weightLevel2", TypeS32, Offset(mWeightLevel2, AIObjective));
+ addField("weightLevel3", TypeS32, Offset(mWeightLevel3, AIObjective));
+ addField("weightLevel4", TypeS32, Offset(mWeightLevel4, AIObjective));
+ addField("offense", TypeBool, Offset(mOffense, AIObjective));
+ addField("defense", TypeBool, Offset(mDefense, AIObjective));
+
+ addField("equipment", TypeString, Offset(mRequiredEquipment, AIObjective));
+ addField("desiredEquipment", TypeString, Offset(mDesiredEquipment, AIObjective));
+ addField("buyEquipmentSet", TypeString, Offset(mBuyEquipmentSets, AIObjective));
+
+ addField("chat", TypeString, Offset(mCannedChat, AIObjective));
+
+ addField("issuedByHuman", TypeBool, Offset(mIssuedByHuman, AIObjective));
+ addField("issuedByClientId", TypeS32, Offset(mIssuedByClientId, AIObjective));
+ addField("forceClientId", TypeS32, Offset(mForceClientId, AIObjective));
+
+ addField("locked", TypeBool, Offset(mLocked, AIObjective));
+
+}
+
+//---------------------------------------------------------------------------//
+
+U32 AIObjective::packUpdate(NetConnection * con, U32 mask, BitStream * stream)
+{
+ U32 retMask = Parent::packUpdate(con, mask, stream);
+
+ //
+ if(stream->writeFlag(mask & UpdateSphereMask))
+ {
+ }
+ return(retMask);
+}
+
+void AIObjective::unpackUpdate(NetConnection * con, BitStream * stream)
+{
+ Parent::unpackUpdate(con, stream);
+ if(stream->readFlag())
+ {
+ }
+}
+
+//---------------------------------------------------------------------------//
+//AIObjectiveQ Methods
+
+IMPLEMENT_CONOBJECT(AIObjectiveQ);
+
+static void cAIQSortByWeight(SimObject *obj, S32, const char **)
+{
+ AIObjectiveQ *objectiveQ = static_cast(obj);
+ objectiveQ->sortByWeight();
+}
+
+void AIObjectiveQ::consoleInit()
+{
+ Parent::consoleInit();
+
+ Con::addCommand("AIObjectiveQ", "sortByWeight", cAIQSortByWeight, "aiQ.sortByWeight()", 2, 2);
+}
+
+void AIObjectiveQ::addObject(SimObject *obj)
+{
+ //only aioObjectives can be part of an objective Queue
+ AIObjective *objective = dynamic_cast(obj);
+ if (! objective)
+ {
+ Con::printf("Error AIObjective::addObject() - attempting to add something other than an AIObjective!");
+ //print the error, but don't return - in case I need to revert the ai scripts...
+ //return;
+ }
+ Parent::addObject(obj);
+}
+
+S32 QSORT_CALLBACK AIObjectiveQ::compareWeight(const void* a,const void* b)
+{
+ return (*reinterpret_cast(b))->getSortWeight() -
+ (*reinterpret_cast(a))->getSortWeight();
+}
+
+void AIObjectiveQ::sortByWeight()
+{
+ dQsort(objectList.address(), objectList.size(), sizeof(SimObject *), compareWeight);
+}
diff --git a/ai/aiObjective.h b/ai/aiObjective.h
new file mode 100644
index 0000000..1453219
--- /dev/null
+++ b/ai/aiObjective.h
@@ -0,0 +1,105 @@
+//-----------------------------------------------------------------------------
+// V12 Engine
+//
+// Copyright (c) 2001 GarageGames.Com
+// Portions Copyright (c) 2001 by Sierra Online, Inc.
+//-----------------------------------------------------------------------------
+
+#ifndef _AIOBJECTIVE_H_
+#define _AIOBJECTIVE_H_
+
+#ifndef _BITSTREAM_H_
+#include "core/bitStream.h"
+#endif
+#ifndef _SIMBASE_H_
+#include "console/simBase.h"
+#endif
+#ifndef _SHAPEBASE_H_
+#include "game/shapeBase.h"
+#endif
+#ifndef _MATHIO_H_
+#include "math/mathIO.h"
+#endif
+#ifndef _SPHERE_H_
+#include "game/sphere.h"
+#endif
+#ifndef _COLOR_H_
+#include "core/color.h"
+#endif
+#ifndef _MISSIONMARKER_H_
+#include "game/missionMarker.h"
+#endif
+
+
+class AIObjective : public MissionMarker
+{
+ private:
+ typedef MissionMarker Parent;
+ static Sphere smSphere;
+
+ StringTableEntry mDescription;
+ StringTableEntry mMode;
+ StringTableEntry mTargetClient;
+ StringTableEntry mTargetObject;
+ S32 mTargetClientId;
+ S32 mTargetObjectId;
+ Point3F mLocation;
+
+ S32 mWeightLevel1;
+ S32 mWeightLevel2;
+ S32 mWeightLevel3;
+ S32 mWeightLevel4;
+ bool mOffense;
+ bool mDefense;
+
+ StringTableEntry mRequiredEquipment;
+ StringTableEntry mDesiredEquipment;
+ StringTableEntry mBuyEquipmentSets;
+
+ StringTableEntry mCannedChat;
+ bool mLocked;
+
+ bool mIssuedByHuman;
+ S32 mIssuedByClientId;
+ S32 mForceClientId;
+
+ public:
+ AIObjective();
+ S32 getSortWeight() const { return mWeightLevel1; }
+
+ static void initPersistFields();
+ static void consoleInit();
+
+ // SimObject
+ bool onAdd();
+ void inspectPostApply();
+
+ // NetObject
+ enum SpawnSphereMasks {
+ UpdateSphereMask = Parent::NextFreeMask,
+ NextFreeMask = Parent::NextFreeMask << 1
+ };
+
+ // NetObject
+ U32 packUpdate(NetConnection *, U32, BitStream *);
+ void unpackUpdate(NetConnection *, BitStream *);
+
+ DECLARE_CONOBJECT(AIObjective);
+};
+
+
+class AIObjectiveQ : public SimSet
+{
+ private:
+ typedef SimSet Parent;
+
+ public:
+ DECLARE_CONOBJECT(AIObjectiveQ);
+ static void consoleInit();
+ void addObject(SimObject *obj);
+
+ static S32 QSORT_CALLBACK compareWeight(const void* a,const void* b);
+ void sortByWeight();
+};
+
+#endif
diff --git a/ai/aiPlayer.cc b/ai/aiPlayer.cc
new file mode 100644
index 0000000..0e14f6b
--- /dev/null
+++ b/ai/aiPlayer.cc
@@ -0,0 +1,446 @@
+//-----------------------------------------------------------------------------
+// V12 Engine
+//
+// Copyright (c) 2001 GarageGames.Com
+// Portions Copyright (c) 2001 by Sierra Online, Inc.
+//-----------------------------------------------------------------------------
+#include "ai/aiPlayer.h"
+#include "core/realcomp.h"
+#include "math/mMatrix.h"
+#include "game/moveManager.h"
+
+/**
+ * Constructor
+ */
+AIPlayer::AIPlayer() {
+ mMoveMode = ModeStop;
+ mMoveDestination.set( 0.0f, 0.0f, 0.0f );
+ mAimLocation.set( 0.0f, 0.0f, 0.0f );
+ mMoveSpeed = 0.0f;
+ mMoveTolerance = 0.25f;
+
+ // Clear the triggers
+ for( int i = 0; i < MaxTriggerKeys; i++ )
+ mTriggers[i] = false;
+
+ mAimToDestination = true;
+
+ mRotation.set( 0.0f, 0.0f, 0.0f );
+ mLocation.set( 0.0f, 0.0f, 0.0f );
+ mPlayer = NULL;
+}
+
+/**
+ * Sets the object the bot is targeting
+ *
+ * @param targetObject The object to target
+ */
+void AIPlayer::setTargetObject( ShapeBase *targetObject ) {
+
+ if ( !targetObject || !bool( mTargetObject ) || targetObject->getId() != mTargetObject->getId() )
+ mTargetInSight = false;
+
+ mTargetObject = targetObject;
+}
+
+/**
+ * Returns the target object
+ *
+ * @return Object bot is targeting
+ */
+S32 AIPlayer::getTargetObject() {
+ if( bool( mTargetObject ) )
+ return mTargetObject->getId();
+ else
+ return -1;
+}
+
+/**
+ * Sets the speed at which this AI moves
+ *
+ * @param speed Speed to move, default player was 10
+ */
+void AIPlayer::setMoveSpeed( F32 speed ) {
+ if( speed <= 0.0f )
+ mMoveSpeed = 0.0f;
+ else
+ mMoveSpeed = getMin( 1.0f, speed );
+}
+
+/**
+ * Sets the movement mode for this AI
+ *
+ * @param mode Movement mode, see enum
+ */
+void AIPlayer::setMoveMode( S32 mode ) {
+ if( mode < 0 || mode >= ModeCount )
+ mode = 0;
+
+ mMoveMode = mode;
+}
+
+/**
+ * Sets how far away from the move location is considered
+ * "on target"
+ *
+ * @param tolerance Movement tolerance for error
+ */
+void AIPlayer::setMoveTolerance( F32 tolerance ) {
+ mMoveTolerance = getMax( 0.1f, tolerance );
+}
+
+/**
+ * Sets the location for the bot to run to
+ *
+ * @param location Point to run to
+ */
+void AIPlayer::setMoveDestination( const Point3F &location ) {
+ // Ok, here's the story...we're going to aim where we are going UNLESS told otherwise
+ if( mAimToDestination ) {
+ mAimLocation = location;
+ mAimLocation.z = 0.0f;
+ }
+
+ mMoveDestination = location;
+
+ // TEST CODE
+ RayInfo dummy;
+
+ if( mPlayer ) {
+ if( !mPlayer->getContainer()->castRay( mLocation, location, InteriorObjectType |
+ StaticShapeObjectType | StaticObjectType |
+ TerrainObjectType, &dummy ) )
+ Con::printf( "I can see the target." );
+ else
+ Con::printf( "I can't see my target!! AAAAHHHHH!" );
+ }
+}
+
+/**
+ * Sets the location for the bot to aim at
+ *
+ * @param location Point to aim at
+ */
+void AIPlayer::setAimLocation( const Point3F &location ) {
+ mAimLocation = location;
+ mAimToDestination = false;
+}
+
+/**
+ * Clears the aim location and sets it to the bot's
+ * current destination so he looks where he's going
+ */
+void AIPlayer::clearAim() {
+ mAimLocation = Point3F( 0.0f, 0.0f, 0.0f );
+ mAimToDestination = true;
+}
+
+/**
+ * This method gets the move list for an object, in the case
+ * of the AI, it actually calculates the moves, and then
+ * sends them down the pipe.
+ *
+ * @param movePtr Pointer to move the move list into
+ * @param numMoves Number of moves in the move list
+ */
+void AIPlayer::getMoveList( Move **movePtr,U32 *numMoves ) {
+ //initialize the move structure and return pointers
+ mMove = NullMove;
+ *movePtr = &mMove;
+ *numMoves = 1;
+
+ // Check if we got a player
+ mPlayer = NULL;
+ mPlayer = dynamic_cast( getControlObject() );
+
+ // We got a something controling us?
+ if( !mPlayer )
+ return;
+
+ // If system is disabled, don't process
+ if ( !gAISystemEnabled )
+ return;
+
+ // What is The Matrix?
+ MatrixF moveMatrix;
+ moveMatrix.set( EulerF( 0, 0, 0 ) );
+ moveMatrix.setColumn( 3, Point3F( 0, 0, 0 ) );
+ moveMatrix.transpose();
+
+ // Position / rotation variables
+ F32 curYaw, curPitch;
+ F32 newYaw, newPitch;
+ F32 xDiff, yDiff, zDiff;
+
+
+
+ // Check if we are dead, if so, throw the script callback -- PUT THIS IN AIPLAYER
+ //if( !dStricmp( mPlayer->getStateName(), "dead" ) ) {
+ // TO-DO: Script callback
+ // return;
+ //}
+
+ F32 moveSpeed = mMoveSpeed;
+
+ switch( mMoveMode ) {
+
+ case ModeStop:
+ return; // Stop means no action, peroid
+ break;
+
+ case ModeIdle:
+ // TO-DO: Insert callback for scripted idle stuff
+ break;
+
+ case ModeWalk:
+ moveSpeed /= 2.0f; // Walking speed is half running speed
+ // Fall through to run
+ case ModeRun:
+
+ // Get my location
+ MatrixF const& myTransform = mPlayer->getTransform();
+ myTransform.getColumn( 3, &mLocation );
+
+ // Set rotation variables
+ mRotation = mPlayer->getRotation();
+ Point3F headRotation = mPlayer->getHeadRotation();
+ curYaw = mRotation.z;
+ curPitch = headRotation.x;
+ xDiff = mAimLocation.x - mLocation.x;
+ yDiff = mAimLocation.y - mLocation.y;
+
+ // first do Yaw
+ if( !isZero( xDiff ) || !isZero( yDiff ) ) {
+ // use the cur yaw between -Pi and Pi
+ while( curYaw > M_2PI )
+ curYaw -= M_2PI;
+ while( curYaw < -M_2PI )
+ curYaw += M_2PI;
+
+ // find the new yaw
+ newYaw = mAtan( xDiff, yDiff );
+
+ // find the yaw diff
+ F32 yawDiff = newYaw - curYaw;
+
+ // make it between 0 and 2PI
+ if( yawDiff < 0.0f )
+ yawDiff += M_2PI;
+ else if( yawDiff >= M_2PI )
+ yawDiff -= M_2PI;
+
+ // now make sure we take the short way around the circle
+ if( yawDiff > M_PI )
+ yawDiff -= M_2PI;
+ else if( yawDiff < -M_PI )
+ yawDiff += M_2PI;
+
+ mMove.yaw = yawDiff;
+
+ // set up the movement matrix
+ moveMatrix.set( EulerF( 0, 0, newYaw ) );
+ }
+ else
+ moveMatrix.set( EulerF( 0, 0, curYaw ) );
+
+ // next do pitch
+ F32 horzDist = Point2F( mAimLocation.x, mAimLocation.y ).len();
+
+ if( !isZero( horzDist ) ) {
+ //we shoot from the gun, not the eye...
+ F32 vertDist = mAimLocation.z;
+
+ newPitch = mAtan( horzDist, vertDist ) - ( M_PI / 2.0f );
+
+ F32 pitchDiff = newPitch - curPitch;
+ mMove.pitch = pitchDiff;
+ }
+
+ // finally, mMove towards mMoveDestination
+ xDiff = mMoveDestination.x - mLocation.x;
+ yDiff = mMoveDestination.y - mLocation.y;
+
+ // Check if we should mMove, or if we are 'close enough'
+ if( ( ( mFabs( xDiff ) > mMoveTolerance ) ||
+ ( mFabs( yDiff ) > mMoveTolerance ) ) && ( !isZero( mMoveSpeed ) ) )
+ {
+ if( isZero( xDiff ) )
+ mMove.y = ( mLocation.y > mMoveDestination.y ? -moveSpeed : moveSpeed );
+ else if( isZero( yDiff ) )
+ mMove.x = ( mLocation.x > mMoveDestination.x ? -moveSpeed : moveSpeed );
+ else if( mFabs( xDiff ) > mFabs( yDiff ) ) {
+ F32 value = mFabs( yDiff / xDiff ) * mMoveSpeed;
+ mMove.y = ( mLocation.y > mMoveDestination.y ? -value : value );
+ mMove.x = ( mLocation.x > mMoveDestination.x ? -moveSpeed : moveSpeed );
+ }
+ else {
+ F32 value = mFabs( xDiff / yDiff ) * mMoveSpeed;
+ mMove.x = ( mLocation.x > mMoveDestination.x ? -value : value );
+ mMove.y = ( mLocation.y > mMoveDestination.y ? -moveSpeed : moveSpeed );
+ }
+
+ //now multiply the mMove vector by the transpose of the object rotation matrix
+ moveMatrix.transpose();
+ Point3F newMove;
+ moveMatrix.mulP( Point3F( mMove.x, mMove.y, 0 ), &newMove );
+
+ //and sub the result back in the mMove structure
+ mMove.x = newMove.x;
+ mMove.y = newMove.y;
+ }
+ else {
+ // Ok, we are close enough
+ setMoveMode( ModeStop );
+ Con::printf( "I'm close enough to my destination to stop." );
+ }
+ break;
+ }
+
+ // Copy over the trigger status
+ for( int i = 0; i < MaxTriggerKeys; i++ ) {
+ mMove.trigger[i] = mTriggers[i];
+ mTriggers[i] = false;
+ }
+}
+
+/**
+ * This method is just called to stop the bots from running amuck
+ * while the mission cycles
+ */
+void AIPlayer::missionCycleCleanup() {
+ setMoveMode( ModeStop );
+}
+
+/**
+ * Sets the move speed for an AI object, remember, walk is 1/2 run
+ */
+static void cAISetMoveSpeed( SimObject *obj, S32, const char** argv ) {
+ AIPlayer *ai = static_cast( obj );
+ ai->setMoveSpeed( dAtoi( argv[2] ) );
+}
+
+/**
+ * Stops all AI movement, halt!
+ */
+static void cAIStop( SimObject *obj, S32, const char** ) {
+ AIPlayer *ai = static_cast( obj );
+ ai->setMoveMode( AIPlayer::ModeStop );
+}
+
+/**
+ * Tells the AI to aim at the location provided
+ */
+static void cAIAimAtLocation( SimObject *obj, S32 argc, const char** argv ) {
+ AIPlayer *ai = static_cast( obj );
+ Point3F v( 0.0f,0.0f,0.0f );
+ dSscanf( argv[2], "%f %f %f", &v.x, &v.y, &v.z );
+
+ ai->setAimLocation( v );
+}
+
+/**
+ * Returns the point the AI is aiming at
+ */
+static const char *cAIGetAimLocation( SimObject *obj, S32, const char** ) {
+ AIPlayer *ai = static_cast( obj );
+ Point3F aimPoint = ai->getAimLocation();
+
+ char* returnBuffer = Con::getReturnBuffer( 256 );
+ dSprintf( returnBuffer, 256, "%f %f %f", aimPoint.x, aimPoint.y, aimPoint.z );
+
+ return returnBuffer;
+}
+
+/**
+ * Sets the bots target object
+ */
+static void cAISetTargetObject( SimObject *obj, S32 argc, const char **argv ) {
+ AIPlayer *ai = static_cast( obj );
+
+ // Find the target
+ ShapeBase *targetObject;
+ if( Sim::findObject( argv[2], targetObject ) )
+ ai->setTargetObject( targetObject );
+ else
+ ai->setTargetObject( NULL );
+}
+
+/**
+ * Gets the object the AI is targeting
+ */
+static const char *cAIGetTargetObject( SimObject *obj, S32, const char ** ) {
+ AIPlayer *ai = static_cast( obj );
+ S32 targetId = ai->getTargetObject();
+ char* returnBuffer = Con::getReturnBuffer( 256 );
+ dSprintf( returnBuffer, 256, "%d", targetId );
+
+ return returnBuffer;
+}
+
+/**
+ * Checks to see if the target is in sight
+ */
+static bool cAITargetInSight( SimObject *obj, S32, const char ** ) {
+ AIPlayer *ai = static_cast( obj );
+ return ai->targetInSight();
+}
+
+/**
+ * Tells the bot the mission is cycling
+ */
+static void cAIMissionCycleCleanup( SimObject *obj, S32, const char ** ) {
+ AIPlayer *ai = static_cast( obj );
+ ai->missionCycleCleanup();
+}
+
+/**
+ * Tells the AI to move forward 100 units...TEST FXN
+ */
+static void cAIMoveForward( SimObject *obj, S32, const char ** ) {
+
+ AIPlayer *ai = static_cast( obj );
+ ShapeBase *player = ai->getControlObject();
+ Point3F location;
+ MatrixF const &myTransform = player->getTransform();
+ myTransform.getColumn( 3, &location );
+
+ location.y += 100.0f;
+
+ ai->setMoveDestination( location );
+} // *** /TEST FXN
+
+/**
+ * Sets the AI to walk mode
+ */
+static void cAIWalk( SimObject *obj, S32, const char ** ) {
+ AIPlayer *ai = static_cast( obj );
+ ai->setMoveMode( AIPlayer::ModeWalk );
+}
+
+/**
+ * Sets the AI to run mode
+ */
+static void cAIRun( SimObject *obj, S32, const char ** ) {
+ AIPlayer *ai = static_cast( obj );
+ ai->setMoveMode( AIPlayer::ModeRun );
+}
+
+
+/**
+ * Console init function
+ */
+void AIPlayer::consoleInit() {
+ // TEST FXN
+ Con::addCommand( "AIPlayer", "moveForward", cAIMoveForward, "ai.moveForward()", 2, 2 );
+
+ Con::addCommand( "AIPlayer", "walk", cAIWalk, "ai.walk()", 2, 2 );
+ Con::addCommand( "AIPlayer", "run", cAIRun, "ai.run()", 2, 2 );
+ Con::addCommand( "AIPlayer", "stop", cAIStop, "ai.stop()", 2, 2 );
+ Con::addCommand( "AIPlayer", "setMoveSpeed", cAISetMoveSpeed, "ai.setMoveSpeed( float )", 3, 3 );
+
+ Con::addCommand( "AIPlayer", "setTargetObject", cAISetTargetObject, "ai.setTargetObject( object )", 3, 4 );
+ Con::addCommand( "AIPlayer", "getTargetObject", cAIGetTargetObject, "ai.getTargetObject()", 2, 2 );
+ Con::addCommand( "AIPlayer", "targetInSight", cAITargetInSight, "ai.targetInSight()", 2, 2 );
+ Con::addCommand( "AIPlayer", "aimAt", cAIAimAtLocation, "ai.aimAt( point )", 3, 3 );
+ Con::addCommand( "AIPlayer", "getAimLocation", cAIGetAimLocation, "ai.getAimLocation()", 2, 2 );
+}
\ No newline at end of file
diff --git a/ai/aiPlayer.h b/ai/aiPlayer.h
new file mode 100644
index 0000000..ce91f03
--- /dev/null
+++ b/ai/aiPlayer.h
@@ -0,0 +1,92 @@
+//-----------------------------------------------------------------------------
+// V12 Engine
+//
+// Copyright (c) 2001 GarageGames.Com
+// Portions Copyright (c) 2001 by Sierra Online, Inc.
+//-----------------------------------------------------------------------------
+
+#ifndef _AIPLAYER_H_
+#define _AIPLAYER_H_
+
+#include "ai/aiConnection.h"
+#include "game/player.h"
+
+class AIPlayer : public AIConnection {
+
+ typedef AIConnection Parent;
+
+ private:
+ enum {
+ FireTrigger = 0,
+ JumpTrigger = 2,
+ JetTrigger = 3,
+ GrenadeTrigger = 4,
+ MineTrigger = 5
+ };
+
+ F32 mMoveSpeed;
+ S32 mMoveMode;
+ F32 mMoveTolerance; // How close to the destination before we stop
+
+ bool mTriggers[MaxTriggerKeys];
+
+ bool mTargetInSight;
+ Player *mPlayer;
+
+ Point3F mMoveDestination;
+ Point3F mLocation;
+ Point3F mRotation; // Euler really
+
+ bool mAimToDestination; // Why is this in here?
+ Point3F mAimLocation; // Because objects would have facing as well.
+
+
+ SimObjectPtr mTargetObject;
+ public:
+
+ DECLARE_CONOBJECT( AIPlayer );
+
+ enum
+ {
+ ModeStop = 0,
+ ModeWalk, // Walk is runSpeed / 2
+ ModeRun,
+ ModeIdle,
+ ModeCount // This is in there as a max index value
+ };
+
+ AIPlayer();
+
+ void getMoveList( Move **movePtr,U32 *numMoves );
+ static void consoleInit();
+
+ // ---Targeting and aiming sets/gets
+ void setTargetObject( ShapeBase *targetObject );
+ S32 getTargetObject();
+ bool targetInSight() { return mTargetInSight; }
+
+ // ---Movement sets/gets
+ void setMoveSpeed( F32 speed );
+ F32 getMoveSpeed() { return mMoveSpeed; }
+
+ void setMoveMode( S32 mode );
+ S32 getMoveMode() { return mMoveMode; }
+
+ void setMoveTolerance( F32 tolerance );
+ F32 getMoveTolerance() { return mMoveTolerance; }
+
+ void setMoveDestination( const Point3F &location );
+ Point3F getMoveDestination() { return mMoveDestination; }
+
+ // ---Facing(Aiming) sets/gets
+ void setAimLocation( const Point3F &location );
+ Point3F getAimLocation() { return mAimLocation; }
+ void clearAim();
+
+ // ---Other
+ void missionCycleCleanup();
+
+ // ---Callbacks
+};
+
+#endif
\ No newline at end of file
diff --git a/ai/aiStep.cc b/ai/aiStep.cc
new file mode 100644
index 0000000..9d6077d
--- /dev/null
+++ b/ai/aiStep.cc
@@ -0,0 +1,805 @@
+//-----------------------------------------------------------------------------
+// V12 Engine
+//
+// Copyright (c) 2001 GarageGames.Com
+// Portions Copyright (c) 2001 by Sierra Online, Inc.
+//-----------------------------------------------------------------------------
+
+#include "core/realComp.h"
+#include "console/simBase.h"
+#include "ai/aiConnection.h"
+#include "game/player.h"
+#include "game/projectile.h"
+#include "math/mRandom.h"
+#include "ai/aiStep.h"
+
+AIStep::AIStep()
+{
+ mStatus = InProgress;
+}
+
+void AIStep::process(AIConnection *client, Player *player)
+{
+ //only process if we're still "in progress"
+ if (mStatus != InProgress)
+ return;
+
+ //make sure we have a client and a player
+ if (!client || !player)
+ mStatus = Failed;
+
+ //make sure we're not dead
+ if (! dStricmp(player->getStateName(), "dead"))
+ mStatus = Failed;
+}
+
+//-----------------------------------------------------------------------------
+//ESCORT step
+AIStepEscort::AIStepEscort(GameConnection *clientToEscort)
+{
+ mInitialized = false;
+ mClientToEscort = clientToEscort;
+ mResetDestinationCounter = 0;
+}
+
+void AIStepEscort::process(AIConnection *client, Player *player)
+{
+ Parent::process(client, player);
+ if (mStatus != InProgress)
+ return;
+
+ //make sure we have a valid target to escort
+ Player *targetPlayer = NULL;
+ if ((! bool(mClientToEscort)) || (! mClientToEscort->getControlObject()))
+ mStatus = Failed;
+ else
+ targetPlayer = dynamic_cast(mClientToEscort->getControlObject());
+ if (! targetPlayer)
+ {
+ mStatus = Failed;
+ return;
+ }
+
+ //set the energy levels
+ if (! mInitialized)
+ {
+ mInitialized = true;
+ if (targetPlayer->isMounted())
+ client->setMoveMode(AIConnection::ModeMountVehicle);
+ else
+ client->setMoveMode(AIConnection::ModeExpress);
+ client->setMoveTolerance(4.0f);
+ client->setEnergyLevels(0.05f, 0.15f);
+ }
+
+ //get the target's current location (global coords)
+ MatrixF const& targetTransform = targetPlayer->getTransform();
+ Point3F targetLocation;
+ targetTransform.getColumn(3, &targetLocation);
+
+ //go back to using the euclidean distance
+ F32 distToTarget = getMax(client->getPathDistRemaining(20), (client->mLocation - targetLocation).len());
+
+ //cut down on the number of path searches
+ if (--mResetDestinationCounter <= 0)
+ {
+ mResetDestinationCounter = 5;
+ client->setMoveDestination(targetLocation);
+ }
+
+ //get the current time
+ S32 curTime = Sim::getCurrentTime();
+
+ //see if we're close enough to the target yet
+ if (distToTarget < 10.0f)
+ {
+ mProximityBuffer = true;
+ if (client->getMoveMode() != AIConnection::ModeStop)
+ {
+ client->setMoveMode(AIConnection::ModeStop);
+ mStoppedTime = curTime;
+ mIdleStarted = false;
+ }
+ }
+ else if (distToTarget < 16.0f)
+ {
+ if (mProximityBuffer)
+ {
+ if (client->getMoveMode() != AIConnection::ModeStop)
+ {
+ client->setMoveMode(AIConnection::ModeStop);
+ mStoppedTime = curTime;
+ mIdleStarted = false;
+ }
+ }
+ else
+ {
+ //make sure we aim at the player momentarily...
+ if (client->getMoveMode() == AIConnection::ModeStop)
+ client->setScriptAimLocation(Point3F(targetLocation.x, targetLocation.y, targetLocation.z + 1.6f), 500);
+
+ if (targetPlayer->isMounted())
+ client->setMoveMode(AIConnection::ModeMountVehicle);
+ else
+ client->setMoveMode(AIConnection::ModeExpress);
+ }
+ }
+ else
+ {
+ //make sure we aim at the player momentarily...
+ if (client->getMoveMode() == AIConnection::ModeStop)
+ client->setScriptAimLocation(Point3F(targetLocation.x, targetLocation.y, targetLocation.z + 1.6f), 500);
+ mProximityBuffer = false;
+ if (targetPlayer->isMounted())
+ client->setMoveMode(AIConnection::ModeMountVehicle);
+ else
+ client->setMoveMode(AIConnection::ModeExpress);
+ }
+
+ //now see if it's time to "idle"
+ if (client->getMoveMode() == AIConnection::ModeStop && curTime - mStoppedTime > 5000)
+ {
+ //init the idle vars
+ if (!mIdleStarted)
+ {
+ mIdleStarted = true;
+ mChokePoints.clear();
+ NavigationGraph::getChokePoints(client->mLocation, mChokePoints, 10, 65);
+ mIdleNextTime = 0;
+ }
+
+ //see if it's time to look around some more
+ if (curTime > mIdleNextTime)
+ {
+ mIdleNextTime = curTime + 2000 + gRandGen.randF() * 3000;
+
+ //see if we should look or play an animation
+ if (gRandGen.randF() < 0.06f)
+ {
+ player->setActionThread("pda", false, true, false);
+ }
+ else
+ {
+ Point3F lookLocation;
+ if (mChokePoints.size() == 0)
+ {
+ if (gRandGen.randF() < 0.3f)
+ lookLocation = targetLocation;
+ else
+ lookLocation = client->mLocation;
+ }
+ else
+ {
+ S32 index = S32(gRandGen.randF() * (mChokePoints.size() + 0.9));
+ if (index == mChokePoints.size())
+ lookLocation = targetLocation;
+ else
+ lookLocation = mChokePoints[index];
+ }
+
+ //add some noise to the lookLocation
+ Point3F tempVector = lookLocation - client->mLocation;
+ tempVector.z = 0;
+ if (tempVector.len() < 0.1f)
+ tempVector.set(1, 0, 0);
+ tempVector.normalize();
+ tempVector *= 10.0f;
+ F32 noise = 2.0f;
+ tempVector.x += -noise + gRandGen.randF() * 2 * noise;
+ tempVector.y += -noise + gRandGen.randF() * 2 * noise;
+ tempVector.z = 1.5f + gRandGen.randF() * 2 * noise;
+ Point3F newAimLocation = client->mLocation + tempVector;
+
+ //now aim at that point
+ if (!client->scriptIsAiming())
+ client->setScriptAimLocation(newAimLocation, 3000);
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+//ENGAGE step
+AIStepEngage::AIStepEngage(GameConnection *target)
+{
+ mInitialized = false;
+ mTarget = target;
+ mStraifeCounter = 0;
+ mPauseCounter = 0;
+ mPausing = false;
+ mCheckLOSCounter = 0;
+ mClearLOSToTarget = false;
+
+ mSearching = false;
+ mSearchInitialized = false;
+
+ mUsingEnergyWeapon = false;
+ mEnergyWeaponRecharge = 0.0f;
+}
+
+Point3F AIStepEngage::findStraifeLocation(AIConnection *client)
+{
+ Point3F newLocation;
+ Point3F tempV = client->mLocation - client->mTargLocation;
+ Point3F newLocationVector;
+ if (tempV.len() < 0.001f)
+ tempV.set(0, 1, 0);
+ tempV.normalize();
+ mCross(tempV, Point3F(0, 0, 1), &newLocationVector);
+ if (newLocationVector.len() < 0.001f)
+ newLocationVector.set(0, 1, 0);
+ newLocationVector.normalize();
+
+ F32 newLocationLength = newLocationVector.len();
+ if (newLocationLength > 0.9f && newLocationLength < 1.1f)
+ {
+ //if we're not moving, choose randomly whether we straife right or left
+ if (client->mVelocity2D.len() < 1.0f)
+ newLocation = client->mTargLocation + (newLocationVector * client->mEngageMinDistance);
+ else
+ {
+ //otherwise, choose the point closest to the direction we're already moving...
+ F32 myAngle = AIConnection::get2DAngle(client->mLocation + client->mVelocity2D, client->mLocation);
+ F32 tempAngle1 = AIConnection::get2DAngle(client->mLocation, client->mTargLocation + newLocationVector);
+ F32 tempAngle2 = AIConnection::get2DAngle(client->mLocation, client->mTargLocation - newLocationVector);
+
+ if (mFabs(tempAngle1 - myAngle) < mFabs(tempAngle2 - myAngle))
+ newLocation = client->mTargLocation + (newLocationVector * client->mEngageMinDistance);
+ else
+ newLocation = client->mTargLocation - (newLocationVector * client->mEngageMinDistance);
+ }
+ }
+ else
+ {
+ Con::printf("DEBUG AIStepEngage::findStraifeLocation() - player and target are on top of each other...");
+ newLocation = client->mTargLocation;
+ }
+
+ //return the result
+ return newLocation;
+}
+
+void AIStepEngage::process(AIConnection *client, Player *player)
+{
+ Parent::process(client, player);
+ if (mStatus != InProgress)
+ return;
+
+ //make sure the target is set
+ if (! mInitialized)
+ {
+ mInitialized = true;
+
+ //set the target and the energy levels
+ client->setMoveTolerance(2.0f);
+ client->setEngageTarget(mTarget);
+ client->setEnergyLevels(0.3f, 0.2f);
+ return;
+ }
+
+ //make sure we still have a target
+ if (! client->mTargetPlayer)
+ {
+ mStatus = Finished;
+ client->setMoveMode(AIConnection::ModeStop);
+ return;
+ }
+
+ //set the outdoor bools
+ bool targIsOutdoors = (client->getOutdoorRadius(client->mTargLocation) > 0);
+ bool playerIsOutdoors = (client->getOutdoorRadius(client->mLocation) > 0);
+
+ //set the LOS variables
+ S32 losTime;
+ Point3F losLocation;
+ bool hasLOS = client->hasLOSToClient(client->getEngageTarget(), losTime, losLocation);
+
+ //only gain height or straife if the target is outdoors
+ if (targIsOutdoors)
+ {
+ //decriment the straife counter
+ bool timeToStraife = false;
+ mStraifeCounter--;
+
+ //see if we should try to gain height
+ S32 mode = client->getMoveMode();
+ if (mode == AIConnection::ModeGainHeight)
+ {
+ //see if we need to cancel the gaining of height
+ if (client->mEnergy < 0.2f || client->mLocation.z - client->mTargLocation.z > 15.0f ||
+ client->mEnergy < client->mWeaponEnergy - client->mEnergyReserve)
+ {
+ timeToStraife = true;
+ }
+ }
+ else
+ {
+ //see if we should try to gain height - only if we're outdoors
+ if (playerIsOutdoors && mPauseCounter <= 0 && (client->mEnergy > 0.5f || client->mEnergy - client->mTargEnergy > 0.3f) &&
+ client->mEnergy > client->mWeaponEnergy + client->mEnergyFloat &&
+ client->mDistToTarg2D < 45.0f && client->mVelocity2D.len() > 5.0f &&
+ client->mLocation.z - client->mTargLocation.z < 15.0f && client->mSkillLevel >= 0.3f)
+ client->setMoveMode(AIConnection::ModeGainHeight);
+
+ //see if we need to straife
+ else
+ {
+ //see if we're nearish our destination
+ Point3F straifeVector = client->mLocation - mStraifeLocation;
+ straifeVector.z = 0;
+ if (straifeVector.len() < getMin(8, client->mEngageMinDistance))
+ {
+ client->setMoveMode(AIConnection::ModeStop);
+ if (!mPausing)
+ {
+ //if we have had LOS to the client within the last 5 seconds at this moment, then pause
+ //no need to pause if we're behind a hill
+ if (hasLOS || losTime < 5000)
+ {
+ F32 skillAdjust = (1.0f - client->mSkillLevel) *
+ (1.0f - client->mSkillLevel) *
+ (1.0f - client->mSkillLevel);
+ mPauseCounter = S32(gRandGen.randF() * 400.0f * skillAdjust);
+ mPausing = true;
+ }
+ }
+ mPauseCounter--;
+ }
+
+ if (mStraifeCounter <= 0 || mPauseCounter <= 0)
+ timeToStraife = true;
+ }
+ }
+
+ //see if we need to straife
+ if (timeToStraife)
+ {
+ F32 skillAdjust = (1.0f - client->mSkillLevel) *
+ (1.0f - client->mSkillLevel);
+ mStraifeCounter = 70 + S32(400.0f * skillAdjust);
+ mPauseCounter = 32767;
+ mPausing = false;
+ mStraifeLocation = findStraifeLocation(client);
+ client->setMoveMode(AIConnection::ModeExpress);
+ client->setMoveDestination(mStraifeLocation);
+ }
+ }
+ else
+ {
+ //if we've never even detected the client, technically, this is a script error -
+ //bots shouldn't be fighting anyone they've never seen. However...
+ if (losLocation == Point3F(0, 0, 0))
+ losLocation = client->mTargLocation;
+
+ //if the targ is Indoors, and we're outdoors, move to the client's position so that
+ //it's not so rediculously easy to lose the bots just by running into a doorway..
+ if (playerIsOutdoors)
+ losLocation = client->mTargLocation;
+
+ //see if we have LOS to the target (needed since we use a slightly different point than the LOS detection table
+ if (--mCheckLOSCounter <= 0)
+ {
+ player->disableCollision();
+ mCheckLOSCounter = 10;
+ mClearLOSToTarget = false;
+ RayInfo rayInfo;
+ Point3F startPt = player->getBoxCenter();
+ Point3F endPt = client->mTargLocation;
+ U32 mask = TerrainObjectType | InteriorObjectType | WaterObjectType | ForceFieldObjectType;
+ mClearLOSToTarget = (! gServerContainer.castRay(startPt, endPt, mask, &rayInfo));
+ player->enableCollision();
+ }
+
+ //see if we're already at our losLocation, is it time to start idling (searching)?
+ if (!mClearLOSToTarget && !mSearching && ((client->mLocation - losLocation).len() < 3.0f) && (client->getMoveMode() == AIConnection::ModeStop))
+ {
+ mSearching = true;
+ mSearchInitialized = false;
+ }
+ else if (mSearching)
+ {
+ //see if we should abort the search (we found our target)
+ if (mClearLOSToTarget)
+ {
+ mSearching = false;
+ }
+
+ //else initialize the search
+ else if (! mSearchInitialized)
+ {
+ mSearchInitialized = true;
+ mChokeLocation = losLocation;
+ mChokeIndex = -1;
+ NavigationGraph::getChokePoints(losLocation, mChokePoints, 10, 65);
+ mSearchTimer = Sim::getCurrentTime() + 3000;
+ }
+
+ else
+ {
+ //see if we're at the choke location, and we've been there for a few seconds...
+ if (((client->mLocation - mChokeLocation).len() < 3.0f) && (client->getMoveMode() == AIConnection::ModeStop) && (Sim::getCurrentTime() > mSearchTimer))
+ {
+ //remove the current choke index from the array
+ if (mChokeIndex >= 0)
+ mChokePoints.erase(mChokeIndex);
+
+ //if we've run out of choke points to check, we've lost our target...
+ if (mChokePoints.size() <= 0)
+ {
+ mStatus = Failed;
+ return;
+ }
+ else
+ {
+ mChokeIndex = S32(gRandGen.randF() * (mChokePoints.size() - 0.1f));
+ mChokeLocation = mChokePoints[mChokeIndex];
+ mSearchTimer = Sim::getCurrentTime() + (gRandGen.randF() * 1000) + 2000;
+ client->setMoveMode(AIConnection::ModeExpress);
+ }
+ }
+
+ //move to the choke point
+ client->setMoveDestination(mChokeLocation);
+ }
+ }
+
+ //else we either have LOS, or we're moving to the target's last known location...
+ else
+ {
+ client->setMoveDestination(losLocation);
+
+ //if we can see the target, and we're close enough, don't get any closer
+ if (mClearLOSToTarget && client->mDistToTarg2D < 15.0f)
+ client->setMoveMode(AIConnection::ModeStop);
+ else
+ client->setMoveMode(AIConnection::ModeExpress);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+//RangeObject step
+AIStepRangeObject::AIStepRangeObject(GameBase *targetObject, const char *projectile, F32 *minDist, F32 *maxDist, Point3F *fromLocation)
+{
+ mInitialized = false;
+ mTargetObject = targetObject;
+ mCheckLOSCounter = 0;
+
+ //set the range distances
+ F32 minDistance = 0;
+ F32 maxDistance = 1000;
+ if (*minDist)
+ minDistance = *minDist;
+ if (*maxDist)
+ maxDistance = *maxDist;
+
+ mMinDistance = getMax(0.0f, getMin(minDistance, maxDistance));
+ mMaxDistance = getMax(1.0f, getMax(minDistance, maxDistance));
+ mFromLocation.set(-1, -1, -1);
+ if (fromLocation)
+ mFromLocation = *fromLocation;
+ mPrevLocation.set(0, 0, 0);
+
+ //set the mask
+ mLOSMask = TerrainObjectType | InteriorObjectType | PlayerObjectType | ForceFieldObjectType;
+ if (bool(mTargetObject))
+ {
+ Player *plyr = dynamic_cast(targetObject);
+ if (bool(plyr))
+ mLOSMask = TerrainObjectType | InteriorObjectType | StaticShapeObjectType | ForceFieldObjectType;
+ }
+
+ if ((! projectile) || (! projectile[0]) || (! dStricmp(projectile, "NoAmmo")))
+ mProjectile = NULL;
+ else
+ {
+ if (! Sim::findObject(projectile, mProjectile))
+ {
+ Con::printf("AIStepRangeObject() failed - unable to find projectile datablock: %s", projectile);
+ mProjectile = NULL;
+ }
+ }
+
+ if (! bool(mTargetObject) || ! bool(mProjectile))
+ mStatus = Failed;
+}
+
+void AIStepRangeObject::process(AIConnection *client, Player *player)
+{
+ //make sure we have a client and a player
+ if (!client || !player)
+ {
+ mStatus = Failed;
+ return;
+ }
+
+ //make sure we're not dead
+ if (! dStricmp(player->getStateName(), "Dead"))
+ {
+ mStatus = Failed;
+ return;
+ }
+
+ //make sure we still have a target object
+ if (! bool(mTargetObject) || ! bool(mProjectile))
+ {
+ mStatus = Failed;
+ return;
+ }
+
+ //make sure the target is set
+ if (! mInitialized)
+ {
+ mInitialized = true;
+
+ //set the target and the energy levels
+ client->setMoveTolerance(0.25f);
+ client->setEngageTarget(NULL);
+
+ mTargetObject->getWorldBox().getCenter(&mTargetPoint);
+ if (mFromLocation != Point3F(-1, -1, -1))
+ mGraphDestination = NavigationGraph::findLOSLocation(client->mLocation, mTargetPoint, mMinDistance, SphereF(mFromLocation, mMinDistance / 2.0f), mMaxDistance);
+ else
+ mGraphDestination = NavigationGraph::findLOSLocation(client->mLocation, mTargetPoint, mMinDistance, SphereF(client->mLocation, mMinDistance / 2.0f), mMaxDistance);
+
+ //if the range is small, the graph may not have enough resolution...
+ if (mMaxDistance <= 10.0f)
+ {
+ if ((mTargetPoint - mGraphDestination).len() > 10.0f)
+ mGraphDestination = mTargetPoint;
+ }
+
+ client->setMoveDestination(mGraphDestination);
+ client->setMoveMode(AIConnection::ModeExpress);
+ mStatus = InProgress;
+ }
+
+ //only check every 6 frames
+ if (--mCheckLOSCounter <= 0)
+ {
+ mCheckLOSCounter = 6;
+
+ //see if we're within range, or if we're where the graph told us to go...
+ F32 directionDot = AIConnection::get2DDot(mGraphDestination - client->mLocation, mTargetPoint - client->mLocation);
+ F32 distToTarg = (mTargetPoint - client->mLocation).len();
+ if (((distToTarg <= mMaxDistance || directionDot <= 0.0f) &&
+ (distToTarg > mMinDistance || directionDot > 0.0f)) ||
+ ((client->mLocation - mGraphDestination).len() < 8.0f))
+ {
+ Point3F aimVectorMin, aimVectorMax;
+ F32 timeMin, timeMax;
+ bool targetInRange = mProjectile->calculateAim(mTargetPoint, Point3F(0, 0, 0), client->mMuzzlePosition, client->mVelocity, &aimVectorMin, &timeMin, &aimVectorMax, &timeMax);
+ if (targetInRange)
+ {
+ //if we're shooting a ballistic projectile (outside), we don't need
+ bool playerIsOutdoors = (client->getOutdoorRadius(client->mLocation) > 0);
+ bool clearLOSToTarget = false;
+ if (mProjectile->isBallistic && playerIsOutdoors)
+ clearLOSToTarget = true;
+ else
+ {
+ //see if we have line of site
+ player->disableCollision();
+ RayInfo rayInfo;
+ Point3F startPt = client->mMuzzlePosition;
+ Point3F endPt = mTargetPoint;
+ if (! gServerContainer.castRay(startPt, endPt, mLOSMask, &rayInfo))
+ {
+ clearLOSToTarget = true;
+ }
+ else
+ {
+ //if we're here, castRay() hit something, and rayInfo should have been initialized...
+ if (bool(rayInfo.object) && rayInfo.object->getId() == mTargetObject->getId())
+ clearLOSToTarget = true;
+ }
+ player->enableCollision();
+ }
+
+ //reset the var based on LOS
+ targetInRange = clearLOSToTarget;
+ }
+
+ if (targetInRange)
+ {
+ //stop the client
+ client->setMoveMode(AIConnection::ModeStop);
+ mPrevLocation = client->mLocation;
+ if (mPrevLocation == client->mLocation)
+ mStatus = Finished;
+ else
+ mStatus = InProgress;
+ }
+ else
+ {
+ //if we're at our graph location, but still aren't within range...
+ if (((client->mLocation - mGraphDestination).len() < 8.0f) && (client->getMoveMode() == AIConnection::ModeStop))
+ {
+ //try a shorter max distance and redo the graph query
+ mMaxDistance = getMax(mMaxDistance - 10.0f, mMinDistance);
+ mInitialized = false;
+ }
+
+ //else keep moving
+ else
+ {
+ client->setMoveDestination(mGraphDestination);
+ client->setMoveMode(AIConnection::ModeExpress);
+ }
+ mStatus = InProgress;
+ }
+ }
+ else
+ {
+ //if we're at our graph location, but still aren't within range...
+ if (((client->mLocation - mGraphDestination).len() < 8.0f) && (client->getMoveMode() == AIConnection::ModeStop))
+ {
+ //try a shorter max distance and redo the graph query
+ mMaxDistance = getMax(mMaxDistance - 10.0f, mMinDistance);
+ mInitialized = false;
+ }
+
+ //else keep moving
+ else
+ {
+ client->setMoveDestination(mGraphDestination);
+ client->setMoveMode(AIConnection::ModeExpress);
+ }
+ mStatus = InProgress;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+//RangeObject step
+AIStepIdlePatrol::AIStepIdlePatrol(Point3F *idleLocation)
+{
+ mInitialized = false;
+ mIdleLocation.set(0, 0, 0);
+ if (idleLocation)
+ mIdleLocation = *idleLocation;
+ mStatus = InProgress;
+}
+
+void AIStepIdlePatrol::process(AIConnection *client, Player *player)
+{
+ Parent::process(client, player);
+ if (mStatus != InProgress)
+ return;
+
+ //make sure the target is set
+ if (! mInitialized)
+ {
+ mInitialized = true;
+
+ if (mIdleLocation == Point3F(0, 0, 0))
+ mIdleLocation = client->mLocation;
+
+ NavigationGraph::getChokePoints(mIdleLocation, mChokePoints, 25, 65);
+ mOutdoorRadius = client->getOutdoorRadius(mIdleLocation);
+
+ mIdleState = MoveToLocation;
+ mStateInit = false;
+ mMoveLocation = mIdleLocation;
+ mHeadingHome = true;
+ return;
+ }
+
+ switch (mIdleState)
+ {
+ case MoveToLocation:
+ if (! mStateInit)
+ {
+ mStateInit = true;
+ client->setMoveDestination(mMoveLocation);
+ client->setMoveMode(AIConnection::ModeExpress);
+ client->setMoveTolerance(4.0f);
+ }
+ else if (client->getPathDistRemaining(20.0f) < 8.0f)
+ {
+ client->setMoveMode(AIConnection::ModeStop);
+ mIdleState = LookAround;
+ mStateInit = false;
+ }
+ break;
+
+
+ case LookAround:
+ {
+ S32 curTime = Sim::getCurrentTime();
+ if (! mStateInit)
+ {
+ mStateInit = true;
+ mIdleNextTime = curTime + 2000 + gRandGen.randF() * 3000;
+ mIdleEndTime = curTime + 8000 + gRandGen.randF() * 8000;
+ }
+
+ //see if it's time to move somewhere
+ if (curTime > mIdleEndTime)
+ {
+ //choose a choke point
+ if (mHeadingHome)
+ {
+ mHeadingHome = false;
+ if (mChokePoints.size() == 0)
+ {
+ mMoveLocation = mIdleLocation;
+ if (mOutdoorRadius > 0.0f)
+ {
+ F32 noise = getMin(mOutdoorRadius, 30.0f);
+ mMoveLocation.x += -noise / 2 + gRandGen.randF() * noise;
+ mMoveLocation.y += -noise / 2 + gRandGen.randF() * noise;
+ }
+ }
+ else
+ {
+ mChokeIndex = S32(gRandGen.randF() * (mChokePoints.size() - 0.1f));
+ mMoveLocation = mChokePoints[mChokeIndex];
+ }
+ }
+ else
+ {
+ //40% chance of heading to the idle point
+ if (gRandGen.randF() < 0.4f || mChokePoints.size() <= 1)
+ {
+ mHeadingHome = true;
+ mMoveLocation = mIdleLocation;
+ }
+ else
+ {
+ mChokeIndex = S32(gRandGen.randF() * (mChokePoints.size() - 0.1f));
+ mMoveLocation = mChokePoints[mChokeIndex];
+ }
+ }
+
+ //set the new state
+ mIdleState = MoveToLocation;
+ mStateInit = false;
+ }
+
+ //alternate between choosing a new place to look, and pausing to look there
+ else if (curTime > mIdleNextTime)
+ {
+ mIdleNextTime = curTime + 2000 + gRandGen.randF() * 3000;
+
+ //see if we should look or play an animation
+ if (gRandGen.randF() < 0.06f)
+ {
+ player->setActionThread("pda", false, true, false);
+ }
+ else
+ {
+ Point3F lookLocation;
+ if (mChokePoints.size() == 0)
+ lookLocation = mIdleLocation;
+ else
+ {
+ S32 index = S32(gRandGen.randF() * (mChokePoints.size() + 0.9));
+ if (index == mChokePoints.size())
+ lookLocation = mIdleLocation;
+ else
+ lookLocation = mChokePoints[index];
+ }
+
+ //add some noise to the lookLocation
+ Point3F tempVector = lookLocation - client->mLocation;
+ tempVector.z = 0;
+ if (tempVector.len() < 0.001f)
+ tempVector.set(0, 1, 0);
+ tempVector.normalize();
+ tempVector *= 10.0f;
+ F32 noise = 2.0f;
+ tempVector.x += -noise + gRandGen.randF() * 2 * noise;
+ tempVector.y += -noise + gRandGen.randF() * 2 * noise;
+ tempVector.z = 1.5f + gRandGen.randF() * 2 * noise;
+ Point3F newAimLocation = client->mLocation + tempVector;
+
+ //now aim at that point
+ if (!client->scriptIsAiming())
+ client->setScriptAimLocation(newAimLocation, 3000);
+ }
+ }
+ }
+ break;
+ }
+}
diff --git a/ai/aiStep.h b/ai/aiStep.h
new file mode 100644
index 0000000..3a802e3
--- /dev/null
+++ b/ai/aiStep.h
@@ -0,0 +1,143 @@
+//-----------------------------------------------------------------------------
+// V12 Engine
+//
+// Copyright (c) 2001 GarageGames.Com
+// Portions Copyright (c) 2001 by Sierra Online, Inc.
+//-----------------------------------------------------------------------------
+
+#ifndef _AISTEP_H_
+#define _AISTEP_H_
+
+#ifndef _SIMBASE_H_
+#include "console/simBase.h"
+#endif
+#ifndef _GAMECONNECTION_H_
+#include "game/gameConnection.h"
+#endif
+#ifndef _PLAYER_H_
+#include "game/player.h"
+#endif
+
+class AIStep : public SimObject
+{
+ public:
+ enum StepResult
+ {
+ InProgress = 0,
+ Failed,
+ Finished
+ };
+ int mStatus;
+
+ protected:
+ float get2DAngle(const Point3F &endPt, const Point3F &basePt);
+
+ public:
+ AIStep();
+ int getStatus() { return mStatus; }
+ virtual void process(AIConnection *ai, Player *player);
+};
+
+class AIStepEscort : public AIStep
+{
+ typedef AIStep Parent;
+
+ bool mInitialized;
+ bool mProximityBuffer;
+ S32 mResetDestinationCounter;
+ SimObjectPtr mClientToEscort;
+
+ //add some idle vars
+ S32 mStoppedTime;
+ Vector mChokePoints;
+ bool mIdleStarted;
+ S32 mIdleNextTime;
+
+ public:
+ AIStepEscort(GameConnection *clientToEscort = NULL);
+ void process(AIConnection *ai, Player *player);
+};
+
+class AIStepEngage : public AIStep
+{
+ typedef AIStep Parent;
+
+ bool mInitialized;
+ SimObjectPtr mTarget;
+
+ S32 mStraifeCounter;
+ S32 mPauseCounter;
+ S32 mCheckLOSCounter;
+ bool mClearLOSToTarget;
+ bool mPausing;
+ Point3F mStraifeLocation;
+
+ bool mUsingEnergyWeapon;
+ float mEnergyWeaponRecharge;
+
+ bool mSearching;
+ bool mSearchInitialized;
+ Point3F mChokeLocation;
+ Vector mChokePoints;
+ S32 mChokeIndex;
+ S32 mSearchTimer;
+ bool mSearchMove;
+
+ public:
+ AIStepEngage(GameConnection *target = NULL);
+ void process(AIConnection *ai, Player *player);
+
+ void initProcessVars(Player *player, Player *targPlayer);
+ Point3F findStraifeLocation(AIConnection *client);
+};
+
+class AIStepRangeObject : public AIStep
+{
+ typedef AIStep Parent;
+
+ bool mInitialized;
+ SimObjectPtr mTargetObject;
+ ProjectileData *mProjectile;
+ U32 mLOSMask;
+
+ F32 mMinDistance;
+ F32 mMaxDistance;
+ Point3F mTargetPoint;
+ Point3F mGraphDestination;
+ Point3F mFromLocation;
+ Point3F mPrevLocation;
+ S32 mCheckLOSCounter;
+
+ public:
+ AIStepRangeObject(GameBase *targetObject = NULL, const char *projectile = NULL, F32 *minDist = NULL, F32 *maxDist = NULL, Point3F *fromLocation = NULL);
+ void process(AIConnection *ai, Player *player);
+};
+
+class AIStepIdlePatrol : public AIStep
+{
+ typedef AIStep Parent;
+
+ bool mInitialized;
+ Point3F mIdleLocation;
+ Point3F mMoveLocation;
+ Vector mChokePoints;
+ S32 mChokeIndex;
+ F32 mOutdoorRadius;
+
+ enum IdleStates
+ {
+ MoveToLocation,
+ LookAround,
+ };
+ S32 mIdleState;
+ S32 mIdleNextTime;
+ S32 mIdleEndTime;
+ bool mStateInit;
+ bool mHeadingHome;
+
+ public:
+ AIStepIdlePatrol(Point3F *idleLocation = NULL);
+ void process(AIConnection *ai, Player *player);
+};
+
+#endif
diff --git a/ai/aiTask.cc b/ai/aiTask.cc
new file mode 100644
index 0000000..30383d8
--- /dev/null
+++ b/ai/aiTask.cc
@@ -0,0 +1,235 @@
+//-----------------------------------------------------------------------------
+// V12 Engine
+//
+// Copyright (c) 2001 GarageGames.Com
+// Portions Copyright (c) 2001 by Sierra Online, Inc.
+//-----------------------------------------------------------------------------
+
+#include "console/console.h"
+#include "console/consoleInternal.h"
+#include "ai/aiTask.h"
+
+IMPLEMENT_CONOBJECT(AITask);
+
+static void cAISetWeightFreq(SimObject *obj, S32, const char **argv)
+{
+ AITask *task = static_cast(obj);
+ // task->setWeightFreq(dAtoi(argv[2]));
+ // LH - did this until proper optimizations are performed since calcWeight()
+ // was 10% of some profiles.
+ task->setWeightFreq(dAtoi(argv[2]) * 3);
+}
+
+static void cAISetWeight(SimObject *obj, S32, const char **argv)
+{
+ AITask *task = static_cast(obj);
+ task->setWeight(dAtoi(argv[2]));
+}
+
+static const char* cAIGetWeight(SimObject *obj, S32, const char **)
+{
+ AITask *task = static_cast(obj);
+ S32 weight = task->getWeight();
+
+ char* returnBuffer = Con::getReturnBuffer(256);
+ dSprintf(returnBuffer, 256, "%d", weight);
+ return returnBuffer;
+}
+
+static void cAIReWeight(SimObject *obj, S32, const char **)
+{
+ AITask *task = static_cast(obj);
+ task->reWeight();
+}
+
+static void cAISetMonitorFreq(SimObject *obj, S32, const char **argv)
+{
+ AITask *task = static_cast(obj);
+ task->setMonitorFreq(dAtoi(argv[2]));
+}
+
+static void cAIReMonitor(SimObject *obj, S32, const char **)
+{
+ AITask *task = static_cast(obj);
+ task->reMonitor();
+}
+
+void AITask::consoleInit()
+{
+ Con::addCommand("AITask", "setWeightFreq", cAISetWeightFreq, "ai.setWeightFreq(freq)", 3, 3);
+ Con::addCommand("AITask", "setWeight", cAISetWeight, "ai.setWeight(weight)", 3, 3);
+ Con::addCommand("AITask", "getWeight", cAIGetWeight, "ai.getWeight()", 2, 2);
+ Con::addCommand("AITask", "reWeight", cAIReWeight, "ai.reWeight()", 2, 2);
+
+ Con::addCommand("AITask", "setMonitorFreq", cAISetMonitorFreq, "ai.setMonitorFreq(freq)", 3, 3);
+ Con::addCommand("AITask", "reMonitor", cAIReMonitor, "ai.reMonitor()", 2, 2);
+}
+
+AITask::AITask()
+{
+ mWeightFreq = 30;
+ mMonitorFreq = 30;
+ mWeightCounter = 0;
+ mMonitorCounter = 0;
+ mWeight = 0;
+}
+
+bool AITask::onAdd()
+{
+ if (! Parent::onAdd())
+ return false;
+
+ //set the task namespace
+ const char *name = getName();
+ if(name && name[0] && getClassRep())
+ {
+ Namespace *parent = getClassRep()->getNameSpace();
+ Con::linkNamespaces(parent->mName, name);
+ mNameSpace = Con::lookupNamespace(name);
+ }
+
+ return true;
+}
+
+void AITask::calcWeight(AIConnection *ai)
+{
+ //make sure task is valid
+ if (! ai)
+ return;
+
+ //see if it's time to re-weight the task
+ // if (--mWeightCounter <= 0)
+ if (gCalcWeightSlicer.ready(mWeightCounter, mWeightFreq))
+ {
+ // mWeightCounter = mWeightFreq;
+ Con::executef(this, 2, "weight", avar("%d", ai->getId()));
+ }
+}
+
+void AITask::monitor(AIConnection *ai)
+{
+ //make sure task is valid
+ if (! ai)
+ return;
+
+ if (--mMonitorCounter <= 0)
+ {
+ mMonitorCounter = mMonitorFreq;
+ Con::executef(this, 2, "monitor", avar("%d", ai->getId()));
+ }
+}
+
+void AITask::assume(AIConnection *ai)
+{
+ //make sure task is valid
+ if (! ai)
+ return;
+
+ mMonitorCounter = 0;
+ Con::executef(this, 2, "assume", avar("%d", ai->getId()));
+}
+
+void AITask::retire(AIConnection *ai)
+{
+ //make sure task is valid
+ if (! ai)
+ return;
+
+ Con::executef(this, 2, "retire", avar("%d", ai->getId()));
+}
+
+//-------------------------------------------------------------------------------------
+
+AISlicer::AISlicer()
+{
+ reset();
+}
+
+void AISlicer::init(U32 delay, S32 maxPerTic)
+{
+ // mRand.setSeed(0x88);
+ mDelay = delay;
+ mLastTime = Sim::getCurrentTime();
+ // mCapPileUp = (getMax(maxPerTic - 1, 0) * mDelay);
+ maxPerTic;
+ mDebugTotal = 0;
+ mBudget = 2.0;
+ // mEnabled = true;
+}
+
+void AISlicer::reset()
+{
+ init();
+ // mEnabled = false;
+}
+
+#if 0
+// Return true if this counter goes to zero AND sufficient time has elapsed. If
+// the time hasn't elapsed - leave the counter. This should ensure that every
+// body gets their shot without super-high frequency guys shutting out slow guys.
+bool AISlicer::ready(S32& counter, S32 resetValue)
+{
+ U32 T = Sim::getCurrentTime();
+
+ if (T - mLastTime >= mDelay && --counter <= 0)
+ {
+ // 1/4 of time, add stagger noise to those that won't really notice it
+ if (resetValue > 3)
+ resetValue += !(mRand.randI() & 3);
+ counter = resetValue;
+
+ // We want to allow multiple times per tic, but also to cap how much "reserve"
+ // readiness is allowed to pile up.
+ mLastTime = getMax(mLastTime + mDelay, T - mCapPileUp);
+
+ // Verify it's handling more than one per tick Ok
+ mDebugTotal++;
+
+ return true;
+ }
+ return false;
+}
+#else
+
+// The above is very mis-conceived, let's try something a little better-
+// Note mDelay is a millisecond amount, while counter & reset value are game ticks.
+bool AISlicer::ready(S32& counter, S32 resetValue)
+{
+ bool goodToGo = false;
+
+ counter--;
+
+ if (mBudget > 0)
+ {
+ if (counter < 0)
+ {
+ mBudget -= 1.0;
+ counter = resetValue;
+ goodToGo = true;
+ }
+ }
+ else
+ {
+ // Budget keeps track of how many we currently are allowed to let through. We'd
+ // like to keep it to no more than one per mDelay milliseconds.
+ U32 T = Sim::getCurrentTime();
+ mBudget += (F32(T - mLastTime) / F32(mDelay));
+ mBudget = getMin(mBudget, 40.0f);
+ mLastTime = T;
+
+ // We have to let through tasks that have been waiting a while. Nothing will
+ // wait in vain for more than about 1.2 extra seconds-
+ if (counter < -37)
+ {
+ counter = resetValue;
+ goodToGo = true;
+ }
+ }
+
+ // Track total to make sure we're getting about the percentage that we want.
+ mDebugTotal += goodToGo;
+
+ return goodToGo;
+}
+
+#endif
diff --git a/ai/aiTask.h b/ai/aiTask.h
new file mode 100644
index 0000000..a1a961d
--- /dev/null
+++ b/ai/aiTask.h
@@ -0,0 +1,55 @@
+//-----------------------------------------------------------------------------
+// V12 Engine
+//
+// Copyright (c) 2001 GarageGames.Com
+// Portions Copyright (c) 2001 by Sierra Online, Inc.
+//-----------------------------------------------------------------------------
+
+#ifndef _AITASK_H_
+#define _AITASK_H_
+
+#ifndef _SIMBASE_H_
+#include "console/simBase.h"
+#endif
+#ifndef _AICONNECTION_H_
+#include "ai/aiConnection.h"
+#endif
+#ifndef _PLAYER_H_
+#include "Test/player.h"
+#endif
+
+class AITask : public SimObject
+{
+ typedef SimObject Parent;
+
+ S32 mWeightFreq;
+ S32 mWeightCounter;
+
+ S32 mMonitorFreq;
+ S32 mMonitorCounter;
+
+ S32 mWeight;
+
+ public:
+ DECLARE_CONOBJECT(AITask);
+ static void consoleInit();
+
+ AITask();
+ bool onAdd();
+
+ void setWeightFreq(S32 freq) { mWeightFreq = getMax(1, freq); }
+ void setWeight(S32 weight) { mWeight = weight; }
+ void reWeight() { mWeightCounter = 0; }
+ S32 getWeight() { return mWeight; }
+
+ void setMonitorFreq(S32 freq) { mMonitorFreq = getMax(1, freq); }
+ void reMonitor() { mMonitorCounter = 0; }
+
+ //methods to call the script functions
+ void calcWeight(AIConnection *ai);
+ void monitor(AIConnection *ai);
+ void assume(AIConnection *ai);
+ void retire(AIConnection *ai);
+};
+
+#endif
diff --git a/ai/graph.cc b/ai/graph.cc
new file mode 100644
index 0000000..c0d19a1
--- /dev/null
+++ b/ai/graph.cc
@@ -0,0 +1,1528 @@
+//-----------------------------------------------------------------------------
+// V12 Engine
+//
+// Copyright (c) 2001 GarageGames.Com
+// Portions Copyright (c) 2001 by Sierra Online, Inc.
+//-----------------------------------------------------------------------------
+
+#include "ai/graph.h"
+#include "console/console.h"
+#include "console/consoleTypes.h"
+#include "core/fileStream.h"
+#include "ai/graphFloorPlan.h"
+#include "terrain/terrRender.h"
+#include "game/player.h"
+#include "game/gameConnection.h"
+#include "ai/graphLOS.h"
+
+IMPLEMENT_CONOBJECT(NavigationGraph);
+
+//-------------------------------------------------------------------------------------
+
+NavigationGraph * gNavGraph = NULL;
+NavGraphGlobals gNavGlobs;
+S32 NavigationGraph::mIncarnation = 0;
+bool NavigationGraph::sDrawOutdoorNodes = true;
+bool NavigationGraph::sDrawIndoorNodes = true;
+bool NavigationGraph::sDrawJetConnections = true;
+bool NavigationGraph::sSeedDropOffs = false;
+F32 NavigationGraph::sProcessPercent = 0.0f;
+S32 NavigationGraph::sTotalEdgeCount = 0;
+S32 NavigationGraph::sEdgeRenderMaxOutdoor = 300;
+S32 NavigationGraph::sEdgeRenderMaxIndoor = 300;
+U32 NavigationGraph::sLoadMemUsed = 0;
+S32 NavigationGraph::sProfCtrl0 = 0;
+S32 NavigationGraph::sProfCtrl1 = 0;
+S32 NavigationGraph::sShowThreatened = -1;
+static const char sSpawnPath[] = "terrains/%s.spn";
+static const char sRegularPath[] = "terrains/%s.nav";
+
+//-------------------------------------------------------------------------------------
+
+// Version history. Only from ChangedToFloorPlan on is now supported. I'll leave the
+// list here for aetiological reasons... :)
+enum Updates {
+ TerrainOnly,
+ HandLaidInterior,
+ AddedBridges,
+ AddedPathTable,
+ AddedLOSTable,
+ ChangedToFloorPlan,
+ TrimmedBridges,
+ RevisedLOSToHash,
+ BetterSpawnMode
+ // AddedChuteHints
+};
+
+S32 NavigationGraph::sVersion = BetterSpawnMode;
+
+//-------------------------------------------------------------------------------------
+
+static const char * getGraphName(char * fileBuf, S32 bufSz, bool spawn)
+{
+ const char * name = Con::getVariable("CurrentMission");
+ if (spawn)
+ dSprintf(fileBuf, bufSz, sSpawnPath, name);
+ else
+ dSprintf(fileBuf, bufSz, sRegularPath, name);
+ return fileBuf;
+}
+
+//-------------------------------------------------------------------------------------
+
+NavigationGraph::NavigationGraph()
+ : mMaxTransients(AbsMaxBotCount * 2),
+ mCustomArea(0,0,0,0)
+{
+ AssertFatal(!gNavGraph, "May not create more than one NavigationGraph!");
+ gNavGraph = this;
+ mNumOutdoor = 0;
+ mNumIndoor = 0;
+ mTransientStart = -1;
+ mCullDensity = 0.3f;
+ mLargestIsland = -1;
+ mPushedBridges = 0;
+ mTableBuilder = NULL;
+ mMainSearcher = NULL;
+ mLOSSearcher = NULL;
+ mDistSearcher = NULL;
+ mHaveVolumes = false;
+ mValidLOSTable = false;
+ mValidPathTable = false;
+ mIsSpawnGraph = false;
+ mCheckNode = 0;
+ mDeadlyLiquid = false;
+ mSubmergedScale = 1.0;
+ mShoreLineScale = 1.0;
+ mEdgePool = NULL;
+ mOutdoorNodes = NULL;
+ mLOSTable = NULL;
+ mVersion = -1;
+ sLoadMemUsed = 0;
+ setGenMagnify(0, 0, 0);
+}
+
+NavigationGraph::~NavigationGraph()
+{
+ if (mTransientStart != -1)
+ for (S32 j = 0; j < mMaxTransients; j++)
+ if (GraphNode * transientNode = mNodeList[mTransientStart + j])
+ delete transientNode;
+
+ newIncarnation(); // (This deletes the searchers)
+
+ delete [] mEdgePool;
+ delete [] mOutdoorNodes;
+
+ gNavGraph = NULL;
+}
+
+bool NavigationGraph::onAdd()
+{
+ U32 memUsedBefore = Memory::getMemoryUsed();
+
+ // Temp to avoid old small devs causing huge graph builds-
+ mConjoin.maxAngleDev = getMax(mConjoin.maxAngleDev, 45.0f);
+
+ if (!Parent::onAdd())
+ return false;
+
+ mTerrainBlock = GroundPlan::getTerrainObj();
+
+ // DMMNOTPRESENT
+// if (TerrainRender::mLiquidType >= 4) {
+// mDeadlyLiquid = true;
+// mSubmergedScale = 1000.0;
+// mShoreLineScale = 2.0;
+// }
+// else {
+ mDeadlyLiquid = false;
+ mSubmergedScale = 3.7;
+ mShoreLineScale = 1.41;
+// }
+
+ bool forceNavLoad = Con::getBoolVariable("$GraphForceLoad");
+
+ if (forceNavLoad)
+ {
+ // Need to insure presence of NAV due to how code works below...
+ char txt[256];
+ if (Stream * S = ResourceManager->openStream(getGraphName(txt, sizeof(txt), 0)))
+ ResourceManager->closeStream(S);
+ else
+ forceNavLoad = false;
+ }
+
+ if (Con::getBoolVariable("$OFFLINE_NAV_BUILD"))
+ forceNavLoad = true;
+
+ // If we're not in a single player game, we check HostGameBotCount to see
+ // if we want to skip NAV (if one is even there). If we are in SinglePlayer,
+ // a NAV file is required.
+ mIsSpawnGraph = false;
+ if (!forceNavLoad)
+ {
+ const char * missionType = Con::getVariable("CurrentMissionType");
+
+ // If not single player, then check the bot count.
+ // If it is single player, we need the graph.
+ if (dStricmp(missionType, "SinglePlayer"))
+ mIsSpawnGraph = !Con::getIntVariable("HostGameBotCount");
+ }
+
+ if (loadGraph())
+ {
+ if (!mIsSpawnGraph)
+ {
+ makeGraph(true);
+ pushBridges();
+ clearLoadData(); // remove load data not needed at run time.
+ }
+ }
+
+ sLoadMemUsed = Memory::getMemoryUsed() - memUsedBefore;
+ Con::printf("Memory consumed = %d", sLoadMemUsed);
+
+ return true;
+}
+
+void NavigationGraph::onRemove()
+{
+ Parent::onRemove();
+}
+
+void NavigationGraph::clearLoadData()
+{
+ mEdgeInfoList.clear(); mEdgeInfoList.compact();
+ mNodeInfoList.clear(); mNodeInfoList.compact();
+ mBridgeList.clear(); mBridgeList.compact();
+ mTerrainInfo.consolidated.clear(); mTerrainInfo.consolidated.compact();
+ mTerrainInfo.shadowHeights.clear(); mTerrainInfo.shadowHeights.compact();
+}
+
+// Called before saving on spawn graphs- clean out all unneeded data.
+void NavigationGraph::purgeForSpawn()
+{
+ mEdgeInfoList.clear(); mEdgeInfoList.compact();
+ mBridgeList.clear(); mBridgeList.compact();
+ // mNodeVolumes.clear(); mNodeVolumes.compact();
+ if (mLOSTable)
+ mLOSTable->clear();
+}
+
+// Called when new graph is made. Incarnation # used so bots can (theoretically)
+// run around which graph editing is taking place.
+void NavigationGraph::newIncarnation()
+{
+ mIncarnation = mIncarnation + 1;
+
+ // Searchers-
+ delete mTableBuilder;
+ delete mMainSearcher;
+ delete mLOSSearcher;
+ delete mDistSearcher;
+ mTableBuilder = NULL;
+ mMainSearcher = NULL;
+ mLOSSearcher = NULL;
+ mDistSearcher = NULL;
+
+ mJetManager.clear();
+}
+
+//-------------------------------------------------------------------------------------
+
+// Print size of anything which supports memSize(), accumulate total.
+#define PrintMemSize(V, msg) { \
+ Con::printf("%s: usage = %d", msg, V.memSize()); \
+ total += V.memSize(); }
+
+// Try to account for all contributors to the overall footprint-
+// (This isn't complete yet)-
+U32 NavigationGraph::reckonMemory() const
+{
+ // TerrainGraphInfo mTerrainInfo.memSize();
+ // ChuteHints mChutes;
+ // OutdoorNode * mOutdoorNodes;
+ // GraphBSPTree mIndoorTree;
+ // GraphEdge * mEdgePool;
+ U32 total = 0;
+ PrintMemSize(mNodeList, "mNodeList"); // GraphNodeList
+ PrintMemSize(mNodeGrid, "mNodeGrid"); // GraphNodeList
+ PrintMemSize(mNonTransient, "mNonTransient"); // GraphNodeList
+ PrintMemSize(mIndoorPtrs, "mIndoorPtrs"); // GraphNodeList
+ PrintMemSize(mIndoorNodes, "mIndoorNodes"); // IndoorNodeList
+ PrintMemSize(mIslandPtrs, "mIslandPtrs"); // GraphNodeList
+ PrintMemSize(mIslandSizes, "mIslandSizes"); // Vector
+ PrintMemSize(mBoundaries, "mBoundaries"); // GraphBoundaries
+ PrintMemSize(mNodeVolumes, "mNodeVolumes"); // GraphVolumeList
+
+ PrintMemSize(mRenderThese, "mRenderThese"); // Vector
+ PrintMemSize(mRenderBoxes, "mRenderBoxes"); // Vector
+ PrintMemSize(mTempNodeBuf, "mTempNodeBuf"); // Vector
+ PrintMemSize(mVisibleEdges, "mVisibleEdges"); // GraphEdgePtrs
+ // SearchThreats mThreats;
+ // MonitorForceFields mForceFields;
+ // JetManager mJetManager;
+ Con::printf("*** Total accounted for = %d", total);
+ return total;
+}
+
+//-------------------------------------------------------------------------------------
+
+void NavigationGraph::checkHashTable(S32 nodeCount) const
+{
+ #if 0
+ S32 accum[4] = {0, 0, 0, 0};
+ S32 errCount = 0;
+
+ for (S32 i = 0; i < nodeCount; i++) {
+ for (S32 j = 0; j < nodeCount; j++) {
+ U32 vOld = mLOSXRef.value(i, j);
+ U32 vNew = mLOSHashTable.value(i, j);
+ if (vOld != vNew) {
+ errCount++;
+ Con::printf("LOSHash Error at %d <-> %d", i, j);
+ }
+ else {
+ AssertFatal(vOld < 4, "Bad LOS hash table entry value");
+ accum[vOld]++;
+ }
+ }
+ }
+
+ if (errCount)
+ Con::printf("There were %d errors in conversion of LOS table", errCount);
+ for (S32 d = 0; d < 4; d++)
+ Con::printf("LOS distribution for %d = %d", d, accum[d]);
+ #else
+ nodeCount;
+ #endif
+}
+
+// From original XREF data, convert to hash version.
+U32 NavigationGraph::makeLOSHashTable()
+{
+ U32 memUsage = 0;
+ S32 nodeCount = numNodes();
+
+ memUsage = mLOSHashTable.convertTable(mLOSXRef, nodeCount);
+ checkHashTable(nodeCount);
+ mLOSXRef.clear();
+ mLOSTable = & mLOSHashTable;
+
+ return memUsage;
+}
+
+//-------------------------------------------------------------------------------------
+
+GraphSearch * NavigationGraph::getMainSearcher()
+{
+ if (!mMainSearcher)
+ mMainSearcher = new GraphSearch();
+ return mMainSearcher;
+}
+
+GraphSearchLOS * NavigationGraph::getLOSSearcher()
+{
+ if (!mLOSSearcher)
+ mLOSSearcher = new GraphSearchLOS();
+ return mLOSSearcher;
+}
+
+GraphSearchDist * NavigationGraph::getDistSearcher()
+{
+ if (!mDistSearcher)
+ mDistSearcher = new GraphSearchDist();
+ return mDistSearcher;
+}
+
+//-------------------------------------------------------------------------------------
+
+bool NavigationGraph::gotOneWeCanUse() // i.e. for navigation (not spawning)
+{
+ if (gNavGraph) {
+ if (gNavGraph->mNumOutdoor || gNavGraph->mNumIndoor)
+ return true;
+ }
+ return false;
+}
+
+bool NavigationGraph::hasSpawnLocs()
+{
+ return (gNavGraph && gNavGraph->mSpawnList.size());
+}
+
+bool NavigationGraph::customArea(GridArea& areaOut) const
+{
+ if (mCustomArea.extent.x > 0 && mCustomArea.extent.y > 0) {
+ areaOut = mCustomArea;
+ return true;
+ }
+ return false;
+}
+
+// Practically speaking, warnings might be part of graph fine tuning until Beta.
+void NavigationGraph::warning(const char* message)
+{
+ message;
+#ifdef DEBUG
+ static S32 warnCount = 0;
+ Con::printf("GraphWarning #%d! %s", ++warnCount, message);
+#endif
+}
+
+void NavigationGraph::initPersistFields()
+{
+ Parent::initPersistFields();
+
+ // Slope deviation at which terrain squares are considered flat.
+ addField("conjoinAngleDev", TypeF32, Offset(mConjoin.maxAngleDev, NavigationGraph));
+
+ // ratio of largest island to total nodes:
+ addField("cullDensity", TypeF32, Offset(mCullDensity, NavigationGraph));
+
+ // If user doesn't want the mission area as default-
+ addField("customArea", TypeRectI, Offset(mCustomArea, NavigationGraph));
+}
+
+//-------------------------------------------------------------------------------------
+// GRAPH LOAD & SAVE.
+
+bool NavigationGraph::loadGraph()
+{
+ bool Ok = false;
+ char fileBuf[256];
+
+ getGraphName(fileBuf, sizeof(fileBuf), mIsSpawnGraph);
+
+ // Load if found-
+ if (Stream * stream = ResourceManager->openStream(fileBuf))
+ {
+ if (! load(* stream, mIsSpawnGraph))
+ Con::printf("loadGraph: Failed to load '%s'", fileBuf);
+ else
+ Ok = true;
+ ResourceManager->closeStream(stream);
+
+ if (Ok && !mIsSpawnGraph)
+ {
+ // NAV graph needs the spawn data. I know, this isn't all that organized...
+ getGraphName(fileBuf, sizeof(fileBuf), true);
+ if (Stream * spawnStream = ResourceManager->openStream(fileBuf))
+ {
+ if (!load(* spawnStream, true))
+ Con::printf("Error loading spawn file into NAV graph");
+ ResourceManager->closeStream(spawnStream);
+ }
+ else
+ Con::printf("loadGraph: NAV Ok, but SPN open failed (%s)", fileBuf);
+ }
+ }
+ else
+ Con::printf("loadGraph: Couldn't open '%s' for read", fileBuf);
+
+ return Ok;
+}
+
+bool NavigationGraph::saveGraph()
+{
+ FileStream fStream;
+ bool Ok = false;
+ char fileBuf[256];
+
+ getGraphName(fileBuf, sizeof(fileBuf), mIsSpawnGraph);
+
+ if (ResourceManager->openFileForWrite(fStream, ResourceManager->getModPathOf(fileBuf), fileBuf))
+ {
+ if (mIsSpawnGraph)
+ {
+ makeSpawnList();
+ purgeForSpawn();
+ }
+ if (!save(fStream))
+ Con::printf("saveGraph: Failed to save '%s'", fileBuf);
+ else
+ Ok = true;
+ fStream.close();
+
+ // call scripts that were done.
+ Con::executef(this, 1, "navBuildComplete");
+ }
+ else
+ Con::printf("saveGraph: Couldn't open '%s' for write", fileBuf);
+
+ return Ok;
+}
+
+//-------------------------------------------------------------------------------------
+// GRAPH STREAM PERSISTENCE
+
+bool NavigationGraph::load(Stream & s, bool isSpawn)
+{
+ bool Ok = true;
+
+ Ok &= s.read(&mVersion);
+
+ // Some reserved-
+ S32 res;
+ Ok &= (s.read(&res) && s.read(&res) && s.read(&res) && s.read(&res));
+
+ // The reduced spawn list-
+ if (mVersion >= BetterSpawnMode)
+ {
+ Ok &= mSpawnList.read(s);
+
+ // Before we're loaded, this condition signals that graph is just a spawn list.
+ // After loaded, non-empty mSpawnList is the condition....
+ if (isSpawn)
+ return Ok;
+ else
+ mSpawnList.reset();
+ }
+
+ // Read edges & nodes/volumes. Volume list is separate from indoor node list only
+ // because of order things were developed in. Otherwise they are one-to-one.
+ Ok &= readVector1(s, mEdgeInfoList);
+ Ok &= readVector1(s, mNodeInfoList);
+ Ok &= mNodeVolumes.read(s);
+
+ // Outside-
+ Ok &= mTerrainInfo.read(s);
+
+ // Bridges-
+ AssertISV(mVersion >= TrimmedBridges, "Graph needs rebuilt for this mission");
+ Ok &= mBridgeList.read(s);
+ // else
+ // Ok &= mBridgeList.readOld(s);
+
+ // Tables-
+ if(mVersion >= AddedPathTable)
+ Ok &= mPathXRef.read(s);
+
+ if(mVersion >= AddedLOSTable)
+ {
+ if (mVersion >= RevisedLOSToHash)
+ {
+ Ok &= mLOSHashTable.read(s);
+ mLOSTable = & mLOSHashTable;
+ }
+ else
+ {
+ Ok &= mLOSXRef.read(s);
+ if (sVersion >= RevisedLOSToHash)
+ {
+ // Temporary conversion code
+ S32 numNodes = mNodeInfoList.size() + mTerrainInfo.consolidated.size();
+ if (mLOSXRef.valid(numNodes))
+ {
+ mLOSHashTable.convertTable(mLOSXRef, numNodes);
+ checkHashTable(numNodes);
+ mLOSXRef.clear();
+ mLOSTable = & mLOSHashTable;
+ }
+ }
+ else
+ mLOSTable = & mLOSXRef;
+ }
+ }
+
+ // if(mVersion >= AddedChuteHints)
+ // Ok &= mChutes.read(s);
+
+ return Ok;
+}
+
+bool NavigationGraph::save(Stream & s)
+{
+ bool Ok = true;
+
+ Ok &= s.write(sVersion);
+
+ // Reserveds-
+ S32 res = 0;
+ Ok &= (s.write(res) && s.write(res) && s.write(res) && s.write(res));
+
+ if (sVersion >= BetterSpawnMode)
+ {
+ if (!mIsSpawnGraph)
+ mSpawnList.reset();
+ Ok &= mSpawnList.write(s);
+ if (mIsSpawnGraph)
+ return Ok;
+ }
+
+ // Indoors-
+ Ok &= writeVector1(s, mEdgeInfoList);
+ Ok &= writeVector1(s, mNodeInfoList);
+ Ok &= mNodeVolumes.write(s);
+
+ // Outside-
+ Ok &= mTerrainInfo.write(s);
+
+ // Bridges-
+ Ok &= mBridgeList.write(s);
+
+ // Tables-
+ Ok &= mPathXRef.write(s);
+
+ // Write out the table-
+ if (sVersion < RevisedLOSToHash)
+ Ok &= mLOSXRef.write(s);
+ else
+ Ok &= mLOSHashTable.write(s);
+
+ // Chute hints-
+ // Ok &= mChutes.write(s);
+
+ return Ok;
+}
+
+//-------------------------------------------------------------------------------------
+// Graph Console Functions
+//-------------------------------------------------------------------------------------
+
+static bool cSaveGraph(SimObject *ptr, S32, const char **)
+{
+ NavigationGraph *navGraph = static_cast(ptr);
+ return navGraph->saveGraph();
+}
+
+//-------------------------------------------------------------------------------------
+
+static bool cSetGround(SimObject *ptr, S32, const char **argv)
+{
+ NavigationGraph * navGraph = static_cast(ptr);
+ GroundPlan * gp = dynamic_cast(Sim::findObject(argv[2]));
+
+ if (!gp)
+ Con::printf("Couldn't find ground plan %s", argv[2]);
+ else
+ return navGraph->setGround(gp);
+
+ return false;
+}
+
+//-------------------------------------------------------------------------------------
+
+static bool cMakeGraph(SimObject *ptr, S32, const char **)
+{
+ NavigationGraph * navGraph = static_cast(ptr);
+ navGraph->makeGraph(false);
+ return true;
+}
+
+//-------------------------------------------------------------------------------------
+
+static bool cLoadGraph(SimObject *ptr, S32, const char **)
+{
+ NavigationGraph *navGraph = static_cast(ptr);
+ return navGraph->loadGraph();
+}
+
+//-------------------------------------------------------------------------------------
+// LOS XRef table construction.
+
+static bool cPrepLOS(SimObject * ptr, S32 argc, const char **argv)
+{
+ Point3F viewLoc(0,0,100);
+ if (argc == 3)
+ dSscanf(argv[2], "%f %f %f", &viewLoc.x, &viewLoc.y, &viewLoc.z);
+ NavigationGraph *navGraph = static_cast(ptr);
+ return navGraph->prepLOSTableWork(viewLoc);
+}
+
+static bool cMakeLOS(SimObject * ptr, S32, const char **)
+{
+ NavigationGraph *navGraph = static_cast(ptr);
+ return navGraph->makeLOSTableEntries();
+}
+
+//-------------------------------------------------------------------------------------
+// Bridge building and management
+
+// Processing routine - just builds the data
+static bool cFindBridges(SimObject *ptr, S32, const char **)
+{
+ NavigationGraph *navGraph = static_cast(ptr);
+ if (const char * errorText = navGraph->findBridges())
+ {
+ Con::printf(errorText);
+ return false;
+ }
+ return true;
+}
+
+// Installs the edges onto the nodes of a made graph
+static bool cPushBridges(SimObject *ptr, S32, const char **)
+{
+ NavigationGraph *navGraph = static_cast(ptr);
+
+ if (S32 islandsBefore = navGraph->numIslands())
+ {
+ if (const char * errorText = navGraph->pushBridges())
+ Con::printf(errorText);
+ else
+ {
+ if (S32 islandsBridged = islandsBefore - navGraph->numIslands())
+ Con::printf("%d islands have been bridged", islandsBridged);
+ return true;
+ }
+ }
+ else
+ Con::printf("Graph hasn't been made");
+
+ return false;
+}
+
+//-------------------------------------------------------------------------------------
+
+// Try to cull out dense clusters of nodes. Pass in a param if you want to
+// cull out everything except biggest island.
+static bool cCullIslands(SimObject *ptr, S32, const char **)
+{
+ NavigationGraph *navGraph = static_cast(ptr);
+ navGraph->cullIslands();
+ return true;
+}
+
+//-------------------------------------------------------------------------------------
+
+static bool cMakeTables(SimObject * ptr, S32, const char* * )
+{
+ NavigationGraph * navGraph = static_cast(ptr);
+ navGraph->makeTables();
+ return true;
+}
+
+//-------------------------------------------------------------------------------------
+
+static bool cAssemble(SimObject * ptr, S32, const char* *)
+{
+ NavigationGraph * navGraph = static_cast(ptr);
+ return navGraph->assemble();
+}
+
+//-------------------------------------------------------------------------------------
+
+static S32 cNumNodes(SimObject* ptr, S32, const char* [])
+{
+ NavigationGraph * navGraph = static_cast(ptr);
+ return navGraph->numNodes();
+}
+
+static bool cSetGenMode(SimObject* ptr, S32 argc, const char* argv[])
+{
+ if (argc == 3) {
+ NavigationGraph * navGraph = static_cast(ptr);
+ navGraph->setGenMode(!dStricmp(argv[2], "spawn"));
+ }
+ else
+ Con::printf("Set graph generation mode to Nav (default) or Spawn");
+ return true;
+}
+
+//-------------------------------------------------------------------------------------
+
+static S32 cRandNode(SimObject* ptr, S32 argc, const char* argv[])
+{
+ NavigationGraph * navGraph = static_cast(ptr);
+
+ if (argc >= 4)
+ {
+ Point3F pt;
+ dSscanf(argv[2], "%f %f %f", &pt.x, &pt.y, &pt.z);
+ F32 radius = dAtoi(argv[3]);
+ bool inside = argc > 4 ? dAtob(argv[4]) : false;
+ bool outside = argc > 5 ? dAtob(argv[5]) : false;
+
+ return navGraph->randNode(pt, radius, inside, outside);
+ }
+ else
+ {
+ Con::printf ("Given a point, radius, and flags for indoor or outdoor");
+ Con::printf ("inclusion, %s returns a node index (-1 if not found).", argv[1]);
+ Con::printf ("Note use navGraph.nodeLoc() to get location from index");
+ }
+ return -1;
+}
+
+static const char bogusLocation[] = "0 0 500";
+
+static const char * cNodeLoc(SimObject* ptr, S32 argc, const char* argv[])
+{
+ NavigationGraph * navGraph = static_cast(ptr);
+
+ if (argc == 3)
+ {
+ S32 nodeIndex =dAtoi(argv[2]);
+
+ if (const Point3F * pt = navGraph->getSpawnLoc(nodeIndex))
+ {
+ char * buff = Con::getReturnBuffer(100);
+ dSprintf(buff, 100, "%f %f %f", pt->x, pt->y, pt->z);
+ return buff;
+ }
+ else
+ Con::printf("Invalid index (%d) passed to nodeLoc()", nodeIndex);
+ }
+ else
+ Con::printf("Gets the location of the specified node or spawn index");
+
+ return bogusLocation;
+}
+
+// Go up slightly and cast down to ground.
+static void adjustSpawnLoc(Point3F & point)
+{
+ point.z += 1.3;
+ Loser cast(-1);
+ if (cast.hitBelow(point, 20.0))
+ {
+ Point2F sideways(cast.mColl.normal.x, cast.mColl.normal.y);
+ point.z += sideways.len() / 2.0;
+ point.z += 0.2; // <<=== some shapes have problems... cf. ThinInce
+ }
+}
+
+static const char * cRandNodeLoc(SimObject* ptr, S32 argc, const char* argv[])
+{
+ NavigationGraph * navGraph = static_cast(ptr);
+
+ if (argc == 3)
+ {
+ S32 nodeIndex =dAtoi(argv[2]);
+
+ if (const Point3F * pt = navGraph->getRandSpawnLoc(nodeIndex))
+ {
+ // if (NavigationGraph::sProfCtrl0 == 337) {
+ // // Testing steepness on Escalade- want spawns near high building-
+ // static U32 stagger = 0;
+ // static Point3F testLocs[5] = {
+ // Point3F(438, -42, 297),
+ // Point3F(434.4, -53.5, 270),
+ // Point3F(428, -45, 264),
+ // Point3F(463, -14, 264),
+ // Point3F(473, -44, 223)
+ // };
+ // pt = &testLocs[stagger++ % 5];
+ // }
+
+ Point3F point = * pt;
+ adjustSpawnLoc(point);
+ char * buff = Con::getReturnBuffer(100);
+ dSprintf(buff, 100, "%f %f %f", point.x, point.y, point.z);
+ return buff;
+ }
+ else
+ Con::printf("Invalid index (%d) passed to %s()", nodeIndex, argv[1]);
+ }
+ else
+ Con::printf("Finds a random location within the space of the given node");
+
+ return bogusLocation;
+}
+
+// Get a good direction to face, given a location.
+static const char * cWhereToLook(SimObject* , S32 argc, const char* argv[])
+{
+ if (argc >= 2) {
+ Point3F point;
+ dSscanf(argv[1], "%f %f %f", &point.x, &point.y, &point.z);
+
+ // We get a Z rotation back-
+ F32 angle = - NavigationGraph::whereToLook(point);
+
+ // Build our return string for use by setTransform().
+ char * buff = Con::getReturnBuffer(120);
+ dSprintf(buff, 120, "%f %f %f 0 0 1 %f", point.x, point.y, point.z, angle);
+ return buff;
+ }
+ else {
+ Con::printf ("Given a point, return a transform in a 'nice' direction to look.");
+ return NULL;
+ }
+}
+
+static bool cNavGraphExists(SimObject*, S32, const char **)
+{
+ return (NavigationGraph::gotOneWeCanUse() || NavigationGraph::hasSpawnLocs());
+}
+
+static void cDumpInfo2File(SimObject *ptr, S32, const char**)
+{
+ NavigationGraph * ng = static_cast(ptr);
+
+ #define printDump(a) {const char * b = a; LogFile.write(dStrlen(b),b);}
+
+ FileStream LogFile;
+
+ LogFile.open("NavMetrics.log", FileStream::ReadWrite);
+
+ if(LogFile.getStatus() == Stream::Ok)
+ {
+ LogFile.setPosition(LogFile.getStreamSize());
+
+ printDump("\r\n\r\n");
+ // printDump(avar("Mission: %s", ng->mGraphFile));
+ printDump("---------------------------\r\n");
+ printDump(avar("Graph Stats: %d nodes (%d outdoor)\r\n",
+ ng->numNodes(), ng->numOutdoor()));
+ printDump(avar("--> %d bridges\r\n", ng->numBridges()));
+ printDump(avar("--> %d edges\r\n", NavigationGraph::sTotalEdgeCount));
+ printDump(avar("Graph load memory used: %d", NavigationGraph::sLoadMemUsed));
+ printDump("\r\n");
+ }
+
+ LogFile.close();
+}
+
+static bool cGraphInfo(SimObject* ptr, S32 argc, const char* argv[])
+{
+ NavigationGraph * navGraph = static_cast(ptr);
+
+ // Test it out
+ // U32 memUse = navGraph->makeLOSHashTable();
+ // Con::printf("Hash table segment pool takes up %d bytes", memUse);
+
+ S32 bridges = navGraph->numBridges();
+ S32 edges = NavigationGraph::sTotalEdgeCount;
+ S32 totalNodes = navGraph->numNodes();
+ S32 numOutdoor = navGraph->numOutdoor();
+
+ Con::printf("Graph Stats: %d nodes (%d outdoor)", totalNodes, numOutdoor);
+ Con::printf("--> %d islands", navGraph->numIslands());
+ Con::printf("--> %d bridges", bridges);
+ Con::printf("--> %d edges", edges);
+ Con::printf("Graph load memory used: %d", NavigationGraph::sLoadMemUsed);
+ Con::printf("Edge alloc = (%d + %d + 2 x %d) x %d == %d",
+ bridges, edges, totalNodes, sizeof(GraphEdge),
+ (bridges + edges + 2 * totalNodes) * sizeof(GraphEdge)
+ );
+ Con::printf("NavGraph structure = %d bytes", sizeof(NavigationGraph));
+
+ if (argc > 2) {
+ Point3F from;
+ dSscanf(argv[2], "%f %f %f", &from.x, &from.y, &from.z);
+ F32 inner = (argc > 3 ? dAtof(argv[3]) : 50);
+ F32 outer = (argc > 4 ? dAtof(argv[4]) : 1e9);
+ U32 cond = (argc > 5 ? dAtoi(argv[5]) : 3);
+
+ navGraph->clearRenderSegs();
+ navGraph->markNodesInSight(from, inner, outer, cond);
+ Con::printf(navGraph->drawNodeInfo(from));
+ }
+ return true;
+}
+
+//-------------------------------------------------------------------------------------
+
+static void cSpawnInfo(SimObject* ptr, S32, const char* [])
+{
+ NavigationGraph * navGraph = static_cast(ptr);
+ navGraph->printSpawnInfo();
+}
+
+//-------------------------------------------------------------------------------------
+
+// Args to installThreat and updateThreat are same, parse here. Also checks for
+// presence of adequate graph.
+static ShapeBase * fetchThreatInfo(S32 argc, const char* argv[], S32& team, F32& rad)
+{
+ if (NavigationGraph::gotOneWeCanUse()) {
+ ShapeBase * threatObject;
+ if (argc >= 4 && Sim::findObject(argv[2], threatObject)) {
+ team = dAtoi(argv[3]);
+ rad = argc > 4 ? dAtof(argv[4]) : 50.0f;
+ return threatObject;
+ }
+ }
+ return NULL;
+}
+
+static bool cInstallThreat(SimObject* , S32 argc, const char* argv[])
+{
+ S32 team;
+ F32 rad;
+
+ if (ShapeBase * threat = fetchThreatInfo(argc, argv, team, rad))
+ return gNavGraph->installThreat(threat, team, rad);
+
+ Con::printf("%s(): register permanent (static object) threat on the graph", argv[1]);
+ return false;
+}
+
+//-------------------------------------------------------------------------------------
+
+static bool cDetectForceFields(SimObject* , S32, const char**)
+{
+ if (NavigationGraph::gotOneWeCanUse()) {
+ gNavGraph->detectForceFields();
+ }
+ return false;
+}
+
+//-------------------------------------------------------------------------------------
+// These are patches to use for suspected script function bottlenecks so they show up
+// in the profiler. We pass down arguments. Do some timing, might give info.
+
+struct TrackPatch
+{
+ enum { MaxDepth = 12 };
+ U32 recursed[MaxDepth + 1];
+ U32 totalMS, numCalls, depth, maxDepth, lastMS;
+ F32 average;
+ const char * name;
+ TrackPatch()
+ {
+ totalMS = numCalls = depth = maxDepth = lastMS = 0;
+ dMemset(recursed, 0, sizeof(recursed));
+ average = 0.0f;
+ }
+ const char * patch(S32, const char**);
+};
+
+const char * TrackPatch::patch(S32 argc, const char * * argv)
+{
+ name = argv[0];
+ recursed[depth++]++;
+ if (depth > maxDepth)
+ {
+ AssertFatal(depth < MaxDepth, avar("Patched too deep: %s", name));
+ maxDepth = depth;
+ }
+ // Call and time it-
+ U32 saveMS = Platform::getRealMilliseconds();
+ const char * result = Con::execute(argc - 1, argv + 1);
+ lastMS = (Platform::getRealMilliseconds() - saveMS);
+ totalMS += lastMS;
+ average = F32(totalMS) / F32(++numCalls);
+ depth--;
+ return result;
+}
+
+TrackPatch gTrackProfPatch1;
+const char * cProfilePatch1(SimObject* , S32 argc, const char** argv)
+{
+ return gTrackProfPatch1.patch(argc, argv);
+}
+
+TrackPatch gTrackProfPatch2;
+const char * cProfilePatch2(SimObject* , S32 argc, const char** argv)
+{
+ return gTrackProfPatch2.patch(argc, argv);
+}
+
+//-------------------------------------------------------------------------------------
+
+
+// For checking out a couple of LOS bugs. Also mulitple casts for getting idea
+// of LOS "budget". ==> Needs to go back into DEBUG
+static const char * cGetLOSPoint(SimObject* , S32 argc, const char * * argv)
+{
+ if (argc >= 4)
+ {
+ Point3F from, losPt, to;
+ dSscanf(argv[2], "%f %f %f", &from.x, &from.y, &from.z);
+ dSscanf(argv[3], "%f %f %f", &to.x, &to.y, &to.z);
+ S32 iters = (argc > 4 ? dAtoi(argv[4]) : 1);
+
+ RayInfo coll;
+ while (--iters >= 0)
+ if (gServerContainer.castRay(from, to, U32(-1), &coll))
+ if (!iters)
+ {
+ Point3F S = coll.point;
+ Con::printf("Found solution = (%f, %f, %f)", S.x, S.y, S.z);
+ }
+ }
+ return 0;
+}
+
+//-------------------------------------------------------------------------------------
+
+// Want to test the spawning:
+static S32 cNumSpawns(SimObject * ptr, S32, const char **)
+{
+ return (static_cast(ptr))->numSpawns();
+}
+static const char * cGetSpawn(SimObject * ptr, S32 argc, const char * * argv)
+{
+ if (argc == 3)
+ {
+ char * buff = Con::getReturnBuffer(100);
+ NavigationGraph * graph = static_cast(ptr);
+ S32 which = dAtoi(argv[2]);
+
+ if (validArrayIndex(which, graph->numSpawns()))
+ {
+ Point3F point = graph->getSpawn(which);
+ adjustSpawnLoc(point);
+ dSprintf(buff, 100, "%f %f %f", point.x, point.y, point.z);
+ return buff;
+ }
+ else
+ Con::printf("Spawn index %d out of range!", which);
+ }
+ return 0;
+}
+
+//-------------------------------------------------------------------------------------
+
+#ifdef DEBUG
+
+static const char * cHidingPlace(SimObject* , S32 argc, const char * * argv)
+{
+ if (argc >= 4) {
+ Point3F from, avoidPt;
+ dSscanf(argv[2], "%f %f %f", &from.x, &from.y, &from.z);
+ dSscanf(argv[3], "%f %f %f", &avoidPt.x, &avoidPt.y, &avoidPt.z);
+ F32 rad = (argc > 4 ? dAtof(argv[4]) : 13.0f);
+ F32 hideLen = (argc > 5 ? dAtof(argv[5]) : 22.0f);
+ Point3F seek;
+
+ // Two hide queries- first seeks those with further hide length byeond, other
+ // seeks a slope away from LOS point (used for sniping)
+ if (hideLen > 0)
+ seek = NavigationGraph::hideOnDistance(from, avoidPt, rad, hideLen);
+ else
+ seek = NavigationGraph::hideOnSlope(from, avoidPt, rad, -hideLen);
+
+ Con::printf("Seek point is (%f, %f, %f)", seek.x, seek.y, seek.z);
+ }
+ else {
+ Con::printf("From src, find hiding place from avoid at least rad away.");
+ Con::printf("Hides based on hidden distance beyond or slope angle.");
+ }
+ return 0;
+}
+
+//-------------------------------------------------------------------------------------
+
+static const char * cChokePoints(SimObject* , S32 argc, const char * * argv)
+{
+ if (argc >= 4) {
+ Point3F from;
+ dSscanf(argv[2], "%f %f %f", &from.x, &from.y, &from.z);
+ F32 hideDist = dAtof(argv[3]);
+ F32 maxDist = (argc > 4 ? dAtof(argv[4]) : 1e9);
+ Vector points;
+ NavigationGraph::getChokePoints(from, points, hideDist, maxDist);
+ }
+ else {
+ Con::printf("Get list of choke points from source. HideDist tells how long of");
+ Con::printf("obstructed length must exist out of LOS. maxDist truncs search");
+ }
+ return 0;
+}
+
+//-------------------------------------------------------------------------------------
+
+// Tracking has been removed, but we may put it back in a different for. Anyway,
+// this has some other info.
+static bool cTrackObject(SimObject* ptr, S32 argc, const char* argv[])
+{
+ NavigationGraph * navGraph = static_cast(ptr);
+ GameConnection * target;
+ if (argc >= 3 && Sim::findObject(argv[2], target))
+ {
+ if (Player * player = dynamic_cast(target->getControlObject()))
+ {
+ // We just sort of dump all our test stuff here....
+ F32 thrust, dur, jumpSpeed;
+ player->getJetAbility(thrust, dur, jumpSpeed);
+ F32 rating = gNavGraph->jetManager().calcJetRating(thrust, dur);
+ Con::printf("Jet rating for player = %f", rating);
+ rating += (jumpSpeed * dur * TickSec);
+ Con::printf("With jump, rating is = %f", rating);
+
+ return true;
+ }
+ }
+ return true;
+}
+
+//-------------------------------------------------------------------------------------
+
+// See if payer can get from A to B.
+static bool cPlayerCanGo(SimObject* ptr, S32 argc, const char* argv[])
+{
+ if (argc == 5)
+ {
+ Point3F A, B;
+ Player * player;
+ if (Sim::findObject(argv[2], player))
+ {
+ NavigationGraph * navGraph = static_cast(ptr);
+ dSscanf(argv[3], "%f %f %f", &A.x, &A.y, &A.z);
+ dSscanf(argv[4], "%f %f %f", &B.x, &B.y, &B.z);
+
+ F32 ratings[2];
+ JetManager::Ability ability;
+ player->getJetAbility(ability.acc, ability.dur, ability.v0);
+ gNavGraph->jetManager().calcJetRatings(ratings, ability);
+ Con::printf("Ratings = %d and %d", ratings[0], ratings[1]);
+ if (gNavGraph->testPlayerCanReach(A, B, ratings))
+ Con::printf("Made it");
+ }
+ else
+ Con::printf("Could not find Player %s", argv[2]);
+ }
+ return false;
+}
+
+
+// debug
+#endif
+
+#ifdef INTERNAL_RELEASE
+#define DO_MATH_TESTS 1
+#else
+#define DO_MATH_TESTS 0
+#endif
+
+#if DO_MATH_TESTS
+extern void Athlon_MatrixF_x_MatrixF(const F32 *matA, const F32 *matB, F32 *res);
+extern void SSE_MatrixF_x_MatrixF(const F32 *matA, const F32 *matB, F32 *res);
+extern void default_matF_x_matF_C(const F32 *matA, const F32 *matB, F32 *res);
+
+extern U32 gSSE_MatXMat_Calls;
+
+static Point3F& rangify(Point3F& cycle) // keep in range-
+{
+ if (cycle.x > 4.0) cycle.x -= (3.14159 * 2.0);
+ if (cycle.y > 5.0) cycle.y -= (3.14159 * 2.0);
+ if (cycle.z > 6.0) cycle.z -= (3.14159 * 2.0);
+ return cycle;
+}
+
+F32 sWorstResultDiff = 0.0; // insure same results-
+static bool verify(const F32 * m1, const F32 * m2, S32 N = 16)
+{
+ while(N--) {
+ F32 D = mFabs(*m1++ - *m2++);
+ sWorstResultDiff = getMax(D, sWorstResultDiff);
+ if (D > 0.01)
+ return false;
+ }
+ return true;
+}
+
+// Test out the Athlon code-
+static S32 cTestMath(SimObject*, S32 argc, const char* argv[])
+{
+ // May just want this info (so pass in zero to add nothing to it)
+ Con::printf("Num calls to SSE func = %d", gSSE_MatXMat_Calls);
+
+ // Default to a million calls-
+ S32 numTests = (argc > 1 ? dAtoi(argv[1]) : 10);
+ S32 numIters = (argc > 2 ? dAtoi(argv[2]) : 100000);
+ S32 whatTest = (argc > 3 ? dAtoi(argv[3]) : 0);
+ void (*mo_Betta_Math)(const F32*, const F32 *, F32*) = default_matF_x_matF_C;
+ S32 failures = 0;
+
+ U32 properties = Platform::SystemInfo.processor.properties;
+ switch(whatTest)
+ {
+ case 0:
+ if (properties & CPU_PROP_SSE) {
+ Con::errorf("Testing SSE");
+ mo_Betta_Math = SSE_MatrixF_x_MatrixF;
+ } else {
+ Con::errorf("SSE not detected!");
+ return 0;
+ }
+ break;
+ case 1:
+ if (properties & CPU_PROP_3DNOW) {
+ Con::errorf("Trying to test 3DNow");
+ mo_Betta_Math = Athlon_MatrixF_x_MatrixF;
+ } else {
+ Con::errorf("3DNow not detected!");
+ return 0;
+ }
+ break;
+ }
+
+ // Make random matrices.
+ for (S32 which = 0; which < 2; which++)
+ {
+ Point3F point1(-1e9, 1000.1, -2), point2(1, 2, 1e17);
+ EulerF cycle1(0,0,0), cycle2(M_PI,M_PI,M_PI);
+ EulerF add1(0.2, 0.3, 0.5), add2(0.7, 0.13, 0.37);
+ U32 ms = Platform::getRealMilliseconds();
+
+ for (S32 i = 0; i < numTests; i++)
+ {
+ MatrixF mat1(cycle1);
+ MatrixF mat2(cycle2);
+ mat1.setColumn(3, point1);
+ mat1.setColumn(3, point2);
+ MatrixF res1, res2;
+
+ // Check we're getting same results-
+ default_matF_x_matF_C(mat1, mat2, res1);
+ mo_Betta_Math(mat1, mat2, res2);
+ if (!verify(res1, res2)) {
+ Con::printf("Matrix outputs differ on case %d!", i);
+ failures++;
+ }
+
+ // Now do repeated calls to get timing. Use separate loops to avoid dilution.
+ if (which) for (S32 j = 0; j < numIters; j++)
+ default_matF_x_matF_C(mat1, mat2, res1);
+ else for (S32 j = 0; j < numIters; j++)
+ mo_Betta_Math(mat1, mat2, res1);
+
+ rangify(cycle1 += add1);
+ rangify(cycle2 += add2);
+ point1 -= add2;
+ point2 += add1;
+ }
+ Con::printf("%d ms on pass %d", Platform::getRealMilliseconds() - ms, which);
+ }
+
+ Con::printf("Worst result difference = %f", sWorstResultDiff);
+
+ return failures;
+}
+#endif
+
+
+//-------------------------------------------------------------------------------------
+
+// Test out the closestNode() function on the graph. Also other misc. test/debug stuff
+static bool cCheckFunction(SimObject* ptr, S32 argc, const char* argv[])
+{
+ if (argc == 3)
+ {
+ Point3F center;
+ F32 contain;
+ NavigationGraph * navGraph = static_cast(ptr);
+ dSscanf(argv[2], "%f %f %f", ¢er.x, ¢er.y, ¢er.z);
+ GraphNode * best = navGraph->closestNode(center, & contain);
+ if (best)
+ {
+ Con::printf("********** Containment = %f **********", contain);
+ Point3F P = best->location();
+ Con::printf("Supplied loc = (%f, %f, %f)", center.x, center.y, center.z);
+ Con::printf("Node location = (%f, %f, %f)", P.x, P.y, P.z);
+ NodeProximity prox = best->containment(center);
+ Con::printf("Prox = (%f, %f, %f)", prox.mLateral, prox.mHeight, prox.mAboveC);
+ }
+ else
+ Con::printf("Nothing found");
+
+ FloorPlan::setBreakPoint(center);
+ }
+ return true;
+}
+
+//-------------------------------------------------------------------------------------
+
+//
+// This probably needs to remain ISV because it can be useful in determining if a graph
+// is feasible / efficient.
+//
+static bool cTimeTest(SimObject * ptr, S32 argc, const char * * argv)
+{
+ if (argc >= 3)
+ {
+ S32 numSearches = dAtoi(argv[2]);
+ NavigationGraph * navGraph = static_cast(ptr);
+ bool doAStar = (argc > 3 ? dAtob(argv[3]) : false);
+
+ U32 saveMS = Platform::getRealMilliseconds();
+ F32 average = navGraph->performTests(numSearches, doAStar);
+ F32 elapsed = F32(Platform::getRealMilliseconds() - saveMS);
+
+ Con::printf("Average number of Q extractions in search = %f", average);
+ Con::printf("Elapsed time = %f seconds", elapsed * 0.001);
+ }
+ return true;
+}
+
+//-------------------------------------------------------------------------------------
+
+PreloadTextures::PreloadTextures()
+{
+ mNext = 0;
+}
+
+PreloadTextures::~PreloadTextures() // just want to watch destruct in debugger...
+{
+ mNext = 0;
+}
+
+void PreloadTextures::load(const char * name, bool clamp)
+{
+ AssertFatal(mNext < MaxHandles-1, "PreloadTextures::load()");
+ mTextures[mNext++] = TextureHandle(name, MeshTexture, clamp);
+}
+
+static bool cPreload(SimObject * ptr, S32, const char * * argv)
+{
+ NavigationGraph * navGraph = static_cast(ptr);
+ navGraph->mTextures.load(argv[2], dAtob(argv[3]));
+ return true;
+}
+
+//-------------------------------------------------------------------------------------
+
+static bool cGenDebug(SimObject * ptr, S32 argc, const char * * argv)
+{
+ NavigationGraph * navGraph = static_cast(ptr);
+
+ if (argc > 2)
+ {
+ Point3F magnifyLoc, dbg1, dbg2;
+ Point3F * p1 = NULL, * p2 = NULL;
+ F32 magnifyRad = 40;
+ F32 xyCheck = 0.5, zCheck = 2.0;
+
+ dSscanf(argv[2], "%f %f %f", &magnifyLoc.x, &magnifyLoc.y, &magnifyLoc.z);
+
+ if (argc > 3)
+ {
+ magnifyRad = dAtof(argv[3]);
+
+ if (argc > 4)
+ {
+ // These aren't documented and are just for internal debugging. They're
+ // kept ISV since the build process is tryingly slow in DTEST, and they
+ // don't effect operation, or significant speed/space, in ship verion.
+ p1 = & dbg1;
+ dSscanf(argv[4], "%f %f %f", &p1->x, &p1->y, &p1->z);
+ if (argc > 5)
+ {
+ p2 = & dbg2;
+ dSscanf(argv[5], "%f %f %f", &p2->x, &p2->y, &p2->z);
+ if (argc > 6)
+ {
+ xyCheck = dAtof(argv[6]);
+ if (argc > 7)
+ zCheck = dAtof(argv[7]);
+ }
+ }
+ }
+ }
+
+ SphereF magnifySphere(magnifyLoc, magnifyRad);
+ navGraph->setGenMagnify(&magnifySphere, p1, p2, xyCheck, zCheck);
+ return true;
+ }
+
+ navGraph->setGenMagnify(NULL, NULL, NULL);
+ Con::printf("This function is for testing the graph generation by focusing");
+ Con::printf("on a smaller area (magnify sphere) to speed things up.");
+ return false;
+}
+
+//-------------------------------------------------------------------------------------
+
+static const char navGraphTxt[] = "NavigationGraph";
+
+#define GraphCmd1(method, cfunc, usage, minparams, maxparams) \
+ Con::addCommand(navGraphTxt, method, cfunc, usage, minparams, maxparams)
+#define GraphCmd2(function, cfunc, usage, minparams, maxparams) \
+ Con::addCommand(function, cfunc, usage, minparams, maxparams)
+#define GraphVar1(name, type, svar) \
+ Con::addVariable(name, type, &NavigationGraph::##svar)
+#define GraphVar2(name, type, varname) \
+ Con::addVariable(name, type, &varname)
+
+
+static void addGraphVariables()
+{
+ GraphVar1("$pref::NavGraph::drawOutdoor", TypeBool, sDrawOutdoorNodes);
+ GraphVar1("$pref::NavGraph::drawIndoor", TypeBool, sDrawIndoorNodes);
+ GraphVar1("$pref::NavGraph::drawJetEdges", TypeBool, sDrawJetConnections);
+ GraphVar1("graphProcessPercent", TypeF32, sProcessPercent);
+
+ // Average MS per call to patch functions-
+ GraphVar2("patch1Avg", TypeF32, gTrackProfPatch1.average);
+ GraphVar2("patch2Avg", TypeF32, gTrackProfPatch2.average);
+ GraphVar2("patch1Total", TypeS32, gTrackProfPatch1.totalMS);
+ GraphVar2("patch2Total", TypeS32, gTrackProfPatch2.totalMS);
+ GraphVar2("patch1Last", TypeS32, gTrackProfPatch1.lastMS);
+ GraphVar2("patch2Last", TypeS32, gTrackProfPatch2.lastMS);
+ GraphVar2("patch1Calls", TypeS32, gTrackProfPatch1.numCalls);
+ GraphVar2("patch2Calls", TypeS32, gTrackProfPatch2.numCalls);
+
+ // Number of edges rendered in the AI editor-
+ GraphVar1("edgeRenderMaxOutdoor", TypeS32, sEdgeRenderMaxOutdoor);
+ GraphVar1("edgeRenderMaxIndoor", TypeS32, sEdgeRenderMaxIndoor);
+
+ // Nodes in view of this threat should be highlighted-
+ GraphVar1("showNodeThreat", TypeS32, sShowThreatened);
+
+ // Some control variables for channeling the profiles. eg. focusing on indoors, etc
+ GraphVar1("ProfileControl0", TypeS32, sProfCtrl0);
+ GraphVar1("ProfileControl1", TypeS32, sProfCtrl1);
+
+ GraphVar1("Graph::SeedDropOffs", TypeBool, sSeedDropOffs);
+}
+
+
+void NavigationGraph::consoleInit()
+{
+ Parent::consoleInit();
+
+ addGraphVariables();
+
+ // Temp-
+ GraphCmd1("Preload", cPreload, "navGraph.preload(name,clamp);", 4, 4);
+
+ GraphCmd1("makeGraph", cMakeGraph, "navGraph.makeGraph();", 2, 2);
+ GraphCmd1("setGenMode", cSetGenMode, "navGraph.setGenMode(nav|spawn);", 2, 3);
+
+ GraphCmd1("saveGraph", cSaveGraph, "navGraph.saveGraph();", 2, 2);
+ GraphCmd1("loadGraph", cLoadGraph, "navGraph.loadGraph();", 2, 2);
+
+ // Misc. graph building functions-
+ GraphCmd1("setGround", cSetGround, "navGraph.setGround(GroundPlan);", 3, 3);
+ GraphCmd1("prepLOS", cPrepLOS, "navGraph.prepLOS();", 2, 3);
+ GraphCmd1("makeLOS", cMakeLOS, "navGraph.makeLOS();", 2, 2);
+ GraphCmd1("findBridges", cFindBridges, "navGraph.findBridges();", 2, 2);
+ GraphCmd1("pushBridges", cPushBridges, "navGraph.pushBridges();", 2, 2);
+ GraphCmd1("cullIslands", cCullIslands, "navGraph.cullIslands();", 2, 2);
+ GraphCmd1("makeTables", cMakeTables, "navGraph.makeTables();", 2, 2);
+ GraphCmd1("assemble", cAssemble, "navGraph.assemble();", 2, 2);
+
+ // Tests / diagnostics
+ GraphCmd1("timeTest", cTimeTest, "navGraph.timeTest(iterations[, doAStar])", 2, 4);
+ GraphCmd1("genDebug", cGenDebug, "navGraph.genDebug(magnifyLoc[, magnifyRad])", 2, 8);
+ GraphCmd1("check", cCheckFunction, "navGraph.check(loc);", 3, 3);
+ GraphCmd1("getLOSPoint", cGetLOSPoint, "navGraph.getLOSPoint(from, to [,N]);", 4, 5);
+ GraphCmd1("numSpawns", cNumSpawns, "navGraph.numSpawns()", 2, 2);
+ GraphCmd1("getSpawn", cGetSpawn, "navGraph.getSpawn(which)", 2, 3);
+ #ifdef DEBUG
+ GraphCmd1("track", cTrackObject, "navGraph.track(clientId);", 2, 3);
+ GraphCmd1("hidingPlace", cHidingPlace, "navGraph.hidingPlace(src,avoid,rad,len/ang);", 2, 6);
+ GraphCmd1("chokePoints", cChokePoints, "navGraph.chokePoints(here,hideD,maxD);", 2, 5);
+ GraphCmd1("playerCanGo", cPlayerCanGo, "navGraph.playerCanGo(fromA,toB,player);", 5, 5);
+ #endif
+
+ // Queries
+ GraphCmd1("randNode", cRandNode, "navGraph.randNode(pt, rad, indoor, outdoor);", 2, 6);
+ GraphCmd1("numNodes", cNumNodes, "navGraph.numNodes();", 2, 2);
+ GraphCmd1("nodeLoc", cNodeLoc, "navGraph.nodeLoc(nodeIndex);", 2, 3);
+ GraphCmd1("randNodeLoc", cRandNodeLoc, "navGraph.randNodeLoc(nodeIndex);", 2, 3);
+ GraphCmd2("WhereToLook", cWhereToLook, "WhereToLook(playerLoc);", 2, 2);
+ GraphCmd2("navGraphExists", cNavGraphExists, "navGraphExists();", 1, 1);
+ GraphCmd1("info", cGraphInfo, "navGraph.info([loc], [rad]);", 2, 7);
+ GraphCmd1("dumpInfo2File", cDumpInfo2File, "navGraph.dumpInfo2File();", 2, 2);
+ GraphCmd1("spawnInfo", cSpawnInfo, "navGraph.spawnInfo();", 2, 2);
+
+ // Script should register threats with these-
+ GraphCmd1("installThreat", cInstallThreat, "navGraph.installThreat(id, team [,R]);", 2, 5);
+ GraphCmd2("NavDetectForceFields", cDetectForceFields, "NavDetectForceFields();", 1, 1);
+
+ // Utilities for profiling script functions-
+ GraphCmd2("ProfilePatch1", cProfilePatch1, "ProfilePatch1(func, args...);", 2, 20);
+ GraphCmd2("ProfilePatch2", cProfilePatch2, "ProfilePatch2(func, args...);", 2, 20);
+
+
+ #if DO_MATH_TESTS
+ GraphCmd2("TestMath", cTestMath, "TestMath(nTests, nIters);", 1, 4);
+ #endif
+}
diff --git a/ai/graph.h b/ai/graph.h
new file mode 100644
index 0000000..bb73b2b
--- /dev/null
+++ b/ai/graph.h
@@ -0,0 +1,341 @@
+//-----------------------------------------------------------------------------
+// V12 Engine
+//
+// Copyright (c) 2001 GarageGames.Com
+// Portions Copyright (c) 2001 by Sierra Online, Inc.
+//-----------------------------------------------------------------------------
+
+#ifndef _GRAPH_H_
+#define _GRAPH_H_
+
+#define _GRAPH_DEBUG_
+#define _GRAPH_WARNINGS_ 0
+#define _GRAPH_PART_ 1
+
+#ifndef _GRAPHDATA_H_
+#include "ai/graphData.h"
+#endif
+#ifndef _GRAPHBASE_H_
+#include "ai/graphBase.h"
+#endif
+#ifndef _GRAPHPARTITION_H_
+#include "ai/graphPartition.h"
+#endif
+#ifndef _GRAPHNODES_H_
+#include "ai/graphNodes.h"
+#endif
+#ifndef _GRAPHTRANSIENT_H_
+#include "ai/graphTransient.h"
+#endif
+#ifndef _GRAPHLOCATE_H_
+#include "ai/graphLocate.h"
+#endif
+#ifndef _GRAPHTHREATS_H_
+#include "ai/graphThreats.h"
+#endif
+#ifndef _GRAPHFORCEFIELD_H_
+#include "ai/graphForceField.h"
+#endif
+#ifndef _GRAPHJETTING_H_
+#include "ai/graphJetting.h"
+#endif
+#ifndef _GRAPHSEARCHES_H_
+#include "ai/graphSearches.h"
+#endif
+#ifndef _GRAPHPATH_H_
+#include "ai/graphPath.h"
+#endif
+#ifndef _GRAPHGROUNDPLAN_H_
+#include "ai/graphGroundPlan.h"
+#endif
+#ifndef _TEXTUREPRELOAD_H_
+#include "ai/texturePreload.h"
+#endif
+
+#ifndef _SIMBASE_H_
+#include "console/simBase.h"
+#endif
+
+class NavigationGraph : public SimObject
+{
+ typedef SimObject Parent;
+ static S32 sVersion;
+ friend class NavigationPath;
+
+ public:
+ static bool sDrawOutdoorNodes, sDrawIndoorNodes, sDrawJetConnections;
+ static S32 sEdgeRenderMaxOutdoor, sEdgeRenderMaxIndoor;
+ static S32 sProfCtrl0, sProfCtrl1;
+ static S32 sTotalEdgeCount;
+ static F32 sProcessPercent;
+ static U32 sLoadMemUsed;
+ static S32 sShowThreatened;
+ static bool sSeedDropOffs;
+
+ static void consoleInit();
+ static void initPersistFields();
+ static bool gotOneWeCanUse();
+ static bool hasSpawnLocs();
+ static void warning(const char* msg);
+ static F32 fastDistance(const Point3F& p1, const Point3F& p2);
+ static Point3F hideOnSlope(const Point3F& from, const Point3F& avoid,
+ F32 rad, F32 deg);
+ static Point3F hideOnDistance(const Point3F& from, const Point3F& avoid,
+ F32 rad, F32 hideLen = 10.0);
+ static Point3F findLOSLocation(const Point3F& from, const Point3F& wantToSee,
+ F32 minAway, const SphereF& getCloseTo, F32 capDist=1e11);
+ static S32 getChokePoints(const Point3F& srcPoint, Vector& points,
+ F32 minHideDist, F32 stopSearchDist=1e9);
+ static F32 whereToLook(Point3F P);
+
+ protected:
+ // .NAV or .SPN file data:
+ SpawnLocations mSpawnList;
+ EdgeInfoList mEdgeInfoList;
+ NodeInfoList mNodeInfoList;
+ GraphVolumeList mNodeVolumes;
+ TerrainGraphInfo mTerrainInfo;
+ BridgeDataList mBridgeList;
+ PathXRefTable mPathXRef;
+ ChuteHints mChutes;
+ LOSHashTable mLOSHashTable;
+
+ // Run time node / edge lists:
+ GraphNodeList mNodeList;
+ GraphNodeList mNodeGrid;
+ GraphNodeList mNonTransient;
+ GraphNodeList mIndoorPtrs;
+ IndoorNodeList mIndoorNodes;
+ OutdoorNode * mOutdoorNodes;
+ GraphNodeList mIslandPtrs;
+ Vector mIslandSizes;
+ GraphBSPTree mIndoorTree;
+ GraphEdge * mEdgePool;
+ LOSXRefTable mLOSXRef;
+ LOSTable * mLOSTable;
+
+ S32 mVersion;
+ S32 mLargestIsland;
+ S32 mNumOutdoor;
+ S32 mNumIndoor;
+ S32 mTransientStart;
+ static S32 mIncarnation;
+ S32 mCheckNode;
+ const S32 mMaxTransients;
+ bool mValidLOSTable;
+ bool mValidPathTable;
+ bool mHaveVolumes;
+ bool mPushedBridges;
+ bool mIsSpawnGraph;
+ bool mDeadlyLiquid;
+ F32 mSubmergedScale;
+ F32 mShoreLineScale;
+ GraphSearch * mTableBuilder;
+ GraphSearch * mMainSearcher;
+ GraphSearchLOS * mLOSSearcher;
+ GraphSearchDist * mDistSearcher;
+ GraphBoundaries mBoundaries;
+ SimObjectPtr mTerrainBlock;
+ Vector mRenderThese;
+ Vector mRenderBoxes;
+ GraphEdge mEdgeBuffer[MaxOnDemandEdges];
+ GraphEdgePtrs mVisibleEdges;
+ SearchThreats mThreats;
+ MonitorForceFields mForceFields;
+ JetManager mJetManager;
+
+ // Temp buffers returned (as const T &) by misc fetch methods-
+ Vector mTempNodeBuf;
+ GraphNodeList mUtilityNodeList1;
+ GraphNodeList mUtilityNodeList2;
+ GraphVolume mTempVolumeBuf;
+
+ // Inspect persist variables.
+ F32 mCullDensity;
+ ConjoinConfig mConjoin;
+ GridArea mCustomArea;
+
+ public:
+ FindGraphNode mFoundNodes[FindGraphNode::HashTableSize];
+
+ protected:
+ bool onAdd();
+ void onRemove();
+ S32 markIslands();
+ S32 doFinalFixups();
+ void clearLoadData();
+ void purgeForSpawn();
+ bool initInteriorNodes(const EdgeInfoList&, const NodeInfoList&, S32);
+ S32 getNodesInArea(GraphNodeList& listOut, GridArea gridArea);
+ void makeRunTimeNodes(bool useEdgePool = false);
+ S32 setupOutdoorNodes(const GridArea& area,const Consolidated& cons,
+ GraphNodeList& grid,GraphNodeList& list);
+ F32 distancef(const Point3F& p1, const Point3F& p2);
+ void chokePoints(const Point3F& S, Vector& pts, F32 H, F32 M);
+ S32 crossingSegs(const GraphNode*, const GraphEdge*, LineSegment* const);
+ void getInSphere(const SphereF&, GraphNodeList& I, GraphNodeList& O);
+ void getOutdoorList(GraphNodeList& list);
+ void newIncarnation();
+ S32 hookTransient(TransientNode&);
+ void unhookTransient(TransientNode&);
+ S32 floodPartition (U32 seed, bool needBoth, F32 jetRating);
+ S32 partitionOneArmor(PartitionList& list, F32 jetRating);
+
+ public:
+ NavigationGraph();
+ ~NavigationGraph();
+ DECLARE_CONOBJECT(NavigationGraph);
+
+ F32 performTests(S32 numTests, bool aStar = 0);
+ bool testPlayerCanReach(Point3F A, Point3F B, const F32* ratings);
+ F32 lineAcrossTerrain(const Point3F& from, Point3F& to);
+ bool inNodeVolume(const GraphNode* node, const Point3F& point);
+ NodeProximity getContainment(S32 indoorNodeIndex, const Point3F& point);
+ bool verticallyInside(S32 indoorIndex, const Point3F& point);
+ bool closestPointOnVol(GraphNode*, const Point3F& pt, Point3F& soln) const;
+ bool possibleToJet(const Point3F& from, const Point3F& to, U32 armor=0);
+ F32 heightAboveFloor(S32 indoorIndex, const Point3F& point);
+ S32 indoorIndex(const GraphNode* node);
+ PlaneF getFloorPlane(GraphNode* node);
+
+ bool useVolumeTraverse(const GraphNode* from, const GraphEdge* to);
+ bool volumeTraverse(const GraphNode* from, const GraphEdge* to, NavigationPath::State&);
+ S32 checkIndoorSkip(NavigationPath::State & state);
+
+ bool volumeTraverse(const GraphNode*, const GraphEdge*, const Point3F&, U8&, Point3F&);
+ bool volumeTraverse(const GraphNode*, const GraphEdge*, const NavigationPath::State&);
+ S32 checkIndoorSkip(const GraphEdgePtrs& edges, const Point3F& cur, S32 from, Vector& visit, Point3F& seek);
+ bool installThreat(ShapeBase * threat, S32 team, F32 rad);
+ bool updateThreat(ShapeBase * threat, S32 team, F32 rad);
+ void patrolForProblems();
+
+ // Three types of search machines get used-
+ GraphSearch* getMainSearcher();
+ GraphSearchLOS* getLOSSearcher();
+ GraphSearchDist* getDistSearcher();
+
+ // Transient management
+ void destroyPairOfHooks(S32 idx);
+ S32 createPairOfHooks();
+ bool pushTransientPair(TransientNode& src, TransientNode& dst,
+ U32 team, const JetManager::ID& jetCaps);
+ bool canReachLoc(const FindGraphNode& src, const FindGraphNode& dst,
+ U32 team, const JetManager::ID& jetCaps);
+ void popTransientPair(TransientNode& src, TransientNode& dst);
+
+ // Graph construction methods called from console, console queries, ...
+ bool load(Stream&, bool isSpawn = false);
+ bool save(Stream&);
+ bool loadGraph();
+ bool saveGraph();
+ bool assemble();
+ bool makeGraph(bool usePool=false);
+ S32 makeTables();
+ const char * findBridges();
+ const char * pushBridges();
+ S32 scatterSeeds();
+ void installSeed(const Point3F& at);
+ S32 compactIndoorData(const Vector& itrList, S32 numOutdoor);
+ void setGenMagnify(const SphereF*,const Point3F*,const Point3F*,F32 xy=1,F32 z=2);
+ bool prepLOSTableWork(Point3F viewLoc);
+ bool makeLOSTableEntries();
+ S32 cullIslands();
+ bool setGround(GroundPlan * gp);
+ S32 randNode(const Point3F& P, F32 R, bool in, bool out);
+ void detectForceFields() {mForceFields.atMissionStart();}
+ void checkHashTable(S32 nodeCount) const;
+ bool sanctionSpawnLoc(GraphNode * node, const Point3F& pt) const;
+ const Point3F* getRandSpawnLoc(S32 nodeIndex);
+ const Point3F* getSpawnLoc(S32 nodeIndex);
+ U32 makeLOSHashTable();
+ void makeSpawnList();
+ U32 reckonMemory() const;
+
+ // Queries for nodes and edges: for hand edited; and for algorithmic
+ S32 setNodesAndEdges(const EdgeInfoList&, const NodeInfoList&);
+ S32 getNodesAndEdges(EdgeInfoList& edges, NodeInfoList& nodes);
+ S32 setEdgesAndNodes(const EdgeInfoList&, const NodeInfoList&);
+ S32 setNodeVolumes(const GraphVolumeList& volumes);
+ S32 setAlgorithmic(const EdgeInfoList& E, const NodeInfoList& N);
+ S32 setChuteHints(const Vector& chutes);
+ S32 clearAlgorithmic();
+ S32 findJumpableNodes();
+ void expandShoreline(U32 wave = 0);
+
+ // Node and edge finding -
+ const GraphEdgePtrs& getBlockedEdges(GameBase* byThis, U32 objTypeMask);
+ const GraphNodeList& getVisibleNodes(GraphNode * S, const Point3F& loc, F32 rad);
+ const GraphNodeList& getVisibleNodes(const Point3F& loc, F32 rad);
+ const GraphVolume& fetchVolume(const GraphNode* N, bool above);
+ S32 getNodesInBox(Box3F worldBox, GraphNodeList& listOut, bool justIndoor=false);
+ S32 crossNearbyVolumes(const Point3F& from, Vector& points);
+ GraphNode * terrainHookNode(const Point3F& pos, S32& index);
+ GraphNode * findTerrainNode(const Point3F& loc);
+ GraphNode * nearbyTerrainNode(const Point3F& loc);
+ GraphNode * closestNode(const Point3F& loc, F32 * containment = NULL);
+ bool haveMuzzleLOS(S32 nodeInd1, S32 nodeInd2);
+ bool terrainHeight(Point3F pos, F32* height, Point3F* normal=0);
+ F32 getRoamRadius(const Point3F &loc);
+ void worldToGrid(const Point3F& wPos, Point2I& gPos);
+ Point3F gridToWorld(const Point2I& gPos);
+ GridArea getGridRectangle(const Point3F& atPos, S32 gridRad);
+ bool customArea(GridArea& fetch) const;
+ GridArea getWorldRect();
+
+ // render methods
+ void drawNodeInfo(GraphNode * node);
+ const char* drawNodeInfo(const Point3F& loc);
+ void render(Point3F &camPos, bool drawClipped);
+ void drawNeighbors(GraphNode*, GraphEdgeArray, const Point3F&, F32 w=1e9);
+ void markNodesInSight(const Point3F& from, F32 in, F32 out, U32 cond);
+ void clearRenderBoxes() {mRenderBoxes.clear();}
+ void clearRenderSegs() {mRenderThese.clear();}
+ void getCrossingPoints(S32 from, Point3F* pts, GraphEdge* to);
+ void setRenderList(const Vector& segs) {mRenderThese=segs;}
+ void renderInteriorNodes(Point3F camPos);
+ void pushRenderSeg(const LineSegment& ls);
+ void pushRenderBox(Point3F boxLoc, F32 zadd=1.5);
+ void renderTransientNodes();
+ GraphThreatSet showWhichThreats() const;
+
+ // One-liners
+ S32 incarnation() const {return mIncarnation;}
+ GraphNode* lookupNode(S32 X) const {return mNodeList[X];}
+ S32 numIslands() const {return mIslandPtrs.size();}
+ S32 numOutdoor() const {return mNumOutdoor;}
+ S32 numNodesAll() const {return mNodeList.size();}
+ S32 numNodes() const {return mNonTransient.size();}
+ bool validLOSXref() const {return mValidLOSTable;}
+ bool validPathXRef() const {return mValidPathTable;}
+ bool haveVolumes() const {return mHaveVolumes;}
+ bool haveForceFields() const {return (mForceFields.count() != 0);}
+ F32 submergedScale() const {return mSubmergedScale;}
+ F32 shoreLineScale() const {return mShoreLineScale;}
+ bool deadlyLiquids() const {return mDeadlyLiquid;}
+ bool haveTerrain() const {return bool(mTerrainBlock);}
+ S32 numBridges() const {return mBridgeList.numPositiveBridges();}
+ S32 largestIsland() const {return mLargestIsland;}
+ S32 numSpawns() const {return mSpawnList.size();}
+ Point3F getSpawn(S32 i) const {return mSpawnList[i];}
+ void printSpawnInfo() const {mSpawnList.printInfo();}
+ void setGenMode(bool spawn) {mIsSpawnGraph = spawn;}
+ void monitorForceFields() {mForceFields.monitor();}
+ Vector& tempNodeBuff() {return mTempNodeBuf;}
+ const LOSTable* getLOSXref() const {return mValidLOSTable ? mLOSTable : NULL;}
+ const GraphBoundary& getBoundary(S32 i) {return mBoundaries[i];}
+ GraphThreatSet getThreatSet(S32 T) const {return mThreats.getThreatSet(T);}
+ void newPartition(GraphSearch* S, U32 T) {mForceFields.informSearchFailed(S, T);}
+ SearchThreats* threats() {return &mThreats;}
+ JetManager& jetManager() {return mJetManager;}
+ ChuteHints& getChutes() {return mChutes;}
+
+ // We manage the data that is shared (for memory optimization) among searchers
+ GraphSearch::Globals mSharedSearchLists;
+
+ // Temporary - store on graph until we have a special object for it-
+ PreloadTextures mTextures;
+};
+
+extern NavigationGraph * gNavGraph;
+
+#endif
diff --git a/ai/graphBSP.h b/ai/graphBSP.h
new file mode 100644
index 0000000..1db5371
--- /dev/null
+++ b/ai/graphBSP.h
@@ -0,0 +1,220 @@
+//-----------------------------------------------------------------------------
+// V12 Engine
+//
+// Copyright (c) 2001 GarageGames.Com
+// Portions Copyright (c) 2001 by Sierra Online, Inc.
+//-----------------------------------------------------------------------------
+
+#ifndef _GRAPHBSP_H_
+#define _GRAPHBSP_H_
+
+template class AxisAlignedBSP
+{
+ public:
+ enum {Lower, Upper};
+
+ class BSPNode {
+ public:
+ U16 mAxis;
+ U16 mCount;
+ union {
+ U16 mBranch[2];
+ T * mLeaf;
+ } X;
+ F32 mDivide;
+ BSPNode()
+ {
+ mAxis = 0;
+ mCount = 0;
+ mDivide = 0.0f;
+ X.mBranch[Lower] = X.mBranch[Upper] = 0;
+ X.mLeaf = NULL;
+ }
+ };
+
+ typedef Vector BSPPool;
+
+ protected:
+ static S32 sSortAxis;
+ BSPPool mTree;
+ VectorPtr mList;
+
+ protected:
+ static S32 QSORT_CALLBACK compareAxis(const void* , const void* );
+ void sortOnAxis(VectorPtr& list, S32 axis);
+ void traverse(S32 ind, const Box3F& box, VectorPtr* result) const;
+ U16 partition(S32 start, S32 N);
+
+ public:
+ void makeTree(const VectorPtr& listIn);
+ S32 getIntersecting(VectorPtr& listOut, Box3F box) const;
+ void clear();
+};
+
+//-------------------------------------------------------------------------------------
+
+template S32 AxisAlignedBSP::sSortAxis;
+
+template
+S32 QSORT_CALLBACK AxisAlignedBSP::compareAxis(const void* a,const void* b)
+{
+ const F32 * locA = (*(T**)a)->location();
+ const F32 * locB = (*(T**)b)->location();
+ F32 A = locA[sSortAxis], B = locB[sSortAxis];
+ return (A < B ? -1 : (A > B ? 1 : 0));
+}
+
+template void AxisAlignedBSP::sortOnAxis(VectorPtr& list, S32 axis)
+{
+ sSortAxis = axis;
+ dQsort((void* )(list.address()), list.size(), sizeof(T* ), compareAxis);
+}
+
+//-------------------------------------------------------------------------------------
+
+// Main tree building routine. Divides up the space at each level until done.
+template U16 AxisAlignedBSP::partition(S32 start, S32 N)
+{
+ S32 place = mTree.size();
+ BSPNode assembleNode;
+
+ if (N < 2)
+ {
+ AssertFatal(N == 1, "Bad call to axis-aligned BSP partition");
+ assembleNode.mCount = 1;
+ assembleNode.X.mLeaf = mList[start];
+ mTree.push_back(assembleNode);
+ }
+ else
+ {
+ S32 mid1 = (N >> 1);
+ S32 mid0 = mid1 - 1;
+ F32 bestSeparation = -1;
+ VectorPtr divisions[3];
+
+ // Try all three divisions. We take the one whose halves are furthest apart
+ // at the divide.
+ for (S32 axis = 0; axis < 3; axis++)
+ {
+ divisions[axis].setSize(N);
+ dMemcpy(&(divisions[axis][0]), &mList[start], N * sizeof(T*));
+ sortOnAxis(divisions[axis], axis);
+
+ const F32 * endOfFirst = divisions[axis][mid0]->location();
+ const F32 * startOf2nd = divisions[axis][mid1]->location();
+
+ F32 low = endOfFirst[axis];
+ F32 high = startOf2nd[axis];
+ F32 separation = (high - low);
+
+ AssertFatal(separation >= 0, "Bad sort in axis-aligned BSP construction");
+
+ if (separation > bestSeparation)
+ {
+ bestSeparation = separation;
+ assembleNode.mCount = N;
+ assembleNode.mAxis = axis;
+ assembleNode.mDivide = low + (separation * 0.5f);
+ }
+ }
+
+ // Copy over the sorted elements along the axis we're dividing-
+ dMemcpy(&mList[start], &(divisions[assembleNode.mAxis][0]), N * sizeof(T*));
+
+ // Install the node proper, and call down to further partition.
+ mTree.push_back(assembleNode);
+
+ // NOTE!! This code doesn't work in release build if we haven't pre-reserved the
+ // !!!!!! tree. It looks like a compiler bug to me, not 100% sure though.
+ mTree[place].X.mBranch[Lower] = partition(start, mid1);
+ mTree[place].X.mBranch[Upper] = partition(start + mid1, N - mid1);
+ }
+
+ // Return where we're at in the tree.
+ return place;
+}
+
+//-------------------------------------------------------------------------------------
+// Make a tree whose leaves are the nodes passed in.
+
+template void AxisAlignedBSP::makeTree(const VectorPtr& listIn)
+{
+ mList = listIn;
+ mTree.clear();
+
+ AssertFatal(mList.size() < 32000, "AxisAlignedBSP::makeTree() - too many nodes");
+
+ if (mList.size()) {
+ mTree.reserve(mList.size() * 3);
+ partition(0, mList.size());
+ mTree.compact();
+ }
+
+ mList.clear();
+ mList.compact();
+}
+
+//-------------------------------------------------------------------------------------
+
+// We want our check to be inclusive of both ends (Box3F method isn't above)
+inline bool boxContainsPt(const Box3F& B, const Point3F& P)
+{
+ if (P.x >= B.min.x && P.x <= B.max.x)
+ if (P.y >= B.min.y && P.y <= B.max.y)
+ return (P.z >= B.min.z && P.z <= B.max.z);
+ return false;
+}
+
+//-------------------------------------------------------------------------------------
+
+
+template void AxisAlignedBSP::traverse(S32 ind, const Box3F& box,
+ VectorPtr* result) const
+{
+ const BSPNode & node = mTree[ind];
+
+ if (node.mCount == 1) { // At node-
+ if (boxContainsPt(box, node.X.mLeaf->location()))
+ result->push_back(node.X.mLeaf);
+ }
+ else {
+ AssertFatal(node.mCount > 1, "AxisAlignedBSP::traverse()");
+ if (node.mDivide > ((const F32*)(box.max))[node.mAxis])
+ traverse(node.X.mBranch[Lower], box, result);
+ else if (node.mDivide < ((const F32*)(box.min))[node.mAxis])
+ traverse(node.X.mBranch[Upper], box, result);
+ else {
+ Box3F splitUpper(box);
+ Box3F splitLower(box);
+ ((F32*)(splitUpper.min))[node.mAxis] = node.mDivide;
+ ((F32*)(splitLower.max))[node.mAxis] = node.mDivide;
+ traverse(node.X.mBranch[Lower], splitLower, result);
+ traverse(node.X.mBranch[Upper], splitUpper, result);
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------
+// Push all nodes intersecting the box onto the user-supplied list. We don't clear
+// list because we'll probably have two trees - for small nodes and large nodes.
+
+template
+S32 AxisAlignedBSP::getIntersecting(VectorPtr& result, Box3F box) const
+{
+ if (mTree.size())
+ traverse(0, box, &result);
+ return result.size();
+}
+
+//-------------------------------------------------------------------------------------
+
+template void AxisAlignedBSP::clear()
+{
+ mTree.clear(); mTree.compact();
+ mList.clear(); mList.compact();
+}
+
+
+//-------------------------------------------------------------------------------------
+
+#endif
diff --git a/ai/graphBase.cc b/ai/graphBase.cc
new file mode 100644
index 0000000..3f4225b
--- /dev/null
+++ b/ai/graphBase.cc
@@ -0,0 +1,388 @@
+//-----------------------------------------------------------------------------
+// V12 Engine
+//
+// Copyright (c) 2001 GarageGames.Com
+// Portions Copyright (c) 2001 by Sierra Online, Inc.
+//-----------------------------------------------------------------------------
+
+#include "ai/graph.h"
+
+//-------------------------------------------------------------------------------------
+// GraphEdge
+
+GraphEdge::GraphEdge()
+{
+ mDest = -1; // Destination node index
+ mBorder = -1; // Border segment on interiors
+ mDist = 1.0; // 3D dist, though jet edges are adjusted a little so ratings work
+ mJet = 0; // Jetting connection
+ mHopOver = 0; // For jetting, amount to hop up and over
+
+ // Factors that influence edge scaling-
+ //
+ mTeam = 0; // An edge that only this team can pass - i.e. force fields.
+ mJump = 0; // On jetting edges - is it flat enough to jump?
+ mLateral = 0; // Crude lateral distance - needed to know if we can jet down.
+ mDown = 0; // Down side of a jet?
+ mSteep = 0; // Can't walk up
+ setInverse(1.0);
+}
+
+const char * GraphEdge::problems() const
+{
+ if (mDist < 0)
+ return "Dijkstra() forbids edge with negative distance";
+ if (getInverse() < 0)
+ return "Edge with negative scale";
+ return NULL;
+}
+
+// Given the jet rating, can this connection be traversed? If down, must satisfy
+// lateral only. Used by searcher, and by partition builder. In the latter case it
+// must (on 1st of its 2 passes) treat down same as up to find stranded partitions.
+bool GraphEdge::canJet(const F32* ratings, bool both) const
+{
+ if (!mDown || both)
+ {
+ F32 rating = ratings[mJump];
+ return (rating >= mDist);
+ }
+ else
+ {
+ F32 rating = ratings[mJump];
+ return rating > F32(mLateral);
+ }
+}
+
+// This table gives our inverse speed values, so we can keep it in a few bits. Should
+// still give the desired behavior, though a little coarsely at times. Perfect paths
+// in terms of travel times is not something that people seem to percieve that much...
+const F32 GraphEdge::csInvSpdTab[InvTabSz + 1] =
+{
+ 0.0, 0.1, 0.2, 0.3, 0.4,
+ 0.6, 0.8, 1.0, 1.2, 1.4,
+
+ 1.7, 2.0, 2.6, 3.5, 4.6,
+ 5.7, 6.8, 8.0, 9.2, 10.4,
+
+ 12.0, 14.1, 17.0, 20.0, 24.0,
+ 30.0, 37.0, 45.0, 54.0, 64.0,
+
+ 80.0, 100.0, 125.0, 156.0, 200.0,
+ 400.0, 800.0, 1600.0, 3200.0, 6400.0,
+
+ 2.56e4, 1.02e5, 4.1e5, 1.6e6, 6.5e6,
+ 2.6e7, 1.05e8, 4.19e8, 1.68e9, 9.9e10,
+
+ 1e11, 1e12, 1e13, 1e14, 1e15,
+ 1e16, 1e17, 1e18, 1e19, 1e20,
+ 1e21, 1e22, 1e23, 1e24,
+
+ // Sentinal value -
+ 1e40,
+};
+
+// Find the 6-bit number using a binary search...
+void GraphEdge::setInverse(F32 is)
+{
+ S32 lo = 0;
+ S32 hi = InvTabSz;
+
+ AssertFatal(is < csInvSpdTab[hi] && is > csInvSpdTab[lo], "Bad edge inverse speed");
+
+ // Find the largest value we're greater than in the table-
+ for (S32 i = InvSpdBits; i > 0; i--)
+ {
+ S32 mid = (hi + lo >> 1);
+ if (is >= csInvSpdTab[mid])
+ lo = mid;
+ else
+ hi = mid;
+ }
+
+ mInverse = lo;
+}
+
+//-------------------------------------------------------------------------------------
+// GraphNode Methods, Default Virtuals
+
+GraphNode::GraphNode()
+{
+ mIsland = -1;
+ mOnPath = 0;
+ mAvoidUntil = 0;
+ mLoc.set(-1,-1,-1);
+ // mOwnedPtr = NULL;
+ // mOwnedCnt = 0;
+ mThreats = 0;
+}
+
+// Derived classes must supply fetcher which takes a buffer to build in. This method
+// patches to that, and should serve fine if pointers to these aren't kept around.
+const Point3F& GraphNode::location() const
+{
+ static Point3F buff[8];
+ static U8 ind = 0;
+ return fetchLoc(buff[ind++ & 7]);
+}
+
+// Outdoor nodes use level (and they start at -1), others don't. Though this is also
+// used by path randomization to determine if it's reasonable to avoid the node.
+S32 GraphNode::getLevel() const
+{
+ return -2;
+}
+
+Point3F GraphNode::getRenderPos() const
+{
+ Point3F buff, adjusted;
+ adjusted = fetchLoc(buff);
+ adjusted.z += 0.3;
+ return adjusted;
+}
+
+// Set if not already set (shouldn't be set already, caller asserts)
+void GraphNode::setIsland(S32 num)
+{
+ AssertFatal(!mFlags.test(GotIsland), "GraphNode::setIsland()");
+ mFlags.set(GotIsland);
+ mIsland = num;
+}
+
+// Ideally this ratio would accurately reflect travel speeds along the slopes.
+// Here we use
+// xy / total If we're going down. Hence XY is net path distance.
+// total / xy If we're going up.
+static F32 getBaseEdgeScale(const Point3F& src, Point3F dst)
+{
+ F32 totalDist = (dst -= src).len();
+ bool goingUp = (dst.z > 0);
+ dst.z = 0;
+ F32 xyDist = dst.len();
+ F32 ratio = 1.0;
+
+ if (xyDist > 0.01 && totalDist > 0.01)
+ {
+ if (goingUp)
+ {
+ ratio = (totalDist / xyDist);
+ if (ratio < 1.2)
+ ratio = 1.0;
+ }
+ else
+ ratio = (xyDist / totalDist);
+
+ // Want to do something between square root and straight value, so we'll
+ // raise it to the 3/4 power here, which will bring it to 1 from either side.
+ ratio = mSqrt(mSqrt(ratio) * ratio);
+ }
+
+ return ratio;
+}
+
+F32 GraphNode::edgeScale(const GraphNode * dest) const
+{
+ // F32 scale = 1.0;
+ F32 scale = getBaseEdgeScale(location(), dest->location());
+
+ if (submerged())
+ scale *= gNavGraph->submergedScale();
+ else if (shoreline())
+ scale *= gNavGraph->shoreLineScale();
+
+ if (dest->submerged())
+ scale *= gNavGraph->submergedScale();
+ else if (dest->shoreline())
+ scale *= gNavGraph->shoreLineScale();
+
+ // Searcher reserves REALLY large numbers (10^20th about) to signal search
+ // failure, so make sure we don't inadvertently interfere with any of that.
+ scale = getMin(scale, 1000000000.0f);
+
+ return scale;
+}
+
+static GraphEdge sEdgeBuff[MaxOnDemandEdges];
+
+bool GraphNode::neighbors(const GraphNode* other) const
+{
+ GraphEdgeArray edgeList = other->getEdges(sEdgeBuff);
+ S32 indexOfThis = getIndex();
+
+ while (GraphEdge * edge = edgeList++)
+ if (edge->mDest == indexOfThis)
+ return true;
+
+ return false;
+}
+
+GraphEdge * GraphNode::getEdgeTo(S32 to) const
+{
+ GraphEdgeArray edgeList = getEdges(sEdgeBuff);
+
+ while (GraphEdge * edge = edgeList++)
+ if (edge->mDest == to)
+ return edge;
+
+ return NULL;
+}
+
+// Virtual that is only used on non-grid nodes
+const GraphEdge * GraphNode::getEdgePtr() const
+{
+ return 0;
+}
+
+// This is only called on nodes known to be terrain.
+F32 GraphNode::terrHeight() const
+{
+ return 1e17;
+}
+
+// Initial use of this is for weighted random selection of nodes for spawn points.
+F32 GraphNode::radius() const
+{
+ return 2.0;
+}
+
+// Not in yet, but we'll use this instead of radius for weighted random spawn points
+F32 GraphNode::area() const
+{
+ return 0.01;
+}
+
+// Used to know thinness of node. Spawn system avoids, as do smoothing edges.
+F32 GraphNode::minDim() const
+{
+ return 0.01;
+}
+
+const Point3F& GraphNode::getNormal() const
+{
+ static const Point3F sStraightUp(0,0,1);
+ return sStraightUp;
+}
+
+// Used for finding how much a node contains a point. More negative => point is
+// more inside.
+NodeProximity GraphNode::containment(const Point3F& point) const
+{
+ NodeProximity nodeProx;
+ nodeProx.mLateral = (point - location()).len() - radius();
+ return nodeProx;
+}
+
+Point3F GraphNode::randomLoc() const
+{
+ return location();
+}
+
+// Nodes which have volumes associated. Added to allow the transient nodes to
+// respond with the proper volume that they may be associated with.
+S32 GraphNode::volumeIndex() const
+{
+ return getIndex();
+}
+
+// Set the avoidance if it isn't already. The inmportant flag will override though.
+void GraphNode::setAvoid(U32 duration, bool isImportant)
+{
+ U32 curTime = Sim::getCurrentTime();
+
+ if (curTime > mAvoidUntil || isImportant)
+ {
+ mAvoidUntil = curTime + duration;
+ mFlags.set(StuckAvoid, isImportant);
+
+ // On avoidance, also mark neighbors-
+ if (isImportant)
+ {
+ GraphEdgeArray edgeList = getEdges(sEdgeBuff);
+ while (GraphEdge * edge = edgeList++)
+ {
+ GraphNode * node = gNavGraph->lookupNode(edge->mDest);
+ node->mAvoidUntil = mAvoidUntil;
+ node->mFlags.clear(StuckAvoid); // slight avoid here.
+ }
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------
+// Default RegularNode Virtuals
+
+RegularNode::RegularNode()
+{
+ mIndex = -1;
+ mNormal.set(0,0,1);
+}
+
+GraphEdgeArray RegularNode::getEdges(GraphEdge*) const
+{
+ return GraphEdgeArray(mEdges.size(), mEdges.address());
+}
+
+GraphEdge & RegularNode::pushEdge(GraphNode * node)
+{
+ GraphEdge edge;
+ edge.mDest = node->getIndex();
+ mEdges.push_back(edge);
+ return mEdges.last();
+}
+
+const Point3F& RegularNode::getNormal() const
+{
+ return mNormal;
+}
+
+//-------------------------------------------------------------------------------------
+
+void GraphNodeList::setFlags(U32 bits)
+{
+ for(const_iterator it = begin(); it != end(); it++)
+ if(*it)
+ (*it)->mFlags.set(bits);
+}
+
+void GraphNodeList::clearFlags(U32 bits)
+{
+ for(const_iterator it = begin(); it != end(); it++)
+ if(*it)
+ (*it)->mFlags.clear(bits);
+}
+
+S32 GraphNodeList::searchSphere(const SphereF& sphere, const GraphNodeList& listIn)
+{
+ clear();
+ F32 radiusSquared = (sphere.radius * sphere.radius);
+ for(const_iterator it = listIn.begin(); it != listIn.end(); it++)
+ if(*it)
+ if((sphere.center - (*it)->location()).lenSquared() < radiusSquared)
+ push_back(*it);
+ return size();
+}
+
+GraphNode * GraphNodeList::closest(const Point3F& loc, bool)
+{
+ GraphNode * bestNode = NULL;
+ F32 bestRadSquared = 1e12, dSqrd;
+ for (const_iterator it = begin(); it != end(); it++)
+ if (*it)
+ if ((dSqrd = (loc - (*it)->location()).lenSquared()) < bestRadSquared)
+ {
+ bestRadSquared = dSqrd;
+ bestNode = *it;
+ }
+ return bestNode;
+}
+
+// Add the node pointer if it's not already in the list.
+bool GraphNodeList::addUnique(GraphNode * node)
+{
+ for (iterator n = begin(); n != end(); n++)
+ if (* n == node)
+ return false;
+ push_back(node);
+ return true;
+}
+
diff --git a/ai/graphBase.h b/ai/graphBase.h
new file mode 100644
index 0000000..a8507e1
--- /dev/null
+++ b/ai/graphBase.h
@@ -0,0 +1,228 @@
+//-----------------------------------------------------------------------------
+// V12 Engine
+//
+// Copyright (c) 2001 GarageGames.Com
+// Portions Copyright (c) 2001 by Sierra Online, Inc.
+//-----------------------------------------------------------------------------
+
+#ifndef _GRAPHBASE_H_
+#define _GRAPHBASE_H_
+
+#ifndef _OVECTOR_H_
+#include "ai/oVector.h"
+#endif
+
+class GraphNodeList;
+class NavigationGraph;
+typedef S32 GraphQIndex;
+typedef U64 GraphThreatSet;
+
+//-------------------------------------------------------------------------------------
+// Graph Edges - See graphNodeBase.cc for methods, comments.
+
+class GraphEdge
+{
+ enum {InvSpdBits = 6, InvTabSz = 1 << InvSpdBits};
+ static const F32 csInvSpdTab[InvTabSz + 1];
+
+ U8 mSteep : 1;
+ U8 mInverse : InvSpdBits;
+ U8 mOnPath : 1;
+ U8 mJet : 1; // These are only private since they're not
+ U8 mDown : 1; // straight regular types. Otherwise this
+ U8 mJump : 1; // class is kept pretty open for the sake of
+ U8 mTeam : 5; // speed in the debug build (since edges are
+ U8 mLateral; // used a lot inlines slow things down
+ U8 mHopOver; // by a noticeable amount).
+
+ public:
+ F32 mDist;
+ S16 mDest;
+ S16 mBorder;
+
+ GraphEdge();
+ bool empty() const {return mDest < 0;}
+ bool isBorder() const {return mBorder >= 0;}
+ bool isJetting() const {return mJet;}
+ bool isDown() const {return mDown;}
+ bool isJump() const {return mJump;}
+ bool isSteep() const {return mSteep;}
+ U8 getTeam() const {return mTeam;}
+ U8 getLateral() const {return mLateral;}
+ F32 getHop() const {return mapU8ToJetHop(mHopOver);}
+ F32 getInverse() const {return csInvSpdTab[mInverse];}
+ bool hasHop() const {return mHopOver!=0;}
+ void setJetting() {mJet = 1;}
+ void setDown(bool b) {mDown = b;}
+ void setJump(bool b) {mJump = b;}
+ void setTeam(U8 team) {mTeam = team;}
+ void setLateral(U8 amt) {mLateral = amt;}
+ void setSteep(bool b) {mSteep = b;}
+ void setImpossible() {setInverse(1e37);}
+ void setHop(F32 amt) {mHopOver = mapJetHopToU8(amt);}
+ void setHop(U8 persistAmt) {mHopOver = persistAmt;}
+ F32 getTime() const {return (mDist * getInverse());}
+ void copyInverse(const GraphEdge* e) {mInverse = e->mInverse;}
+ bool canJet(const F32* ratings, bool both=false) const;
+ void setInverse(F32 inv);
+ const char* problems() const;
+};
+
+typedef OVector GraphEdgeList;
+typedef Vector GraphEdgePtrs;
+
+class GraphEdgeArray
+{
+ S32 count;
+ GraphEdge * edges;
+ public:
+ GraphEdgeArray() {count = 0; edges = NULL;}
+ GraphEdgeArray(S32 c, GraphEdge* e) {count = c; edges = e;}
+ GraphEdge * operator++(int) {return (count ? (count--, edges++) : 0);}
+ S32 numEdges() const {return count;}
+ void incCount() {count++;}
+};
+
+struct NodeProximity
+{
+ F32 mLateral, mHeight, mAboveC;
+ void makeBad() {mLateral=1e13;}
+ void makeGood() {mAboveC = mLateral = -1e13;}
+ operator F32&() {return mLateral;}
+ NodeProximity() {makeBad();}
+ bool inside() const {return (mLateral <= 0 && mAboveC <= 0);}
+ bool insideZ() const {return (mAboveC <= 0);}
+ bool possible() const;
+};
+
+//-------------------------------------------------------------------------------------
+// Virtual Base Class For Run Time Graph Nodes
+
+#define GraphMaxOnPath 31
+
+class GraphNode // graphNodeBase.cc
+{
+ friend class NavigationGraph;
+ friend class GraphNodeList;
+
+ protected:
+ enum{
+ NodeSpecific = 0xFF,
+ Grid = BIT(8),
+ Outdoor = BIT(9),
+ Transient = BIT(10),
+ ExtendedGrid = BIT(11),
+ Indoor = BIT(12),
+ BelowPortal = BIT(13),
+ GotIsland = BIT(14),
+ Transient0 = BIT(15),
+ Transient1 = (Transient0<<1),
+ HaveTransient = (Transient0|Transient1),
+ PotentialSeed = BIT(19),
+ UsefulSeed = BIT(20),
+ Flat = BIT(21),
+ Algorithmic = BIT(22),
+ StuckAvoid = BIT(23),
+ Inventory = BIT(24),
+ Render0 = BIT(25),
+ Shadowed = BIT(26),
+ // Reserve three contiguous bits for shoreline, which is wider for lava.
+ LiquidPos = 27,
+ LiquidZone = (0x7 << LiquidPos),
+ Submerged = BIT(LiquidPos + 0),
+ ShoreLine = BIT(LiquidPos + 1),
+ LavaBuffer = BIT(LiquidPos + 2),
+ };
+ BitSet32 mFlags;
+ S16 mIsland;
+ S16 mIndex;
+ S8 mOnPath;
+ S8 mLevel;
+ // U16 mOwnedCnt;
+ // GraphEdge * mOwnedPtr;
+ U32 mAvoidUntil;
+ GraphThreatSet mThreats;
+ GraphEdgeList mEdges;
+ Point3F mLoc;
+
+ protected:
+ GraphEdge * pushTransientEdge(S32 dest);
+ void popTransientEdge();
+
+ public:
+ GraphNode();
+
+ // One-liners
+ void set(U32 bits) {mFlags.set(bits);}
+ void clear(U32 bits) {mFlags.clear(bits);}
+ bool test(U32 bits) const {return mFlags.test(bits);}
+ bool grid() const {return test(Grid);}
+ bool outdoor() const {return test(Outdoor);}
+ bool indoor() const {return test(Indoor);}
+ bool belowPortal() const {return test(BelowPortal);}
+ bool shoreline() const {return test(ShoreLine);}
+ bool shadowed() const {return test(Shadowed);}
+ bool submerged() const {return test(Submerged);}
+ bool stuckAvoid() const {return test(StuckAvoid);}
+ bool transient() const {return test(Transient);}
+ bool gotIsland() const {return test(GotIsland);}
+ bool inventory() const {return test(Inventory);}
+ bool render0() const {return test(Render0);}
+ bool algorithmic() const {return test(Algorithmic);}
+ bool flat() const {return test(Flat);}
+ bool canHaveBorders() const {return test(Indoor|Transient);}
+ bool liquidZone() const {return test(LiquidZone);}
+ bool potentialSeed() const {return test(PotentialSeed);}
+ bool usefulSeed() const {return test(UsefulSeed);}
+ bool uselessSeed() const {return potentialSeed() && !usefulSeed();}
+ S32 island() const {return mIsland;}
+ U32 onPath() const {return mOnPath;}
+ U32 avoidUntil() const {return mAvoidUntil;}
+ void setOnPath() {mOnPath = GraphMaxOnPath;}
+ void decOnPath() {if(mOnPath) mOnPath--;}
+ void setUsefulSeed() {set(UsefulSeed);}
+ GraphThreatSet& threats() {return mThreats;}
+
+ // Pure virtuals.
+ virtual const Point3F& fetchLoc(Point3F& buff) const = 0;
+ virtual GraphEdgeArray getEdges(GraphEdge* buff) const = 0;
+ virtual S32 getIndex() const = 0;
+
+ // Virtuals-
+ virtual const Point3F& location() const;
+ virtual const Point3F& getNormal() const;
+ virtual const GraphEdge* getEdgePtr() const;
+ virtual S32 getLevel() const;
+ virtual S32 volumeIndex() const;
+ virtual F32 edgeScale(const GraphNode* to) const;
+ virtual NodeProximity containment(const Point3F& loc) const;
+ virtual Point3F getRenderPos() const;
+ virtual Point3F randomLoc() const;
+ virtual F32 terrHeight() const;
+ virtual F32 radius() const;
+ virtual F32 minDim() const;
+ virtual F32 area() const;
+
+ // Other:
+ void setIsland(S32 islandNum);
+ void setAvoid(U32 duration, bool important=false);
+ bool neighbors(const GraphNode*) const;
+ GraphEdge* getEdgeTo(S32 to) const;
+ GraphEdge* getEdgeTo(const GraphNode* n) const {return getEdgeTo(n->getIndex());}
+};
+
+//-------------------------------------------------------------------------------------
+
+class GraphNodeList : public VectorPtr
+{
+ public:
+ void setFlags(U32 bitset);
+ void clearFlags(U32 bitset);
+ S32 searchSphere(const SphereF& sphere, const GraphNodeList& listIn);
+ GraphNode* closest(const Point3F& loc, bool los=false);
+ bool addUnique(GraphNode * node);
+};
+
+typedef AxisAlignedBSP GraphBSPTree;
+
+#endif
diff --git a/ai/graphBridge.cc b/ai/graphBridge.cc
new file mode 100644
index 0000000..531e1f3
--- /dev/null
+++ b/ai/graphBridge.cc
@@ -0,0 +1,974 @@
+//-----------------------------------------------------------------------------
+// V12 Engine
+//
+// Copyright (c) 2001 GarageGames.Com
+// Portions Copyright (c) 2001 by Sierra Online, Inc.
+//-----------------------------------------------------------------------------
+
+#include "ai/graph.h"
+#include "ai/graphLOS.h"
+#include "ai/graphBridge.h"
+
+//-------------------------------------------------------------------------------------
+
+// Controls generation-
+static bool sSpawnGraph = false;
+
+// Numbers that are different between the two types of graphs. Most jet connections
+// on NAV generation use the smaller lateral value, but we special case for large
+// terrain-to-terrain hops that are passed through. cf. FireStorm.
+#define NAVLateralMax 60.0
+#define IslandHopLateralMax 120.0
+#define SPNLateralMax 100.0
+static const Point3F sBoxOffNAV(IslandHopLateralMax, IslandHopLateralMax, 10000.0);
+static const Point3F sBoxOffSPN(SPNLateralMax, SPNLateralMax, 10000.0);
+
+//-------------------------------------------------------------------------------------
+
+static const U32 sLOSMask = InteriorObjectType
+ | StaticShapeObjectType
+ | StaticObjectType
+ | WaterObjectType
+ | TerrainObjectType;
+
+static Point3F * sBreakPt1 = 0, * sBreakPt2 = 0;
+static SphereF * sMagSphere;
+static F32 sBreakRangeXY = 0.5, sBreakRangeZ = 2.0;
+static const S32 sGraphFanCount = 10; // ==> 1-11-1, reduced, should be fine.
+static const F32 sGraphFanWidth = 1.0;
+static const F32 sWideFanRaise = 1.3;
+
+// Clear distance assures they can get over a ledge, land Dist assures there's purchase.
+// Second clear dist is for case when one node is a portal (i.e. chutes)
+static const F32 sClearDists[2] = {1.3, 1.0};
+static const F32 sLandDists[2] = {0.4, 0.2};
+static const F32 sJumpAngDot = 0.707;
+
+// NOT SURE ABOUT THIS # Sometimes we want more - maybe allow other checks to insure
+// that things work when it's small (clear dist, etc.) There are some small ledge
+// hops which are needed - but hop over code will probably assure...
+static const F32 sMinXY = 2.0;
+static const F32 sMinXYSqrd = (sMinXY * sMinXY);
+
+static const Point3F sMinAboveVec(0,0,2.0);
+
+#define SlightlyOffFloor 0.157
+#define MaxHopOverXY 37.0
+// lhpatch- Make this # go on graph (was barely too small in one map at 17)
+#define MaxWalkableXY 20.0
+#define MinLateralThresh 12.0
+
+//-------------------------------------------------------------------------------------
+
+#define SquareOf(x) ((x)*(x))
+
+static bool bridgeable(const Point3F& from, const Point3F& to, bool bothOutdoor)
+{
+ F32 lateralThresh;
+
+ if (sSpawnGraph)
+ {
+ lateralThresh = SPNLateralMax;
+ }
+ else
+ {
+ if (bothOutdoor)
+ lateralThresh = IslandHopLateralMax;
+ else
+ lateralThresh = NAVLateralMax;
+
+ // Pull down the lateral thresh for tall hops. Basically subtract off of it for
+ // every meter of Z, but keep a minimum amount, which is necessary to assure
+ // floating bases do get bridged (if only the downward hop is doable).
+ if ( (lateralThresh -= mFabs(from.z - to.z)) < MinLateralThresh )
+ lateralThresh = MinLateralThresh;
+ }
+
+ Point2F lateral(from.x - to.x, from.y - to.y);
+ return (lateral.lenSquared() < SquareOf(lateralThresh));
+}
+
+//-------------------------------------------------------------------------------------
+
+// Get the fan vector in passed ref, and return number of increments. horzVec
+// is a horizontal vector guaranteed to have length.
+static S32 calcFanVector(const VectorF& horzVec, VectorF& fanIncOut)
+{
+ fanIncOut.set(-horzVec.y, horzVec.x, 0);
+ fanIncOut.normalize(sGraphFanWidth / F32(sGraphFanCount));
+ return sGraphFanCount;
+}
+
+//-------------------------------------------------------------------------------------
+// Some code for isolating cases in the debugger. Mainly for focusing on a couple of
+// nodes to see why they're not getting bridged. See navGraph.genDebug() in graph.cc
+
+static bool doBreak() {return true;}
+
+static bool checkEnds(const Point3F& n1, const Point3F& n2)
+{
+ if (mFabs(n1.x - sBreakPt1->x) < sBreakRangeXY)
+ if (mFabs(n2.x - sBreakPt2->x) < sBreakRangeXY)
+ if (mFabs(n2.y - sBreakPt2->y) < sBreakRangeXY)
+ if (mFabs(n1.y - sBreakPt1->y) < sBreakRangeXY)
+ if (mFabs(n1.z - sBreakPt1->z) < sBreakRangeZ)
+ if (mFabs(n2.z - sBreakPt2->z) < sBreakRangeZ)
+ return doBreak();
+
+ return false;
+}
+
+//-------------------------------------------------------------------------------------
+
+GraphBridge::GraphBridge(const GraphNodeList& mainList, S32 islands,
+ BridgeDataList& listOut)
+ : mMainList(mainList),
+ mIslands(islands),
+ mBridgesOut(listOut)
+{
+ heedSeeds(false);
+}
+
+//-------------------------------------------------------------------------------------
+
+// This is a bit indirect... but here's where we control which brdiges to attempt on
+// the two passes. On the first, all seeds are ignored. On the second we only bridge
+// where at least one useful seed is present, and NO useless seed is involved.
+bool GraphBridge::heedThese(const GraphNode* from, const GraphNode* to)
+{
+ if (mHeedSeeds)
+ if (from->usefulSeed() || to->usefulSeed())
+ return !(from->uselessSeed() || to->uselessSeed());
+ else
+ return false;
+ else
+ return !(from->potentialSeed() || to->potentialSeed());
+}
+
+void GraphBridge::heedSeeds(bool b)
+{
+ mHeedSeeds = b;
+}
+
+//-------------------------------------------------------------------------------------
+
+// Try straight distance for the ratios. After we added good scale values on the
+// edges, our bridger makes too many edges since the ratios are off. Until this is
+// addressed (if indeed it really needs it), let's use straight distance on edges.
+F32 GraphBridge::getEdgeTime(const GraphEdge * edge)
+{
+ return edge->mDist;
+}
+
+//-------------------------------------------------------------------------------------
+
+// Here's where we do all the work - try to see if a bridge exists.
+//
+bool GraphBridge::tryToBridge(const GraphNode* from, const GraphNode* to, F32 heuristic)
+{
+ // This is actually where the separate passes (normal, then seeding) is controlled-
+ if (!heedThese(from, to))
+ return false;
+
+ // Check for in, or near, liquids.
+ if (from->liquidZone() || to->liquidZone())
+ return false;
+
+ S32 fromIndex = from->getIndex();
+ S32 toIndex = to->getIndex();
+
+ Point3F fromLoc = from->location();
+ Point3F toLoc = to->location();
+
+ bool oneIsPortal = (from->belowPortal() ^ to->belowPortal());
+
+ // Debug stopping points.
+ if (sBreakPt1 && sBreakPt2)
+ {
+ if (checkEnds(fromLoc, toLoc) || checkEnds(toLoc, fromLoc))
+ if (oneIsPortal)
+ doBreak();
+ }
+
+ // NOTE: we can make the OffFloor amount smaller if outdoor node locations are
+ // raised up to always be above terrain. (U16 rounding I think)
+ fromLoc.z += SlightlyOffFloor;
+ toLoc.z += SlightlyOffFloor;
+
+ bool bothOutside = (from->outdoor() && to->outdoor());
+
+ if (from->neighbors(to))
+ return false;
+ else
+ {
+ // Prefer to bridge going UP to insure that the ratio check gets handled (larger
+ // travel times going up).
+ if (fromLoc.z > toLoc.z)
+ return false;
+ else if (fromLoc.z == toLoc.z && fromIndex > toIndex)
+ return false;
+ }
+
+ // Create low, high, mid (point above low, but on level with high).
+ Point3F mid, low, high;
+ low = fromLoc;
+ high = toLoc;
+ (mid = low).z = high.z;
+ F32 zDist = (high.z - low.z);
+
+ // Get 2D dist
+ Point2F xyVec(toLoc.x - fromLoc.x, toLoc.y - fromLoc.y);
+ F32 xyDistSq = xyVec.lenSquared();
+
+ // Handles all the LOS work
+ Loser los(sLOSMask, true);
+
+ if ((xyDistSq > 0.0001) && bridgeable(fromLoc, toLoc, bothOutside))
+ {
+ Point3F aboveM, aboveH, clearPt, landRoom, across;
+ bool isChute = false, chuteTop = false;
+ bool walkable = false, gapWalk = false;
+ bool canCheckJet = true, direct = false;
+ bool makeBridge = false;
+ F32 wall;
+
+ // See if we shouldn't look for jetting.
+ F32 xyDist = mSqrt(xyDistSq);
+ if (from->inventory() || to->inventory() || xyDistSq < sMinXYSqrd)
+ canCheckJet = false;
+ else
+ {
+ if (from->getNormal().z < sJumpAngDot || to->getNormal().z < sJumpAngDot)
+ canCheckJet = false;
+ }
+
+ bool checkLandRoom = (zDist > 1.0 && to->indoor());
+
+ Point3F aboveVec = sMinAboveVec;
+
+ aboveM = mid + aboveVec;
+ aboveH = high + aboveVec;
+ across = (high - mid);
+ across.normalize();
+ clearPt = mid + (sClearDists[oneIsPortal] * across);
+ landRoom = high - (sLandDists[oneIsPortal] * across);
+
+ // Move back on the up checks so the guy has room behind him. We have to
+ // raise up the low a little bit to account for slope, but of course
+ // we can't raise more than height.
+ if (canCheckJet)
+ {
+ // There are two types of chute situations here, one for if we have this
+ // connection goes to the top of the chute, the other for middle ones.
+ // For the top we do a slope check off the collision up there and are lax
+ // about other LOS checks. For the middle we require a couple more.
+ if (ChuteHints::Info info = gNavGraph->getChutes().info(low, mid))
+ {
+ isChute = true;
+ if (info == ChuteHints::ChuteTop)
+ chuteTop = true;
+ }
+
+ // Might still want to do a little of this on chutes... ?
+ if (!isChute)
+ {
+ VectorF backUpVec = (across * 0.8);
+ low -= backUpVec;
+ low.z += getMin(2.0f, zDist);
+ mid -= backUpVec;
+ aboveM -= backUpVec;
+ }
+ }
+
+ bool walkOff = los.haveLOS(mid, high);
+ bool hopOver = false;
+ if (!walkOff && xyDist < MaxHopOverXY)
+ {
+ hopOver = los.findHopLine(mid, high, 2, wall);
+ if (hopOver)
+ {
+ mid.z += wall;
+ high.z += wall;
+ aboveM.z += wall;
+ aboveH.z += wall;
+ }
+ }
+
+ if (walkOff || hopOver)
+ if (los.haveLOS(high, aboveH))
+ if (chuteTop || los.haveLOS(low, aboveM))
+ if (chuteTop || los.haveLOS(aboveM, aboveH))
+ {
+ // Don't do walk checks between two outdoor nodes-
+ if (!isChute && (!from->outdoor() || !to->outdoor()))
+ {
+ // do gap walk for special case of small zdiff or direct LOS
+ direct = los.haveLOS(low, high);
+ if (direct)
+ gapWalk = los.walkOverGaps(low, high, 0.8);
+ else if (zDist < gNavGlobs.mStepHeight)
+ gapWalk = los.walkOverGaps(mid, high, 0.8);
+
+ // If there's a wall to hop over, don't do the walk code. Also, if both
+ // nodes are in the same island, we only consider walking if the
+ // heuristic (ratio of best-path-so-far to straight line dist) is high.
+ if (walkOff)
+ if ((from->island() != to->island()) || (heuristic > 3.7))
+ walkable = los.walkOverBumps(aboveM, aboveH);
+ }
+
+ if (walkable || canCheckJet)
+ {
+ // Do the fanned LOS - just replicate the above slew of checks.
+ VectorF fanInc;
+ S32 numFan = calcFanVector(across, fanInc);
+
+ // Added extra checks 1-11-1: Must do a wider fanned check on
+ // low -> aboveM. Connections without room are slipping through
+ // the tests because they are diagonal. Also must from a point
+ // below the clear point, else they effectively can't clear on
+ // tall hops.
+ bool ignoreWide = (chuteTop || (zDist < sWideFanRaise + 0.1));
+ Point3F aboveL, belowC;
+ if (!ignoreWide)
+ {
+ aboveL.set(low.x, low.y, low.z + sWideFanRaise);
+ belowC.set(clearPt.x, clearPt.y, low.z + sWideFanRaise);
+ }
+
+ if (los.fannedLOS1(high, aboveH, fanInc, numFan))
+ if (los.fannedLOS1(high, mid, fanInc, numFan))
+ if (chuteTop || los.fannedLOS1(low, aboveM, fanInc, numFan))
+ if (chuteTop || los.fannedLOS2(aboveM, aboveH, fanInc, numFan))
+ if (ignoreWide || los.fannedLOS2(aboveL, aboveM, fanInc, numFan))
+ {
+ if (!isChute)
+ {
+ if (gapWalk)
+ if (direct) // in the direct case we need one more fanned LOS
+ gapWalk = los.fannedLOS1(low, high, fanInc, numFan);
+
+ if (gapWalk)
+ walkable = true;
+ else if (walkable) // (i.e. walkable so far in our checks)
+ walkable = los.fannedBumpWalk(aboveM, aboveH, fanInc, numFan);
+ }
+
+ if (walkable)
+ {
+ // Well, it's too bad we had to do all this work on to find long
+ // connections that we throw out, but we have to figure out if it
+ // needs jet.
+ makeBridge = (xyDist < MaxWalkableXY);
+ }
+ else
+ {
+ if (canCheckJet && (xyDist > GraphJetBridgeXY))
+ {
+ // The clear check is only needed for jetting connections,
+ // same with landing room check - which we should only use when
+ // going up to indoor nodes (and one LOS failure is adequate)
+ if (!checkLandRoom || !los.haveLOS(low, landRoom))
+ if (isChute || los.haveLOS(low, clearPt))
+ if (isChute || los.fannedLOS1(low, clearPt, fanInc, numFan))
+ if (ignoreWide || los.fannedLOS2(belowC, clearPt, fanInc, numFan))
+ {
+ // Check for bonk due to jumps, which add a couple meters.
+ // We want this to pass chutes and other indoor situations Ok,
+ // so do LOS up at each end and use that as endpoints.
+
+ F32 jumpAdd = (oneIsPortal && xyDist < LiberalBonkXY ? 0.6 : gNavGlobs.mJumpAdd);
+
+ aboveM.z += (los.heightUp(aboveM, jumpAdd) - 0.1);
+ aboveH.z += (los.heightUp(aboveH, jumpAdd) - 0.1);
+
+ if (isChute || los.fannedLOS2(aboveM, aboveH, fanInc, numFan))
+ makeBridge = true;
+ }
+
+ // LOS keeps track of this at lowest level- don't allow
+ // jetting connections through force fields.
+ if (los.mHitForceField)
+ makeBridge = false;
+ }
+ }
+ }
+ }
+ }
+
+ if (makeBridge)
+ {
+ GraphBridgeData bridge1(fromIndex, toIndex);
+
+ if (walkable)
+ bridge1.setWalkable();
+
+ if (hopOver)
+ bridge1.setHop(wall);
+
+ mBridgesOut.push_back(bridge1);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// We use a searcher to find those same-island nodes which we should try to bridge
+// to - namely those who are far on the path relative to straight line distance.
+// This will hopefully find for us all indoor jetting connections which are 'useful',
+// as well as finding which terrain nodes should be bridged (like over water).
+void GraphBridge::onQExtraction()
+{
+ GraphNode * mExtractNode = extractedNode();
+ if (mExtractNode != mCurrent)
+ {
+ if (mSameIslandMark.test(mHead.mIndex))
+ {
+ AssertFatal(mSameIslandCount > 0, "GraphBridge::onQExtraction()");
+
+ // done when we've visited all the possibilities in the same island
+ if (--mSameIslandCount == 0)
+ setEarlyOut(true);
+
+ Point3F nodeLoc = mExtractNode->location();
+ F32 straightLineDist = (mStartLoc - nodeLoc).len();
+ if (straightLineDist > 0.01)
+ {
+ bool bothOutdoor = (mFromOutdoor && mExtractNode->outdoor());
+ F32 ratio = distSoFar() / straightLineDist;
+ F32 thresh = mRatios[bothOutdoor];
+
+ if (ratio > thresh)
+ {
+ // Same-island bridges with good ratios are considered first...
+ Candidate candidate(mExtractNode, -ratio);
+ mCandidates.push_back(candidate);
+ }
+ }
+
+ mSameIslandMark.clear(mHead.mIndex);
+ }
+ }
+}
+
+// Do all the bridging. Has two modes- spawn and regular nav. Each iteration performs
+// one loop of bridging- from one source node to all nodes that need to be bridged.
+S32 GraphBridge::findAllBridges()
+{
+ S32 nodeCount = mMainList.size();
+ Vector islandCross(mIslands);
+ GraphNodeList considerList;
+
+ mSameIslandMark.setSize(nodeCount);
+ mSameIslandMark.clear();
+
+ mRatios[1] = 1.6; // Outdoor-to-outdoor- try a little smaller ratio than-
+ mRatios[0] = 2.2; // Other combinations of connections
+
+ setSizeAndClear(islandCross, mIslands);
+
+ for (S32 i = 0; i < nodeCount; i++)
+ {
+ if ((mCurrent = mMainList[i]) != NULL)
+ {
+ // Get list of all possibilities (bound box query)
+ mStartLoc = mCurrent->location();
+
+ if (sMagSphere)
+ {
+ Point3F pt = sMagSphere->center;
+ if ((pt -= mStartLoc).lenSquared() > SquareOf(sMagSphere->radius))
+ continue;
+ }
+
+ Point3F boxOff = (sSpawnGraph ? sBoxOffSPN : sBoxOffNAV);
+ Box3F area(mStartLoc - boxOff, mStartLoc + boxOff);
+ S32 curIsland = mCurrent->island();
+
+ gNavGraph->getNodesInBox(area, considerList);
+
+ // Divy out those in separate island, and mark those in the same island for
+ // consideration by the special seacher.
+ mCandidates.clear();
+ mSameIslandCount = 0;
+ for (S32 j = 0; j < considerList.size(); j++) {
+ GraphNode * consider = considerList[j];
+ if (consider != mCurrent) {
+ if (consider->island() == curIsland) {
+ if (!sSpawnGraph) {
+ mSameIslandMark.set(consider->getIndex());
+ mSameIslandCount++;
+ }
+ }
+ else {
+ // Bridges to different islands sorted by distance
+ F32 dist = (mStartLoc - consider->location()).lenSquared();
+ Candidate candidate(consider, dist);
+ mCandidates.push_back(candidate);
+ }
+ }
+ }
+
+ // Special search to find which same-island nodes need consideration. We want
+ // to bridge same-island nodes if their ground travel distance is far relative
+ // to their straight-line distance.
+ if (!sSpawnGraph)
+ {
+ setEarlyOut(false);
+ mFromOutdoor = mCurrent->outdoor();
+ performSearch(mCurrent, NULL);
+ }
+
+ // Candidates considered in order of best-ness heuristic
+ mCandidates.sort();
+
+ // Try to bridge to all candidates which remain.
+ setSizeAndClear(islandCross, mIslands);
+ for (CandidateList::iterator c = mCandidates.begin(); c != mCandidates.end(); c++)
+ {
+ if (sSpawnGraph)
+ {
+ // For spawn we only need to cross to an island once.
+ if (!islandCross[c->node->island()])
+ if (tryToBridge(mCurrent, c->node))
+ islandCross[c->node->island()] = true;
+ }
+ else
+ {
+ // For regular graph, allow a certain number of bridges to each island.
+ // Need to sort list, and vary base on island size. We pass in the
+ // heuristic # for avoiding too many walking connections.
+ if (islandCross[c->node->island()] < 3)
+ if (tryToBridge(mCurrent, c->node, -(c->heuristic)))
+ islandCross[c->node->island()]++;
+ }
+ }
+ }
+ }
+
+ return mBridgesOut.size();
+}
+
+S32 gCountReplacementEdges, gCountUnreachable;
+
+// This checks to see if outdoor neighbor edges need to be modified or removed due
+// to steepness. If so, a special 'replacement' bridge is created, which either
+// removes the default one at startup, or converts it to a jetting connection. Note
+// that this also needs to remove steep edges underwater as well.
+void GraphBridge::checkOutdoor(const GraphNode* from, const GraphNode* to)
+{
+ static const F32 stepD = 0.3;
+
+ Point3F fromLoc = from->location();
+ Point3F toLoc = to->location();
+
+ // Vector for stepping sideways, and across. We're given that these are
+ // non-zero vectors since from and to are separate neighbors.
+ Point3F across = (toLoc - fromLoc);
+ Point3F sideVec(across.y, -across.x, 0);
+ sideVec.normalize(0.8);
+
+ // Figure out step across vector, and number of iterations.
+ across.z = 0;
+ F32 dist2D = across.len();
+ S32 numSteps = S32(dist2D / stepD + 1.0);
+ across /= F32(numSteps);
+
+ // Get top and bottom points. Add amount should be enough given that the
+ // consolidator generally won't pass really large peaks between nodes.
+ F32 maxZ = getMax(fromLoc.z, toLoc.z);
+ Point3F stepVec(fromLoc.x, fromLoc.y, maxZ);
+
+ // Step three separate lines across to account for player width.
+ VectorF normal;
+ F32 height, previousZ, highestZ = -1e12;
+ bool removeEdge = false;
+ bool walkable = true;
+ stepVec -= sideVec;
+
+ for (F32 sideWise = 0.0; sideWise < 2.1 && !removeEdge; sideWise += 1.0)
+ {
+ Point3F point = (stepVec + sideVec * sideWise);
+
+ if (gNavGraph->terrainHeight(point, &height))
+ highestZ = getMax(previousZ = height, highestZ);
+ else
+ removeEdge = true;
+
+ // Do loop plus one extra for last point. Look for too steep a slope if our Z
+ // is increasing. This means we can't walk. Also check to see if it's possible
+ // to jet - namely if the highest point isn't too far above higher node.
+ for (S32 N = 0; N <= numSteps && !removeEdge; N++, point += across)
+ {
+ if (gNavGraph->terrainHeight(point, &height, &normal))
+ {
+ // Going up and too steep?
+ if (normal.z < gNavGlobs.mWalkableDot && height > previousZ)
+ walkable = false;
+ highestZ = getMax(previousZ = height, highestZ);
+ }
+ else
+ removeEdge = true;
+ }
+ }
+
+ // Most of the time, this should happen-
+ if (walkable && !removeEdge)
+ return;
+
+ GraphBridgeData bridge(from->getIndex(), to->getIndex());
+ bridge.setReplacement();
+
+ // See if we can at least jet.
+ if (!removeEdge &&
+ (from->getNormal().z >= sJumpAngDot && to->getNormal().z >= sJumpAngDot) &&
+ (!from->liquidZone() && !to->liquidZone()) &&
+ (highestZ - maxZ < 5.0)
+ )
+ {
+ gCountReplacementEdges++;
+ bridge.setHop(highestZ - maxZ + 1.0);
+ }
+ else
+ {
+ gCountUnreachable++;
+ bridge.setUnreachable();
+ }
+
+ mBridgesOut.push_back(bridge);
+}
+
+// Special pass to find those outdoor neighbors that are too steep to walk up. The
+// collision checking in checkOutdoor() only masks with terrain since it is already
+// established that there are no other obstructions along the ground.
+void GraphBridge::trimOutdoorSteep()
+{
+ gCountReplacementEdges = 0;
+ gCountUnreachable = 0;
+
+ S32 nodeCount = mMainList.size();
+ for (S32 i = 0; i < nodeCount; i++)
+ if (GraphNode * from = mMainList[i])
+ if (from->outdoor()) {
+ GraphEdgeArray edges = from->getEdges(NULL);
+ while (GraphEdge * e = edges++)
+ if (!e->isJetting())
+ if (GraphNode * to = gNavGraph->lookupNode(e->mDest))
+ if (to->outdoor())
+ checkOutdoor(from, to);
+ }
+}
+
+//-------------------------------------------------------------------------------------
+
+S32 QSORT_CALLBACK GraphBridge::CandidateList::cmpCandidates(const void * a,const void * b)
+{
+ F32 A = ((Candidate*)a)->heuristic;
+ F32 B = ((Candidate*)b)->heuristic;
+ return (A < B ? -1 : (A > B ? 1 : 0));
+}
+
+void GraphBridge::CandidateList::sort()
+{
+ dQsort((void* )(this->address()), this->size(), sizeof(Candidate), cmpCandidates);
+}
+
+//-------------------------------------------------------------------------------------
+
+// Something to make it easy to quickly check out the graph generation in a particular
+// area by only doing bridge building in that vicinity.
+void NavigationGraph::setGenMagnify(const SphereF * sphere,
+ const Point3F * end1, const Point3F * end2,
+ F32 xyCheck, F32 zCheck)
+{
+ static SphereF sSphere;
+ static Point3F sPoint1, sPoint2;
+
+ sMagSphere = NULL;
+ sBreakPt2 = sBreakPt1 = NULL;
+
+ if (sphere)
+ *(sMagSphere = &sSphere) = *sphere;
+ if (end1)
+ *(sBreakPt1 = &sPoint1) = *end1;
+ if (end2)
+ *(sBreakPt2 = &sPoint2) = *end2;
+
+ sBreakRangeXY = xyCheck;
+ sBreakRangeZ = zCheck;
+}
+
+//-------------------------------------------------------------------------------------
+
+const char * NavigationGraph::findBridges()
+{
+ const char * errorText = NULL;
+
+ if (!mIslandPtrs.size())
+ errorText = "Error: islands haven't been marked in graph";
+ else
+ {
+ BridgeDataList bridges;
+
+ GraphBridge builder(mNodeList, numIslands(), bridges);
+
+ sSpawnGraph = mIsSpawnGraph;
+ Loser::mCasts = 0;
+
+ builder.findAllBridges();
+ builder.trimOutdoorSteep();
+
+ // set the graph variable:
+ mBridgeList = bridges;
+ }
+ return errorText;
+}
+
+const char * NavigationGraph::pushBridges()
+{
+ if (mPushedBridges)
+ return "Bridges already pushed - do MakeGraph() to remove them";
+
+ if (mBridgeList.size() == 0)
+ return "No bridges exist";
+
+ for (S32 i = 0; i < mBridgeList.size(); i++)
+ {
+ GraphBridgeData & B = mBridgeList[i];
+
+ if (!B.isReplacement())
+ {
+ for (S32 k = 0; k < 2; k++)
+ {
+ RegularNode * src = dynamic_cast(mNodeList[B.nodes[k^0]]);
+ RegularNode * dst = dynamic_cast(mNodeList[B.nodes[k^1]]);
+
+ GraphEdge& edge = src->pushEdge(dst);
+ if (B.mustJet()) {
+ edge.setJetting();
+ if (B.jetClear)
+ edge.setHop(B.jetClear);
+ }
+
+ mJetManager.initEdge(edge, src, dst);
+ }
+ }
+ else
+ {
+ mJetManager.replaceEdge(B);
+ }
+ }
+
+ Con::printf("Connected %d bridges", mBridgeList.size());
+ mPushedBridges = true;
+ markIslands();
+ return NULL;
+}
+
+//-------------------------------------------------------------------------------------
+
+GraphSeeds::GraphSeeds(NavigationGraph& graph)
+ : mGraph(graph),
+ mLoser(InteriorObjectType)
+{
+ // Save node count for later mapping into antecedent index table.
+ mOriginalCount = graph.numNodes();
+}
+
+// Add the seed. We have to remember our parent (antecedent) that spawned us thinking
+// that we might be potentially useful.
+void GraphSeeds::pushSeed(GraphNode* antecedent, const Point3F& pos, GraphSeed::Type type)
+{
+ S32 index = antecedent->getIndex();
+ GraphSeed seed(index, pos, type);
+ mAntecedents.push_back(index);
+ push_back(seed);
+}
+
+Point3F sgCheckSeedUpper(-149.0, -136.5, 166.42);
+
+// Look at drop off from each edge of node. If it lands inside of an interior volume
+// with a modicum of containment, then we want to add a seed node there.
+void GraphSeeds::seedDropOffs(GraphNode * antecedent)
+{
+ // Just for quick testing - look at our shape and see if we can get the behavior we
+ // want locally here-
+ // if (!within(antecedent->location(), sgCheckSeedUpper, 0.6))
+ // return;
+
+ mVolume = mGraph.fetchVolume(antecedent, false);
+ Vector &corners = mVolume.mCorners;
+
+ if (S32 N = corners.size())
+ {
+ // Simplify the loop-
+ corners.push_back(corners[0]);
+
+ // Do drop offs on each edge.
+ for (S32 i = 0; i < N; i++)
+ {
+ Point3F p0 = corners[i + 0];
+ Point3F p1 = corners[i + 1];
+ Point3F drop = scaleBetween(p0, p1, 0.5);
+ VectorF normal(p0.y - p1.y, p1.x - p0.x, 0);
+ F32 len = normal.len();
+
+ if (len > 0.01)
+ {
+ // Go out at least by clear distance. Insure we're above our plane.
+ normal *= ((sClearDists[0] * 1.2) / len);
+ drop += normal;
+ F32 floorZ = solveForZ(mVolume.mFloor, drop);
+ drop.z = (floorZ + 0.5);
+ if (mLoser.hitBelow(drop, 50.0f))
+ {
+ // Now see if we landed inside of a (different) node that's large,
+ // as defined by having good containment on this point.
+ drop.z += 0.2;
+ F32 containment;
+ // if (within(drop, sgCheckSeedLower, 3.0))
+ if (GraphNode * closest = mGraph.closestNode(drop, &containment))
+ {
+ if (closest == antecedent)
+ {
+ AssertFatal(containment > 0, "GraphSeeds::seedDropOffs()");
+ }
+ else
+ {
+ if (containment < -1.3)
+ {
+ GraphSeed::Type seedType = GraphSeed::DropOff;
+
+ // For those close to chute nodes, we make these regular nodes
+ // that go through full bridging process.
+ if (mGraph.getChutes().findNear(drop, 2.2, 1.0, mNearChutes))
+ seedType = GraphSeed::Regular;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+// Procedure is then to cast across any of the walls we are outside of and see if
+// the point that is cast across has decent containment within this selfsame node.
+// Probably the containment we are looking for is a little less than the cast
+// across distance. If Ok, then we create the node with our given antecedent.
+// See crossNearbyVolumes() in graphFind.cc where most of this work happens.
+void GraphSeeds::seedInventory(GraphNode * antecedent)
+{
+ Vector crossings;
+ const Point3F& nodeLoc = antecedent->location();
+ if (S32 N = mGraph.crossNearbyVolumes(antecedent->location(), crossings))
+ for (S32 i = 0; i < N; i++)
+ if (mLoser.haveLOS(nodeLoc, crossings[i]))
+ pushSeed(antecedent, crossings[i], GraphSeed::Inventory);
+}
+
+S32 GraphSeeds::scatter()
+{
+ for (S32 i = 0; i < mGraph.numNodes(); i++)
+ if (GraphNode * node = mGraph.lookupNode(i))
+ if (node->indoor())
+ if (node->inventory())
+ seedInventory(node);
+ else if (NavigationGraph::sSeedDropOffs)
+ seedDropOffs(node);
+
+ return size();
+}
+
+// Find those seeds which have antecedents that aren't part of the largest island
+// in the graph. These are the seeds we will want to use. For the drop off seeds
+// we only attempt to bridge to their antecedent.
+void GraphSeeds::markUsefulSeeds()
+{
+ // Make sure offsets are managed Ok - all seeds should now be nodes.
+ AssertFatal(mOriginalCount + size() == mGraph.numNodes(), "markUsefulSeeds()");
+
+ for (iterator it = begin(); it != end(); it++)
+ {
+ GraphNode * antecedent = mGraph.lookupNode(it->antecedent());
+
+ if (antecedent->island() != mGraph.largestIsland())
+ {
+ S32 mapIndex = mOriginalCount + (it - begin());
+ GraphNode * seed = mGraph.lookupNode(mapIndex);
+
+ // Bridge builder uses this bit-
+ seed->setUsefulSeed();
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------
+
+#define SeedBoxW 0.3
+#define SeedBoxH 2.0
+
+// Here's where we're adding the seed into the (persisted) data lists. They will
+// take effect as run time nodes on the next call to makeGraph().
+void NavigationGraph::installSeed(const Point3F& pos)
+{
+ IndoorNodeInfo nodeInfo;
+ nodeInfo.pos = pos;
+ nodeInfo.setSeed(true);
+ mNodeInfoList.push_back(nodeInfo);
+ mNodeVolumes.addVolume(pos, SeedBoxW, SeedBoxH);
+}
+
+//
+// What was previously several calls from script (makeGraph, findBridges, pushBridges)
+// has now been rolled altogether for the purposes of trying to fill the last holes
+// that we are seeing in the graph. This assumes that script has already uploaded
+// the groundPlan and floorPlan information.
+//
+bool NavigationGraph::assemble()
+{
+ sSpawnGraph = mIsSpawnGraph;
+ Loser::mCasts = 0;
+
+ // First we need a made graph for the seeder to work.
+ makeGraph();
+
+ // Look for problem areas and scatter seeds.
+ GraphSeeds seeds(*this);
+ seeds.scatter();
+ for (S32 i = 0; i < seeds.size(); i++)
+ installSeed(seeds[i].location());
+
+ // Remake to create the run time seed nodes.
+ makeGraph();
+
+ // Now build bridges WITHOUT using the seed nodes-
+ BridgeDataList bridges;
+ GraphBridge bridgeBuilder(mNodeList, numIslands(), bridges);
+ bridgeBuilder.heedSeeds(false);
+ bridgeBuilder.findAllBridges();
+
+ // Set the bridges and connect. This finds largest island, which puts us -
+ mBridgeList = bridges;
+ pushBridges();
+
+ // - in a position to know which seeds might matter (those with stranded parents)
+ seeds.markUsefulSeeds();
+
+ // Now bridge seed nodes-
+ bridges.clear();
+ bridgeBuilder.heedSeeds(true);
+ bridgeBuilder.findAllBridges();
+
+ // Still need to trim outdoor steep connections (this adds to bridges)
+ bridgeBuilder.trimOutdoorSteep();
+
+ // Add the new bridges to the list and rebuild everything-
+ mBridgeList.merge(bridges);
+ makeGraph();
+ pushBridges();
+
+ // Done
+ return 0;
+}
diff --git a/ai/graphBridge.h b/ai/graphBridge.h
new file mode 100644
index 0000000..b3a9522
--- /dev/null
+++ b/ai/graphBridge.h
@@ -0,0 +1,96 @@
+//-----------------------------------------------------------------------------
+// V12 Engine
+//
+// Copyright (c) 2001 GarageGames.Com
+// Portions Copyright (c) 2001 by Sierra Online, Inc.
+//-----------------------------------------------------------------------------
+
+#ifndef _GRAPHBRIDGE_H_
+#define _GRAPHBRIDGE_H_
+
+class GraphBridge : public GraphSearch
+{
+ protected:
+ typedef GraphSearch Parent;
+ struct Candidate {
+ GraphNode * node;
+ F32 heuristic;
+ Candidate(GraphNode* n=NULL, F32 m=0.0) {node = n; heuristic = m;}
+ };
+ class CandidateList : public Vector {
+ static S32 QSORT_CALLBACK cmpCandidates(const void* , const void* );
+ public:
+ void sort();
+ };
+
+ protected:
+ // Parameters to this object-
+ const GraphNodeList& mMainList;
+ const S32 mIslands;
+ BridgeDataList& mBridgesOut;
+
+ // Working variables-
+ GraphNode * mCurrent;
+ Point3F mStartLoc;
+ BitVector mSameIslandMark;
+ S32 mSameIslandCount;
+ bool mFromOutdoor;
+ bool mHeedSeeds;
+ F32 mRatios[2];
+ CandidateList mCandidates;
+ ChutePtrList mChuteList;
+
+ protected:
+ void onQExtraction();
+ F32 getEdgeTime(const GraphEdge* e);
+ bool earlyOut() {return true;}
+ bool heedThese(const GraphNode* from, const GraphNode* to);
+ bool tryToBridge(const GraphNode* from, const GraphNode* to, F32 ratio=1e13);
+ void checkOutdoor(const GraphNode* from, const GraphNode* to);
+
+ public:
+ GraphBridge(const GraphNodeList& mainList, S32 islands, BridgeDataList& listOut);
+ S32 findAllBridges();
+ void trimOutdoorSteep();
+ void heedSeeds(bool b);
+};
+
+class GraphSeed
+{
+ public:
+ enum Type {DropOff, Inventory, Regular};
+
+ protected:
+ S32 mAntecedent;
+ Point3F mLocation;
+ Type mType;
+
+ public:
+ GraphSeed(S32 A, const Point3F& P, Type T) : mAntecedent(A), mLocation(P), mType(T) {}
+ bool isDropOff() const {return mType == DropOff;}
+ bool isInventory() const {return mType == Inventory;}
+ S32 antecedent() const {return mAntecedent;}
+ const Point3F& location() const {return mLocation;}
+};
+
+class GraphSeeds : public Vector
+{
+ protected:
+ NavigationGraph& mGraph;
+ GraphVolume mVolume;
+ Loser mLoser;
+ Vector mAntecedents;
+ S32 mOriginalCount;
+ ChutePtrList mNearChutes;
+
+ void pushSeed(GraphNode* antecedent, const Point3F&, GraphSeed::Type);
+ void seedDropOffs(GraphNode* antecedent);
+ void seedInventory(GraphNode* antecedent);
+
+ public:
+ GraphSeeds(NavigationGraph& G);
+ void markUsefulSeeds();
+ S32 scatter();
+};
+
+#endif
diff --git a/ai/graphBuildLOS.cc b/ai/graphBuildLOS.cc
new file mode 100644
index 0000000..7665485
--- /dev/null
+++ b/ai/graphBuildLOS.cc
@@ -0,0 +1,208 @@
+//-----------------------------------------------------------------------------
+// V12 Engine
+//
+// Copyright (c) 2001 GarageGames.Com
+// Portions Copyright (c) 2001 by Sierra Online, Inc.
+//-----------------------------------------------------------------------------
+
+#include "ai/graph.h"
+
+#define MuzzleHt 0.82f
+#define HeadHt 2.2f
+
+
+//-------------------------------------------------------------------------------------
+
+// Want to see stuff on screen, so we scamble node order (bunch of random swaps)
+static void makeScrambler(Vector& vec, S32 size)
+{
+ S32 i, i1, i2, count = size * 10;
+
+ for (i = 0, vec.setSize(size); i < size; i++)
+ vec[i] = i;
+
+ while (--count >= 0)
+ {
+ S32 t = vec[i1 = (gRandGen.randI()&0xFFFFFF) % size];
+ vec[i1] = vec[i2 = (gRandGen.randI()&0xFFFFFF) % size];
+ vec[i2] = t;
+ }
+}
+
+static RayInfo sColl;
+static bool haveLOS(Point3F src, Point3F dst, F32 above)
+{
+ static const U32 sMask = InteriorObjectType|TerrainObjectType;
+ src.z += above;
+ dst.z += above;
+ return !gServerContainer.castRay(src, dst, sMask, &sColl);
+}
+
+class MakeLOSEntries : public GraphSearch
+{
+ const GraphNodeList& mList;
+ LOSXRefTable& mLOSTable;
+ S32 mCurrent;
+ GraphNode * mFromNode;
+ Point3F mFromLoc;
+ F32 mThreshDist;
+ U32 mStartTime, mSaveMS;
+ S32 mLowButNotHigh;
+ S32 mFromIndex;
+ Vector mScramble;
+ S32 mLOSCalls;
+
+ public:
+ Vector mRenderSegs;
+ Point3F mViewLoc;
+
+ protected:
+ // virtuals-
+ void onQExtraction();
+ F32 getEdgeTime(const GraphEdge*);
+ bool earlyOut();
+
+ public:
+ MakeLOSEntries(const GraphNodeList& list, LOSXRefTable& xRef, Point3F view);
+
+ bool isDone() const {return mCurrent>=mList.size();}
+ U32 elapsedTime() const {return Platform::getRealMilliseconds()-mSaveMS;}
+ S32 lowNotHigh() const {return mLowButNotHigh;}
+ S32 numLOSCalls() const {return mLOSCalls;}
+ bool nextOneReady();
+ void workAWhile();
+};
+
+MakeLOSEntries::MakeLOSEntries(const GraphNodeList& list, LOSXRefTable& los, Point3F v)
+ : mList(list), mLOSTable(los), mViewLoc(v)
+{
+ S32 N = list.size();
+ mLOSTable.setDims(N * (N + 1) >> 1, 2); // Alloc our 2-bit table
+ mViewLoc.set(-44,-31,90);
+ mThreshDist = 700.0f;
+ mCurrent = -1;
+ mSaveMS = Platform::getRealMilliseconds();
+ mLOSCalls = mLowButNotHigh = 0;
+ makeScrambler(mScramble, list.size());
+}
+
+// Run LOS from base to this node. We truncate the search at a certain path distance,
+// which should make sense in all but a few cases that we should be aware of. It
+// will make the search quicker for most worlds though.
+void MakeLOSEntries::onQExtraction()
+{
+ S32 toIndex = extractedNode()->getIndex();
+
+ if (mFromIndex < toIndex) {
+ //==> Path distance really not right- just need a better local query.
+ if (distSoFar() < mThreshDist) {
+ Point3F toLoc = extractedNode()->location();
+ bool losLow = haveLOS(mFromLoc, toLoc, MuzzleHt);
+ bool losHigh = haveLOS(mFromLoc, toLoc, HeadHt);
+ U32 tabEntry = LOSXRefTable::Hidden;
+
+ // Speed tracking
+ mLOSCalls += 2;
+
+ // These are strange- add to list of warning log for graph maker
+ mLowButNotHigh += (losLow && !losHigh);
+
+ // Crude calc of entry for now-
+ if(losLow)
+ tabEntry = LOSXRefTable::FullLOS;
+ else if (losHigh)
+ tabEntry = LOSXRefTable::MinorLOS;
+
+ // Enter into table-
+ mLOSTable.setEntry(mFromIndex, toIndex, tabEntry);
+
+ // Stuff to render-
+ if (mRenderSegs.size() < 600) {
+ LineSegment segment(mFromLoc, tabEntry ? toLoc : sColl.point);
+ if (segment.distance(mViewLoc) < 120.0f)
+ mRenderSegs.push_back(segment);
+ }
+ }
+ else // outside
+ setDone();
+ }
+}
+
+// Virtual - this will cause graph time to equal distance.
+F32 MakeLOSEntries::getEdgeTime(const GraphEdge * edge)
+{
+ return edge->mDist;
+}
+
+// Prepare a new search. Note mCurrent constructs at -1 for proper entry.
+bool MakeLOSEntries::nextOneReady()
+{
+ if (++mCurrent < mList.size()) {
+ mFromNode = mList[mScramble[mCurrent]];
+ mFromLoc = mFromNode->location();
+ mFromIndex = mFromNode->getIndex();
+ return true;
+ }
+ return false;
+}
+
+#define MillisecondWorkShift 90
+
+// Virtual - runSearch() can be stopped in the middle.
+bool MakeLOSEntries::earlyOut()
+{
+ U32 timeElapsed = (Platform::getRealMilliseconds() - mStartTime);
+ return (timeElapsed > MillisecondWorkShift);
+}
+
+void MakeLOSEntries::workAWhile()
+{
+ mStartTime = Platform::getRealMilliseconds();
+
+ while (!isDone() && !earlyOut())
+ if (inProgress())
+ runSearch(mFromNode);
+ else if (nextOneReady())
+ runSearch(mFromNode);
+
+ NavigationGraph::sProcessPercent = F32(mCurrent) / F32(mList.size());
+}
+
+bool NavigationGraph::prepLOSTableWork(Point3F viewLoc)
+{
+ if (mTableBuilder) {
+ delete mTableBuilder;
+ mTableBuilder = NULL;
+ }
+
+ mTableBuilder = new MakeLOSEntries(mNonTransient, mLOSXRef, viewLoc);
+ return true;
+}
+
+// This gets called repeatedly until the table is built. Idea here is to slice
+// the process so that some rendering can occur during this lengthy process.
+bool NavigationGraph::makeLOSTableEntries()
+{
+ MakeLOSEntries * makeEntries = dynamic_cast(mTableBuilder);
+ AssertFatal(makeEntries, "Graph preprocess: prepLOSTable() needed");
+
+ makeEntries->workAWhile();
+
+ mRenderThese = makeEntries->mRenderSegs;
+ makeEntries->mRenderSegs.clear();
+
+ if (makeEntries->isDone()) {
+ clearRenderSegs();
+ Con::printf("Total table size = %d", mLOSXRef.numBytes());
+ Con::printf("Performed %d LOS calls", makeEntries->numLOSCalls());
+ Con::printf("Elapsed time = %d milliseconds", makeEntries->elapsedTime());
+ if (makeEntries->lowNotHigh())
+ Con::printf("%d Low-not-high entries found", makeEntries->lowNotHigh());
+ delete mTableBuilder;
+ mTableBuilder = NULL;
+ // Convert data to better hasher-
+ makeLOSHashTable();
+ return false;
+ }
+ return true;
+}
diff --git a/ai/graphConjoin.cc b/ai/graphConjoin.cc
new file mode 100644
index 0000000..8ae39e9
--- /dev/null
+++ b/ai/graphConjoin.cc
@@ -0,0 +1,660 @@
+//-----------------------------------------------------------------------------
+// V12 Engine
+//
+// Copyright (c) 2001 GarageGames.Com
+// Portions Copyright (c) 2001 by Sierra Online, Inc.
+//-----------------------------------------------------------------------------
+
+#include "ai/graphData.h"
+#include "Core/BitTables.h"
+#include "ai/graphGroundPlan.h"
+// Two pass consolidation:
+//
+// 1. Find what is maximum level that can be consolidated based on flatness (+ same-
+// type-ness of the squares inside).
+// 2. Actually assign the levels, limiting some based on condition II below.
+//
+// Here are the conditions for consolidating squares.
+//
+// I. A square can only be consolidated if there are no level zero squares inside
+// and if all of the four subsquares have managed to be consolidated (plus
+// being inside the graph region). ====> plus no empty squares.
+// II. No terrain node can have a neighbor whose level differs by more than one. So
+// we can't have a huge consolidated square with a bunch of little neighbors,
+// for example. There are two motivations for this: We'd like to be able
+// to cap the neighbor count; Second, it will help us insure that large
+// nodes still make sense as distinct entries in the LOS xref table.
+// III. A square can be consolidated if all sub-squares are of the same type, plus
+// this square is of size 1< PI/6.
+//
+Point2F GridNormalInfo::normalToAngle(const VectorF& normal)
+{
+ Point2F angle;
+ angle.x = (M_PI / 2.0) - mAtan( normal.x, normal.z );
+ angle.y = (M_PI / 2.0) - mAtan( normal.y, normal.z );
+ return angle;
+}
+VectorF GridNormalInfo::angleToNormal(const Point2F& A)
+{
+ VectorF normal( mCos(A.x)/mSin(A.x), mCos(A.y)/mSin(A.y), 1.0);
+ normal.normalize();
+ return normal;
+}
+
+//-------------------------------------------------------------------------------------
+
+//
+// The following 3 classes perform separate passes of the data consolidation.
+// Note they only build the data (what gets persisted) - creating run time
+// nodes from this data is done elsewhere.
+//
+class FindBestConsolidations : public GridVisitor
+{
+ protected:
+ Vector mGridNormals;
+ TrackLevels & mTrackLevels;
+ const ConjoinConfig & mConfigure;
+ F32 mDotThreshold;
+ void getGridNormals();
+ bool allSameType(const GridArea& R, U8& nodeType);
+ bool areaIsFlat(const GridArea& R);
+ bool atLevelZero(const GridArea& R); // virtual
+ bool afterDivide(const GridArea& R, S32 level, bool success); // virtual
+
+ public:
+ FindBestConsolidations(const GridArea& G, const ConjoinConfig& C, TrackLevels& T);
+};
+class SmoothOutLevels : public GridVisitor
+{
+ protected:
+ const S32 mLevel;
+ TrackLevels & mTrackLevels;
+ bool allWater(const GridArea& R);
+ void capLevelAt(const GridArea& R, S32 L);
+ void doCurrentBottom(const GridArea& R);
+ bool atLevelZero(const GridArea& R); // virtual
+ bool beforeDivide(const GridArea& R, S32 level); // virtual
+
+ public:
+ SmoothOutLevels(const GridArea& G, TrackLevels& T, S32 L);
+};
+class BuildConsolidated : public GridVisitor
+{
+ protected:
+ const TrackLevels & mTrackLevels;
+ Consolidated & mConsolidated;
+ bool checkAddToList(const GridArea& R, S32 level);
+ bool beforeDivide(const GridArea& R, S32 level); // virtual
+ bool atLevelZero(const GridArea& R); // virtual
+
+ public:
+ BuildConsolidated(const GridArea& G, const TrackLevels& T, Consolidated& C);
+};
+
+
+//-------------------------------------------------------------------------------------
+
+ConjoinConfig::ConjoinConfig()
+{
+ maxAngleDev = 45; // (only one actually used right now...)
+ maxBowlDev = 70;
+ maxLevel = 6;
+}
+
+//-------------------------------------------------------------------------------------
+
+// Just check that it's a valid rect for that level.
+static bool checkArea(const GridArea & G, S32 L, const char * caller)
+{
+ U16 ext = (1 << L);
+ U16 mask = ext - 1;
+ const char * problem = NULL;
+
+ if( G.point.x & mask )
+ problem = "X point isn't aligned";
+ else if ( G.point.y & mask )
+ problem = "Y point isn't aligned";
+ else if( G.extent.x != ext )
+ problem = "X extent is bad";
+ else if( G.extent.y != ext )
+ problem = "Y extent is bad";
+
+ AssertFatal( caller && ! problem, avar("Problem= %s in %s", problem, caller) );
+
+ return ! problem && caller;
+}
+
+TrackLevels::TrackLevels(S32 sz)
+{
+ init(sz);
+}
+
+void TrackLevels::init(S32 size)
+{
+ setSizeAndClear(achievedLevels, size);
+ setSizeAndClear(nodeTypes, size);
+}
+
+S32 TrackLevels::size() const
+{
+ return achievedLevels.size();
+}
+
+U16 TrackLevels::getNodeType(S32 idx) const
+{
+ return nodeTypes[idx];
+}
+
+void TrackLevels::setNodeType(S32 idx, U8 nodeType)
+{
+ nodeTypes[idx] = nodeType;
+}
+
+// Achieved level is the highest set bit.
+void TrackLevels::setAchievedLevel(S32 i, S32 level)
+{
+ AssertFatal(i < achievedLevels.size(), "TrackLevels::setAchievedLevel()");
+
+ if(level < 0) {
+ achievedLevels[i] = 0;
+ }
+ else{
+ U16 mask = (1 << level);
+ AssertFatal(achievedLevels[i]==mask-1, "setAchievedLevel");
+ achievedLevels[i] |= mask;
+ }
+}
+
+// Note that TrackLevels maps the case of zero not being achieved into a -1 value.
+S32 TrackLevels::getAchievedLevel(S32 idx) const
+{
+ U16 achieved = achievedLevels[idx];
+ U16 L = BitTables::getPower16(achieved);
+
+ if( L-- > 0 )
+ return S32(L);
+ else
+ return -1;
+}
+
+// There's a problem with -1 and 0 not carrying enough information. We have some
+// nodes which need to be considered as -1 for the purposes of the consolidation
+// since they don't really span a square, but we need to consider them zero below
+// when we're assembling the list. Pretty klunky...
+S32 TrackLevels::originalNodeLevel(S32 idx) const
+{
+ S32 level = getAchievedLevel(idx);
+ if(level == -1 && nodeTypes[idx])
+ return 0;
+ else
+ return level;
+}
+
+void TrackLevels::capLevelAt(S32 i, U16 lev)
+{
+ AssertFatal(validArrayIndex(i, achievedLevels.size()), "TrackLevels::capLevelAt()");
+ U16 mask = (1 << lev + 1) - 1;
+ achievedLevels[i] &= mask;
+}
+
+//-------------------------------------------------------------------------------------
+// Visitor to find best consolidations
+
+FindBestConsolidations::FindBestConsolidations( const GridArea& gridArea,
+ const ConjoinConfig& thresholds,
+ TrackLevels& trackArrayOut
+ )
+ : mTrackLevels(trackArrayOut),
+ mConfigure(thresholds),
+ GridVisitor(gridArea)
+{
+ // Pre-gather terrain normal information; convert angle values to what we need
+ getGridNormals();
+ mDotThreshold = F32(mCos(mDegToRad(thresholds.maxAngleDev)));
+}
+
+// Go through and fetch the triangle normals from the terrain, plus compute the our
+// pseudo-Euler for each normal.
+void FindBestConsolidations::getGridNormals()
+{
+ TerrainBlock * terr = GroundPlan::getTerrainObj();
+ F32 sqrW = gNavGlobs.mSquareWidth;
+ F32 stepIn = (sqrW * 0.1);
+
+ Point2I stepper;
+ mGridNormals.clear();
+
+ for (mArea.start(stepper); mArea.pointInRect(stepper); mArea.step(stepper))
+ {
+ // this is empty by default
+ GridNormalInfo info;
+
+ // find out, based on split, good points to use for normal check:
+ Point2F loc(stepper.x * sqrW, stepper.y * sqrW);
+ Point2F upperCheckPt = loc;
+ Point2F lowerCheckPt = loc;
+ if( (stepper.x + stepper.y) & 1 ){
+ lowerCheckPt += Point2F(stepIn, stepIn);
+ upperCheckPt += Point2F(sqrW - stepIn, sqrW - stepIn);
+ }
+ else{
+ lowerCheckPt += Point2F(sqrW - stepIn, stepIn);
+ upperCheckPt += Point2F(stepIn, sqrW - stepIn);
+ }
+
+ // get slopes and convert to the pseudo-Euler
+ if (terr->getNormal(lowerCheckPt, &info.normals[0]))
+ if (terr->getNormal(upperCheckPt, &info.normals[1])) {
+ for (S32 both = 0; both < 2; both++) {
+ info.angles[both] = info.normalToAngle(info.normals[both]);
+ if (info.normals[both].z < gNavGlobs.mWalkableDot)
+ info.hasSteep = true;
+ }
+ info.notEmpty = true;
+ }
+
+ mGridNormals.push_back( info );
+ }
+}
+
+bool FindBestConsolidations::allSameType(const GridArea& R, U8& nodeType)
+{
+ nodeType = mTrackLevels.getNodeType(mArea.getIndex(R.point));
+ Point2I stepper;
+ for (R.start(stepper); R.pointInRect(stepper); R.step(stepper))
+ if (mTrackLevels.getNodeType(mArea.getIndex(stepper)) != nodeType)
+ return false;
+ return true;
+}
+
+// ==>
+// ==> This routine is where all the consolidation work happens and will eventually
+// ==> do a lot more work to do more robust checks. Example:
+// ==> We want to allow more flatness for bowls than for hills.
+// ==> We may want to consolidate more in places far from action.
+// ==>
+bool FindBestConsolidations::areaIsFlat(const GridArea& R)
+{
+ // Area must be of same type. If that type is submerged - then we consider
+ // it flat right away.
+ U8 nodeType;
+ if (!allSameType(R, nodeType))
+ return false;
+ else if (nodeType == GraphNodeSubmerged)
+ return true;
+
+ Point2F minAngle(M_PI, M_PI);
+ Point2F maxAngle(0,0);
+ Point2I stepper;
+
+ // First accumulate angle bounds,
+ for (R.start(stepper); R.pointInRect(stepper); R.step(stepper))
+ {
+ const GridNormalInfo & info = mGridNormals[ mArea.getIndex(stepper) ];
+
+ // Don't consolidate if there are non-walkable surfaces-
+ // Actually...
+ // Turns out this makes too many nodes on some maps - we'll do this differently
+ // if (info.hasSteep)
+ // return false;
+
+ for (S32 triangle = 0; triangle < 2; triangle++)
+ {
+ minAngle.x = getMin( info.angles[triangle].x, minAngle.x );
+ minAngle.y = getMin( info.angles[triangle].y, minAngle.y );
+ maxAngle.x = getMax( info.angles[triangle].x, maxAngle.x );
+ maxAngle.y = getMax( info.angles[triangle].y, maxAngle.y );
+ }
+ }
+
+ // Get the middle angle and the corresponding unit vector:
+ Point2F medianAngle = (minAngle + maxAngle) * 0.5;
+ VectorF medianNormal = GridNormalInfo::angleToNormal (medianAngle);
+
+ // Find maximum deviation from median slope.
+ F32 minDot = 1.0;
+ for (R.start(stepper); R.pointInRect(stepper); R.step(stepper))
+ {
+ const GridNormalInfo & info = mGridNormals[ mArea.getIndex(stepper) ];
+ for (S32 triangle = 0; triangle < 2; triangle++) {
+ // == > We can check early out here, but want to watch behavior for now
+ minDot = getMin( mDot(info.normals[triangle], medianNormal), minDot );
+ }
+ }
+
+ // if all dot products are sufficiently close to 1, we're hopefully flat-
+ return minDot > mDotThreshold;
+}
+
+// pass one of consolidation - finds maximum possible squares.
+bool FindBestConsolidations::atLevelZero(const GridArea& R)
+{
+ S32 idx = mArea.getIndex(R.point);
+ S32 achieved = mTrackLevels.getAchievedLevel(idx);
+
+ if( achieved < 0 )
+ return false;
+
+ AssertFatal( achieved == 0, "FindBest messed up at level zero." );
+
+ return true;
+}
+
+bool FindBestConsolidations::afterDivide(const GridArea& R, S32 level, bool success)
+{
+ S32 idx = mArea.getIndex(R.point);
+ AssertFatal( validArrayIndex(idx, mTrackLevels.size()), "Conjoin weird idx");
+
+ // ==> Next: Look at node type as part of consolidation. Mainly Water.
+ // ==> Q: Will other volume types be important? (i.e. fog, ..?).
+ if( success && level <= MaxConsolidateLevel )
+ {
+ if (areaIsFlat( R ))
+ {
+ Point2I stepper; // set achieved level in all sub-squares.
+ for (R.start(stepper); R.pointInRect(stepper); R.step(stepper))
+ mTrackLevels.setAchievedLevel( mArea.getIndex(stepper), level );
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//-------------------------------------------------------------------------------------
+// VISITOR TO SMOOTH OUT LEVELS.
+// Some of the best ones may be eliminated to satisfy condition II above.
+
+static GridArea getParentArea(const GridArea & R, S32 L)
+{
+ checkArea( R, L, "getParentArea one" );
+ L = L + 1;
+ Point2I roundDownPoint((R.point.x >> L) << L, (R.point.y >> L) << L);
+ Point2I doubleTheExtent( R.extent.x << 1, R.extent.y << 1 );
+ GridArea parent(roundDownPoint, doubleTheExtent);
+ checkArea( parent, L, "getParentArea two" );
+ return parent;
+}
+
+SmoothOutLevels::SmoothOutLevels(const GridArea& G, TrackLevels& T, S32 L)
+ : mTrackLevels(T), mLevel(L), GridVisitor(G) { }
+
+
+// Cap all within the given area at L.
+void SmoothOutLevels::capLevelAt(const GridArea& R, S32 L)
+{
+ checkArea( R, L, "capping level in smoother" );
+
+ Point2I stepper;
+ for (R.start(stepper); R.pointInRect(stepper); R.step(stepper)) {
+ S32 index = mArea.getIndex(stepper);
+ if (index >= 0)
+ mTrackLevels.capLevelAt(index, L);
+ }
+}
+
+bool SmoothOutLevels::allWater(const GridArea& R)
+{
+ Point2I stepper;
+ for (R.start(stepper); R.pointInRect(stepper); R.step(stepper))
+ if (mTrackLevels.getNodeType(mArea.getIndex(stepper)) != GraphNodeSubmerged)
+ return false;
+ return true;
+}
+
+// Each pass does a different bottom level to smooth it out. Within this routine we
+// are guaranteed that we're at the bottom level for this pass.
+void SmoothOutLevels::doCurrentBottom(const GridArea& R)
+{
+ bool needToCapSurrounding;
+ S32 idx = mArea.getIndex(R.point);
+ S32 achieved = mTrackLevels.getAchievedLevel(idx);
+
+ // if (allWater(R))
+ // needToCapSurrounding = false;
+ // else
+ {
+ // At level 0, we cap if either the achieved is 0, or -1 (an empty square).
+ if (mLevel == 0)
+ needToCapSurrounding = (achieved <= 0);
+ else
+ needToCapSurrounding = (achieved == mLevel);
+ }
+
+ if (needToCapSurrounding)
+ {
+ // THIS IS THE TRICKY STEP IN SMOOTHING OUT THE LEVELS! We cap all eight of our
+ // same-sized neighbors- but we choose the cap level based on whether or not they
+ // fall inside OUR parent, or if they fall in a NEIGHBOR of our parent. If in our
+ // parent, then cap at our level. If in neighbor's parent, we cap at THAT level.
+
+ GridArea ourParent = getParentArea(R, mLevel);
+
+ // Loop on all 8 neighbors:
+ for(S32 y = -1; y <= 1; y++) for(S32 x = -1; x <= 1; x++) if (x || y)
+ {
+ Point2I neighborPoint = Point2I(x << mLevel, y << mLevel) + R.point;
+ S32 neighborIndex = mArea.getIndex( neighborPoint );
+ if (neighborIndex >= 0)
+ {
+ GridArea neighborArea(neighborPoint, R.extent);
+ if (ourParent.contains(neighborArea))
+ capLevelAt(neighborArea, mLevel);
+ else
+ {
+ GridArea neighborParent = getParentArea( neighborArea, mLevel );
+ if(mArea.contains(neighborParent))
+ capLevelAt(neighborParent, mLevel + 1);
+ }
+ }
+ }
+ }
+}
+
+// If the current bottom level we are looking at is the best consolidation
+// that can happen for this square, go around to all neighbors and cap
+// their best-so-far levels.
+bool SmoothOutLevels::atLevelZero(const GridArea& R)
+{
+ if(mLevel == 0) {
+ doCurrentBottom(R);
+ return true;
+ }
+ return false;
+}
+
+bool SmoothOutLevels::beforeDivide(const GridArea& R, S32 level)
+{
+ checkArea( R, level, "smoother before divide" );
+ // AssertFatal( level >= mLevel, "Smoothing making it too far down somehow" );
+
+ if( level > mLevel ) // still at high level - tell it to divide further.
+ return true;
+ else if(level == mLevel){ //
+ doCurrentBottom(R);
+ return false;
+ }
+ else
+ return false;
+}
+
+//-------------------------------------------------------------------------------------
+// This Visitor assembles the list into mConsolidated:
+
+BuildConsolidated::BuildConsolidated(const GridArea& G, const TrackLevels& T, Consolidated& C)
+ : GridVisitor(G), mTrackLevels(T), mConsolidated(C)
+{ }
+
+bool BuildConsolidated::checkAddToList(const GridArea& R, S32 level)
+{
+ checkArea(R, level, "checkAddToList");
+
+ if (mTrackLevels.originalNodeLevel(mArea.getIndex(R.point)) == level)
+ {
+ OutdoorNodeInfo nodeInfo;
+ nodeInfo.level = level;
+ nodeInfo.x = R.point.x;
+ nodeInfo.y = R.point.y;
+ mConsolidated.push_back(nodeInfo);
+ return false;
+ } // (ret vals needed for beforeDivide(), false will stop the depth recurse)
+ return true;
+}
+bool BuildConsolidated::beforeDivide(const GridArea& R, S32 level)
+{
+ return checkAddToList( R, level );
+}
+bool BuildConsolidated::atLevelZero(const GridArea& R)
+{
+ checkAddToList( R, 0 );
+ return Whatever;
+}
+
+//-------------------------------------------------------------------------------------
+
+// This routine is supplied with those grid squares that can't conjoin.
+// The 'output' is to set up the consolidated list member variable,
+// and return if successful.
+bool TerrainGraphInfo::buildConsolidated(const TrackLevels & whichZero,
+ const ConjoinConfig & configInfo)
+{
+ TrackLevels trackData = whichZero;
+ GridArea gridArea(originGrid, gridDimensions);
+
+ // pass one to find best possible consolidations
+ FindBestConsolidations findLargestPossible(gridArea,configInfo,trackData);
+ findLargestPossible.traverse();
+
+ // Next, enforce maximum difference of one on the levels.
+ for (S32 bottom = 0; bottom < MaxConsolidateLevel; bottom++)
+ {
+ // two passes needed to properly propagate the smoothing...
+ for (S32 pass = 0; pass < 2; pass++)
+ {
+ SmoothOutLevels doSmoothing(gridArea, trackData, bottom);
+ doSmoothing.traverse();
+ }
+ }
+
+ // Now build the node list. The caller is responsible for making it
+ // part of the graph.
+ BuildConsolidated buildIt(gridArea, trackData, consolidated);
+ consolidated.clear();
+ buildIt.traverse();
+
+ return true;
+}
+
+//-------------------------------------------------------------------------------------
+// Mark bit on grid square signalling if it's near steep. Will cause roam radii to
+// be capped so bots don't walk off slopes, and don't advance through a slope they must
+// walk around.
+
+S32 TerrainGraphInfo::markNodesNearSteep()
+{
+ TerrainBlock * terr = GroundPlan::getTerrainObj();
+ F32 startAng = M_PI * (15.0 / 8.0);
+ F32 angDec = M_PI / 4.0;
+ Point3F loc, normal;
+ S32 nSteep = 0;
+
+ for (S32 i = 0; i < nodeCount; i++)
+ {
+ // Get position- terrain system is grid aligned...
+ indexToLoc(loc, i);
+ loc -= originWorld;
+
+ // Could be smarter here, but it's a drop in the preprocessing bucket. Just
+ // check enough points to be sure to find if any triangle is steep.
+ for (F32 ang = startAng; ang > 0; ang -= angDec)
+ {
+ Point2F checkPos(mCos(ang) * 0.2 + loc.x, mSin(ang) * 0.2 + loc.y);
+ if (terr->getNormal(checkPos, &normal))
+ if (normal.z < gNavGlobs.mWalkableDot) {
+ setSteep(i);
+ nSteep++;
+ break;
+ }
+ }
+ }
+ return nSteep;
+}
+
+//-------------------------------------------------------------------------------------
+// This was being done off of run time nodes- convert to just using the grid data.
+
+bool TerrainGraphInfo::consolidateData(const ConjoinConfig & config)
+{
+ if (nodeCount <= 0)
+ return false;
+
+ // This is a good place to mark the steep bits on the navigableFlags. Note it's not
+ // used quite the same as the hasSteep in the consolidation, which shouldn't carry
+ // over to other grid squares. The bit does (used for roamRadii)
+ markNodesNearSteep();
+
+ // Initialize NULL track data (keeps track of conjoin levels achieved per square).
+ TrackLevels levelData(nodeCount);
+ const U32 checkOpenSquare = 0xff;
+
+ // Assemble the data for the consolidator.
+ for (S32 i = 0; i < nodeCount; i++)
+ {
+ U8 nodeType = NavGraphNotANode;
+ S32 nodeLevel = -1;
+
+ if (!obstructed(i))
+ {
+ // Check for shadowed - we flag like indoor as well to make them all stay
+ // at the low level. We need them dense in shadow so there will be enough
+ // jetting connections going up.
+ if (shadowed(i))
+ {
+ nodeType = NavGraphIndoorNode;
+ }
+ else if (submerged(i))
+ {
+ nodeType = NavGraphSubmergedNode;
+ nodeLevel = 0;
+ }
+ else
+ {
+ // We have to use level -1 for things which don't have all their neighbors,
+ // with one exception being on the edge of the mission area. Here we just
+ // see if it has the neighbors it should have (returned by onSideOfArea()).
+ nodeType = NavGraphOutdoorNode;
+ if (const U32 neighbors = onSideOfArea(i)) {
+ if (neighborFlags[i] == neighbors)
+ nodeLevel = 0;
+ }
+ else if (neighborFlags[i] == checkOpenSquare)
+ nodeLevel = 0;
+ }
+
+ levelData.setAchievedLevel(i, nodeLevel);
+ levelData.setNodeType(i, nodeType);
+ }
+ }
+
+ bool Ok = buildConsolidated(levelData, config);
+
+ return Ok;
+}
diff --git a/ai/graphData.cc b/ai/graphData.cc
new file mode 100644
index 0000000..3380886
--- /dev/null
+++ b/ai/graphData.cc
@@ -0,0 +1,1283 @@
+//-----------------------------------------------------------------------------
+// V12 Engine
+//
+// Copyright (c) 2001 GarageGames.Com
+// Portions Copyright (c) 2001 by Sierra Online, Inc.
+//-----------------------------------------------------------------------------
+
+#include "ai/graphData.h"
+#include "terrain/terrRender.h"
+
+// 4 6 7 Layout for how we translate the
+// 2 N 5 numbers 0 to 7 into the eight
+// 0 1 3 surrounding grid locations.
+const Point2I TerrainGraphInfo::gridOffs[8] = {
+ Point2I( -1, -1 ),
+ Point2I( 0, -1 ), Point2I( -1, 0 ),
+ Point2I( 1, -1 ), Point2I( -1, 1 ),
+ Point2I( 1, 0 ), Point2I( 0, 1 ),
+ Point2I( 1, 1 ) };
+
+S32 TerrainGraphInfo::smVersion = 0;
+
+
+//-------------------------------------------------------------------------------------
+// Indoor Graph Edges
+
+bool GraphEdgeInfo::isAlgorithmic() const {
+ return to[0].flags.test(Algorithmic);
+}
+void GraphEdgeInfo::setAlgorithmic(bool yesOrNo) {
+ to[0].flags.set(Algorithmic, yesOrNo);
+}
+
+GraphEdgeInfo::GraphEdgeInfo()
+{
+ segNormal.set(0,0,1);
+ segPoints[0] = segPoints[1] = segNormal;
+}
+
+bool GraphEdgeInfo::read(Stream & s)
+{
+ for( S32 i = 0; i < 2; i++ ){
+ U32 mask;
+ if( s.read(&mask) ){
+ to[i].flags = mask;
+ if( s.read(&to[i].res) && s.read(&to[i].dest) )
+ continue;
+ return false;
+ }
+ }
+
+ if (mathRead(s, &segPoints[0]))
+ if (mathRead(s, &segPoints[1]))
+ if (mathRead(s, &segNormal))
+ return true;
+
+ return false;
+}
+bool GraphEdgeInfo::write(Stream & s) const
+{
+ for( S32 i = 0; i < 2; i++ ){
+ U32 mask = to[i].flags;
+ if( s.write(mask) && s.write(to[i].res) && s.write(to[i].dest) )
+ continue;
+ return false;
+ }
+
+ if (mathWrite(s, segPoints[0]))
+ if (mathWrite(s, segPoints[1]))
+ if (mathWrite(s, segNormal))
+ return true;
+
+ return false;
+}
+
+//-------------------------------------------------------------------------------------
+// Indoor Nodes
+
+IndoorNodeInfo::IndoorNodeInfo()
+{
+ pos.set(-1,-1,-1);
+ antecedent = -1;
+ unused = 0;
+}
+
+// The boolean setters & getters. Avoiding inlines since these should check
+// invariant relationships among the flags.
+bool IndoorNodeInfo::isAlgorithmic() const
+{
+ return flags.test( Algorithmic );
+}
+
+void IndoorNodeInfo::setAlgorithmic(bool yesOrNo)
+{
+ flags.set( Algorithmic, yesOrNo );
+}
+
+bool IndoorNodeInfo::isInventory() const
+{
+ return flags.test(Inventory);
+}
+
+void IndoorNodeInfo::setInventory(bool yesOrNo)
+{
+ flags.set(Inventory, yesOrNo);
+}
+
+bool IndoorNodeInfo::isBelowPortal() const
+{
+ return flags.test(BelowPortal);
+}
+
+void IndoorNodeInfo::setBelowPortal(bool yesOrNo)
+{
+ flags.set(BelowPortal, yesOrNo);
+}
+
+bool IndoorNodeInfo::isSeed() const
+{
+ return flags.test(Seed);
+}
+
+void IndoorNodeInfo::setSeed(bool yesOrNo)
+{
+ flags.set(Seed, yesOrNo);
+}
+
+bool IndoorNodeInfo::read(Stream & s)
+{
+ U32 mask;
+ if (s.read(&mask))
+ {
+ flags = mask;
+ return s.read(&unused) && s.read(&antecedent) && mathRead(s, &pos);
+ }
+ return false;
+}
+bool IndoorNodeInfo::write(Stream & s) const
+{
+ U32 mask = flags;
+ if (s.write(mask))
+ return s.write(unused) && s.write(antecedent) && mathWrite(s, pos);
+ return false;
+}
+
+//-------------------------------------------------------------------------------------
+// Outdoor (consolidated) node information
+
+OutdoorNodeInfo::OutdoorNodeInfo()
+{
+ level = 0xff;
+ flags = 0;
+ height = 0; //==> Not used - can be taken out.
+ x = y = -1;
+}
+
+bool OutdoorNodeInfo::read(Stream& s)
+{
+ return s.read(&level) &&
+ s.read(&flags) &&
+ s.read(&height) &&
+ s.read(&x) &&
+ s.read(&y)
+ ;
+}
+
+bool OutdoorNodeInfo::write(Stream & s) const
+{
+ return s.write(level) &&
+ s.write(flags) &&
+ s.write(height) &&
+ s.write(x) &&
+ s.write(y)
+ ;
+}
+
+//-------------------------------------------------------------------------------------
+
+SpawnLocations::SpawnLocations()
+{
+ reset();
+}
+
+void SpawnLocations::reset()
+{
+ clear();
+ compact();
+ mSpheres.clear();
+ mSpheres.compact();
+ mRes0 = 0;
+}
+
+bool SpawnLocations::read(Stream& s)
+{
+ if (s.read(&mRes0))
+ if (mathReadVector(*this, s))
+ return readVector1(s, mSpheres);
+ return false;
+}
+
+bool SpawnLocations::write(Stream& s) const
+{
+ if (s.write(mRes0))
+ if (mathWriteVector(*this, s))
+ return writeVector1(s, mSpheres);
+
+ return false;
+}
+
+SpawnLocations::Sphere::Sphere(const Point3F& center, F32 radius)
+ : mSphere(center, radius)
+{
+ mInside = false;
+ mCount = 0;
+ mOffset = 0;
+ mRes0 = 0;
+}
+
+bool SpawnLocations::Sphere::read(Stream& s)
+{
+ if (s.read(&mRes0))
+ if (mathRead(s, &mSphere))
+ if (s.read(&mInside))
+ if (s.read(&mCount))
+ return s.read(&mOffset);
+ return false;
+}
+
+bool SpawnLocations::Sphere::write(Stream& s) const
+{
+ if (s.write(mRes0))
+ if (mathWrite(s, mSphere))
+ if (s.write(mInside))
+ if (s.write(mCount))
+ return s.write(mOffset);
+ return false;
+}
+
+// Lookup a random location from our pre-computed data.
+S32 SpawnLocations::getRandom(const SphereF& sphere, bool inside, U32 rnd)
+{
+ // Find closest sphere matching inside/outside requirement:
+ const Sphere * closest = NULL;
+ F32 minDist = 1e22, d;
+ for (S32 i = 0; i < mSpheres.size(); i++)
+ {
+ const Sphere * S = & mSpheres[i];
+ if (S->mInside == inside && S->mCount > 0)
+ if ((d = (S->mSphere.center - sphere.center).lenSquared()) < minDist)
+ {
+ closest = S;
+ minDist = d;
+ }
+ }
+
+ // Get the random index in the list.
+ if (closest)
+ {
+ U32 count = closest->mCount;
+ return (closest->mOffset + ((rnd & 0x7FFFFF) % count));
+ }
+ else
+ return -1;
+}
+
+void SpawnLocations::printInfo() const
+{
+ Con::printf("--- %d Spheres were generated", mSpheres.size());
+ for (S32 i = 0; i < mSpheres.size(); i++)
+ {
+ const Sphere * S = & mSpheres[i];
+ const Point3F & P = S->mSphere.center;
+
+ Con::printf("%d : %s at center (%f, %f, %f) - generated %d",
+ i + 1,
+ S->mInside ? "Inside" : "Outside",
+ P.x, P.y, P.z,
+ S->mCount
+ );
+ }
+}
+
+//-------------------------------------------------------------------------------------
+// Potential bridges.
+
+void GraphBridgeInfo::init(S32 from, S32 to)
+{
+ srcNode = from;
+ dstNode = to;
+ jetClear = 0;
+ howTo = 0;
+ res1 = 0;
+ res2 = 0;
+ res3 = 0;
+}
+
+bool GraphBridgeInfo::read(Stream& s)
+{
+ return
+ ( s.read(&dstNode) &&
+ s.read(&srcNode) &&
+ s.read(&jetClear) &&
+ s.read(&howTo) &&
+ s.read(&res1) &&
+ s.read(&res2) &&
+ s.read(&res3)
+ );
+}
+
+bool GraphBridgeInfo::write(Stream & s) const
+{
+ return
+ ( s.write(dstNode) &&
+ s.write(srcNode) &&
+ s.write(jetClear) &&
+ s.write(howTo) &&
+ s.write(res1) &&
+ s.write(res2) &&
+ s.write(res3)
+ );
+}
+
+//-------------------------------------------------------------------------------------
+// Graph Bridge Data
+
+void GraphBridgeData::init(U16 n0, U16 n1)
+{
+ nodes[0] = n1; // for some reason, these are backwards, which wound up causing
+ nodes[1] = n0; // a problem for the replacement edges. The quick remedy is to
+ jetClear = 0; // change the edge replacement ON LOAD (graphJetting.cc) rather
+ howTo = 0; // than rebuild just to change a non-intuitive ordering here...
+}
+
+bool GraphBridgeData::read(Stream& s)
+{
+ return( s.read(&nodes[0]) &&
+ s.read(&nodes[1]) &&
+ s.read(&jetClear) &&
+ s.read(&howTo)
+ );
+}
+
+bool GraphBridgeData::write(Stream & s) const
+{
+ return( s.write(nodes[0]) &&
+ s.write(nodes[1]) &&
+ s.write(jetClear) &&
+ s.write(howTo)
+ );
+}
+
+BridgeDataList::BridgeDataList()
+{
+ mReplaced[0] = mReplaced[1] = 0;
+ mSaveTotal = 0;
+}
+
+bool BridgeDataList::read(Stream& s)
+{
+ mSaveTotal = 0;
+ if (readVector1(s, *this))
+ {
+ mSaveTotal = size();
+ return true;
+ }
+ return false;
+}
+
+bool BridgeDataList::write(Stream& s) const
+{
+ return writeVector1(s, *this);
+}
+
+// Given array of accumulation numbers for how many edges each node has, add
+// in the bridges as well.
+S32 BridgeDataList::accumEdgeCounts(U16 * edgeCounts)
+{
+ S32 total = 0;
+ mReplaced[0] = mReplaced[1] = 0;
+
+ for (iterator it = begin(); it != end(); it++)
+ {
+ if (!it->isReplacement())
+ {
+ total += 2;
+ for (S32 i = 0; i < 2; i++)
+ edgeCounts[it->nodes[i]]++;
+ }
+ else
+ {
+ mReplaced[it->isUnreachable() != 0]++;
+ }
+ }
+
+ return total;
+}
+
+// Number of non-replacement bridges- basically how many edges it adds
+// to the world (this is just used for memory tracking).
+S32 BridgeDataList::numPositiveBridges() const
+{
+ return (mSaveTotal << 1) - replaced();
+}
+
+S32 BridgeDataList::replaced() const
+{
+ return mReplaced[0] + mReplaced[1];
+}
+
+// Read the old style and convert
+#if 0
+bool BridgeDataList::readOld(Stream& s)
+{
+ BridgeInfoList oldList;
+
+ if (oldList.read(s))
+ {
+ S32 oldSz = oldList.size();
+ AssertFatal((oldSz % 2) == 0, "Old sizes were always even");
+ setSize(oldSz >>= 1);
+
+ // Old format very bloated and stores both ways, which turned out not to have any
+ // differences right now (later systems, maybe...).
+ while (oldSz--)
+ {
+ GraphBridgeInfo& bridgeOld = oldList[oldSz << 1];
+ GraphBridgeData& bridgeNew = (*this)[oldSz];
+
+ bridgeNew.nodes[0] = U16(bridgeOld.srcNode);
+ bridgeNew.nodes[1] = U16(bridgeOld.dstNode);
+ bridgeNew.howTo = bridgeOld.howTo;
+ bridgeNew.jetClear = mapJetHopToU8(bridgeOld.jetClear);
+ }
+ return true;
+ }
+ return false;
+}
+#endif
+
+//-------------------------------------------------------------------------------------
+
+bool GraphVolumeList::read(Stream& s)
+{
+ return readVector1(s, *this) && mathReadVector(mPlanes, s);
+}
+
+bool GraphVolumeList::write(Stream& s) const
+{
+ return writeVector1(s, *this) && mathWriteVector(mPlanes, s);
+}
+
+//-------------------------------------------------------------------------------------
+
+S32 ChuteHints::findNear(const Box3F& box, ChutePtrList& list) const
+{
+ list.clear();
+ return mBSP.getIntersecting(list, box);
+}
+
+// Patch for the query we usually do-
+S32 ChuteHints::findNear(const Point3F& P, S32 xy, S32 z, ChutePtrList& list) const
+{
+ Point3F boxMin(P.x - xy, P.y - xy, P.z - z);
+ Point3F boxMax(P.x + xy, P.y + xy, P.z + z);
+ Box3F theBox(boxMin, boxMax, true);
+ return findNear(theBox, list);
+}
+
+// Create the BSP. makeTree() needs a pointer list, so we must build one.
+void ChuteHints::makeBSP()
+{
+ VectorPtr pointers;
+ pointers.setSize(size());
+ iterator c = begin();
+ for (S32 i = size() - 1; i >= 0; i--)
+ pointers[i] = c++;
+ mBSP.makeTree(pointers);
+}
+
+void ChuteHints::init(const Vector& list)
+{
+ setSize(list.size());
+ Vector::const_iterator p = list.begin();
+ iterator c = begin();
+ for (S32 i = list.size() - 1; i >= 0; i--, p++, c++) {
+ c->x = p->x;
+ c->y = p->y;
+ c->z = p->z;
+ }
+ makeBSP();
+}
+
+bool ChuteHints::read(Stream& s)
+{
+ if (readVector1(s, *this)) {
+ makeBSP();
+ return true;
+ }
+ return false;
+}
+
+bool ChuteHints::write(Stream& s) const
+{
+ return writeVector1(s, *this);
+}
+
+// Given the verticle line of a (potential) jetting hop, see if it is one of the
+// chute types. Build a box up as far as LOS goes to do this. We're at the
+// top if the highest found chute hint is level with the given top point.
+ChuteHints::Info ChuteHints::info(Point3F bottom, const Point3F& top)
+{
+ const F32 boxR = 1.4;
+
+ if (mFabs(top.z - bottom.z) > 4.0 * /*Take out for moment-*/1e9) {
+ // Do these queries as a first pass to avoid LOS calls-
+ if (findNear(bottom, boxR, 2.0, mQuery) > 0) {
+ if (findNear(top, boxR, 2.0, mQuery) > 0) {
+
+ bottom.z += 1.0;
+
+ const U32 mask = (InteriorObjectType|TerrainObjectType);
+ const F32 maxH = 500.0;
+ Point3F upper(bottom.x, bottom.y, bottom.z + maxH);
+ RayInfo coll;
+
+ if (gServerContainer.castRay(bottom, upper, mask, &coll))
+ upper.z = coll.point.z;
+
+ Point3F boxMin(bottom.x - boxR, bottom.y - boxR, bottom.z - 2.0);
+ Point3F boxMax(upper.x + boxR, upper.y + boxR, upper.z);
+ Box3F theBox(boxMin, boxMax, true);
+
+ // Well, this should always return something at this point..
+ if (S32 numHints = findNear(theBox, mQuery)) {
+ // Find max-
+ F32 maxZ = -1e12, z;
+ for (S32 i = 0; i < numHints; i++)
+ if ((z = mQuery[i]->z) > maxZ)
+ maxZ = z;
+
+ if (mFabs(maxZ - top.z) < 2.0)
+ return ChuteTop;
+ else
+ return ChuteMid;
+ }
+ }
+ }
+ }
+ return NotAChute;
+}
+
+//-------------------------------------------------------------------------------------
+
+bool PathXRefEntry::read(Stream& s)
+{
+ U32 numEntries, bitWidth;
+ if (s.read(&numEntries) && s.read(&bitWidth)) {
+ setDims(numEntries, bitWidth);
+ return s.read(numBytes(), dataPtr());
+ }
+ return false;
+}
+
+bool PathXRefEntry::write(Stream& s) const
+{
+ if (s.write(numEntries()) && s.write(bitWidth()))
+ return s.write(numBytes(), dataPtr());
+ return false;
+}
+
+PathXRefTable::~PathXRefTable()
+{
+ clear();
+}
+
+bool PathXRefTable::read(Stream& s)
+{
+ return constructVector(s, *this);
+}
+
+bool PathXRefTable::write(Stream& s) const
+{
+ return writeVector1(s, *this);
+}
+
+void PathXRefTable::setSize(S32 size)
+{
+ clear();
+ setSizeAndConstruct(*this, size);
+}
+
+void PathXRefTable::clear()
+{
+ destructAndClear(*this);
+}
+
+//-------------------------------------------------------------------------------------
+
+bool LOSXRefTable::read(Stream& s)
+{
+ U32 numEntries, bitWidth;
+ if (s.read(&numEntries) && s.read(&bitWidth)) {
+ setDims(numEntries, bitWidth);
+ return s.read(numBytes(), dataPtr());
+ }
+ return false;
+}
+
+bool LOSXRefTable::write(Stream& s) const
+{
+ if (s.write(numEntries()) && s.write(bitWidth()))
+ return s.write(numBytes(), dataPtr());
+ return false;
+}
+
+U32 LOSXRefTable::value(S32 i1, S32 i2) const
+{
+ if (i1 == i2)
+ return FullLOS;
+ else
+ {
+ U32 lookup = triangularTableIndex(i1, i2);
+ AssertFatal(lookup < numEntries(), "LOSXRefTable::value() read beyond end");
+ return getU17(lookup);
+ }
+}
+
+void LOSXRefTable::setEntry(S32 i1, S32 i2, U32 val)
+{
+ setU17(triangularTableIndex(i1, i2), val);
+}
+
+bool LOSXRefTable::valid(S32 numNodes) const
+{
+ S32 expectedTableSize = triangularTableIndex(numNodes + 1, 0);
+ return (numEntries() == expectedTableSize);
+}
+
+// Reset, free up memory.
+void LOSXRefTable::clear()
+{
+ setDims(0, 0);
+}
+
+//-------------------------------------------------------------------------------------
+// LOS Hash Table
+
+LOSHashTable::LOSHashTable()
+{
+ mTable = NULL;
+ mNumNodes = 0;
+ mRes0 = mRes1 = 0;
+}
+
+LOSHashTable::~LOSHashTable()
+{
+ delete [] mTable;
+}
+
+bool LOSHashTable::valid(S32 numNodes) const
+{
+ return (numNodes == mNumNodes);
+}
+
+// Do the node-to-node lookup.
+U32 LOSHashTable::value(S32 i1, S32 i2) const
+{
+ if (i1 == i2)
+ return FullLOS;
+ else
+ {
+ U16 from, to, seg;
+
+ if (i1 < i2) { // Lookup goes UP
+ from = i1;
+ seg = ((to = i2) >> SegShift);
+ }
+ else {
+ from = i2;
+ seg = ((to = i1) >> SegShift);
+ }
+
+ Key key(from, seg);
+ U32 hash = calcHash(key);
+ AssertFatal(hash < mTabSz, "LOSHashTable: bad hash value in lookup");
+
+ // Do the bucket "walk" -
+ if (S32 bucketSize = mTable[hash + 1] - mTable[hash])
+ {
+ const Segment * seg = &mSegments[mTable[hash]];
+ while (--bucketSize >= 0)
+ {
+ if (seg->mKey.mCompare == key.mCompare)
+ return seg->value(to & SegMask);
+ seg++;
+ }
+ }
+
+ // Nothing found, so it's hidden
+ return Hidden;
+ }
+}
+
+U32 LOSHashTable::mTabSz = 0;
+
+// This needs to be improved (hence we sort after loading as well...)
+U32 LOSHashTable::calcHash(Key key)
+{
+ U32 n = key.mKey.mNode;
+ U32 s = key.mKey.mSeg;
+ U32 val = (n << 8 + (s & 7)) ^ (n + (n >> 1)) ^ ((s << 2) | (s & 1));
+ return val % mTabSz;
+}
+
+// Look for size that is relatively prime to first few primes (uh, can't hurt...)
+U32 LOSHashTable::calcTabSize()
+{
+ static const U32 nPrimes = 12;
+ static U32 primes[nPrimes] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};
+
+ for (U32 nSegs = mSegments.size(); /* until we find one */ ; nSegs++ )
+ {
+ bool done = true;
+
+ for (U32 i = 0; i < nPrimes; i++)
+ if (!(nSegs % primes[i]))
+ done = false;
+
+ if (done) {
+ mTabSz = nSegs;
+ break;
+ }
+ }
+ return mTabSz;
+}
+
+S32 QSORT_CALLBACK LOSHashTable::cmpHashes(const void * a,const void * b)
+{
+ U32 A = calcHash( ((Segment*)a)->mKey );
+ U32 B = calcHash( ((Segment*)b)->mKey );
+ AssertFatal(A < mTabSz && B < mTabSz, "LOSHashTable: hash has bad result");
+ return (A < B ? -1 : (A > B ? 1 : 0));
+}
+
+// This assumes the table size has been calculated-
+void LOSHashTable::sortByHashVal()
+{
+ dQsort((void* )(mSegments.address()), mSegments.size(), sizeof(Segment), cmpHashes);
+}
+
+LOSHashTable::Segment::Segment(Key key, const U32 losInfo[SegSize])
+{
+ mKey = key;
+ dMemset(mLOS, 0, sizeof(mLOS));
+ for (U32 i = 0; i < SegAlloc; i++)
+ for (U32 j = 0; j < 4; j++)
+ mLOS[i] |= (losInfo[i * 4 + j] << (j * 2));
+}
+
+// Build the new better table from the old style. Return memory usage (for measuring,
+// but it's also conceivable that we could try different seg sizes dynamically).
+U32 LOSHashTable::convertTable(const LOSXRefTable& XRefTable, S32 numNodes)
+{
+ S32 numUniform = 0;
+ S32 numSameSeg = 0;
+
+ mNumNodes = numNodes;
+ mSegments.clear();
+
+ for (S32 srcNode = 0; srcNode < numNodes - 1; srcNode++)
+ {
+ // Check all other segments (including ours if we're not the last in it). Note
+ // inclusive compare to catch last segment!
+ for (S32 seg = (srcNode + 1 >> SegShift); seg <= (numNodes >> SegShift); seg++)
+ {
+ S32 begin = (seg << SegShift);
+ S32 end = begin + SegSize;
+
+ if (end > numNodes)
+ end = numNodes;
+
+ // If in our own segment, only check node numbers higher...
+ bool sameSeg = (seg == (srcNode >> SegShift));
+ if (sameSeg)
+ {
+ begin = srcNode + 1;
+ end = ((srcNode + SegSize) & ~SegMask);
+ if (end > numNodes)
+ end = numNodes;
+ }
+
+ // Examine all the nodes in the segment for non-hidden
+ S32 losCount = 0;
+ U32 losInfo[SegSize];
+ dMemset(losInfo, 0, sizeof(losInfo));
+ for (U32 dstNode = begin; dstNode < end; dstNode++)
+ {
+ U32 info = XRefTable.value(U16(srcNode), U16(dstNode));
+ losInfo[dstNode & SegMask] = info;
+ losCount += (info != 0);
+ }
+
+ // Add the segment if something was seen.
+ if (losCount)
+ {
+ Key key(srcNode, seg);
+ Segment segment(key, losInfo);
+ mSegments.push_back(segment);
+
+ // See if they're all the same
+ S32 a;
+ for (a = 1; a < SegSize; a++)
+ if (losInfo[a] != losInfo[0])
+ break;
+ numUniform += (a == SegSize);
+ numSameSeg += sameSeg;
+ }
+ }
+ }
+
+ Con::printf("Found %d Uniform entries", numUniform);
+ Con::printf("Found %d SameSeg entries", numSameSeg);
+
+ // Sorts segment vector-
+ calcTabSize();
+ sortByHashVal();
+
+ // Set up mTable
+ U32 tabMemUse = makeTheTable();
+
+ return (mSegments.memSize() + tabMemUse);
+}
+
+// Given that we've got a vector of segments sorted by hash value, make mTable.
+// Assumes table size has already been calculated.
+// Return memory size of table.
+U32 LOSHashTable::makeTheTable()
+{
+ U32 deepest = 0, depth;
+
+ // Alloc and flag as uninitialized. Extra 1 needed for bucket size math at end.
+ delete [] mTable;
+ mTable = new IndexType[ mTabSz + 1 ];
+ U32 memSize = (mTabSz + 1) * sizeof(IndexType);
+ dMemset(mTable, 0xFF, memSize);
+
+ // Set up hash pointers-
+ for (U32 seg = 0; seg < mSegments.size(); seg++)
+ {
+ U32 hash = calcHash(mSegments[seg].mKey);
+ if (mTable[hash] == IndexType(-1))
+ mTable[hash] = seg;
+ else if ((depth = seg - mTable[hash]) > deepest)
+ deepest = depth;
+ }
+
+ // Fill in empty elements so bucket size math works
+ AssertFatal(mTable[mTabSz] == IndexType(-1), "LOS Hash table overwritten at end");
+ U32 fillValue = mSegments.size();
+ U32 numEmpty = 0, numFilled = 0;
+ for (U32 H = mTabSz; H; H--)
+ {
+ if (mTable[H] == IndexType(-1)) {
+ mTable[H] = fillValue;
+ numEmpty++;
+ }
+ else {
+ fillValue = mTable[H];
+ numFilled++;
+ }
+ }
+
+ Con::printf("Table size = %d, Segments = %d, Filled = %d, Deepest = %d",
+ mTabSz, mSegments.size(), numFilled, deepest );
+
+ return memSize;
+}
+
+bool LOSHashTable::Segment::read(Stream& s)
+{
+ if (s.read(&mKey.mKey.mNode) && s.read(&mKey.mKey.mSeg))
+ return s.read(SegAlloc, mLOS);
+ return false;
+}
+
+bool LOSHashTable::Segment::write(Stream& s) const
+{
+ if (s.write(mKey.mKey.mNode) && s.write(mKey.mKey.mSeg))
+ return s.write(SegAlloc, mLOS);
+ return false;
+}
+
+void LOSHashTable::clear()
+{
+ mNumNodes = 0;
+ delete [] mTable;
+ mTable = NULL;
+ mSegments.clear();
+ mSegments.compact();
+}
+
+bool LOSHashTable::read(Stream& s)
+{
+ if (s.read(&mNumNodes))
+ if (readVector1(s, mSegments))
+ if (s.read(&mRes0) && s.read(&mRes1))
+ {
+ calcTabSize();
+ sortByHashVal(); // For now we sort Segments on load since we're still
+ makeTheTable(); // tweaking the hash function
+ return true;
+ }
+ return false;
+}
+
+bool LOSHashTable::write(Stream& s) const
+{
+ if (s.write(mNumNodes))
+ if (writeVector1(s, mSegments))
+ if (s.write(mRes0) && s.write(mRes1))
+ return true;
+ return false;
+}
+
+//-------------------------------------------------------------------------------------
+// Header info for the Terrain Data.
+
+TerrainGraphInfo::TerrainGraphInfo()
+{
+ haveGraph = false;
+ dMemset(indOffs, 0xff, sizeof(indOffs));
+ nodeCount = numShadows = -1;
+ originWorld.set(-1,-1,-1);
+ originGrid.set(-1,-1);
+ gridDimensions = gridTopRight = originGrid;
+}
+
+bool TerrainGraphInfo::read(Stream & s)
+{
+ S32 version;
+
+ haveGraph = false;
+
+ if( !s.read(&version) || version != smVersion )
+ return false;
+
+ bool Ok = s.read( & nodeCount) &&
+ mathRead(s, & originWorld) &&
+ mathRead(s, & originGrid) &&
+ mathRead(s, & gridDimensions) &&
+ mathRead(s, & gridTopRight) &&
+ readVector2(s, navigableFlags) &&
+ readVector2(s, neighborFlags) &&
+ readVector2(s, shadowHeights) &&
+ readVector2(s, roamRadii) &&
+ consolidated.read(s)
+ ;
+
+ if( Ok )
+ doFinalDataSetup();
+
+ return( haveGraph = Ok );
+}
+
+bool TerrainGraphInfo::write(Stream & s) const
+{
+ bool Ok = s.write( smVersion ) &&
+ s.write( nodeCount) &&
+ mathWrite(s, originWorld) &&
+ mathWrite(s, originGrid) &&
+ mathWrite(s, gridDimensions) &&
+ mathWrite(s, gridTopRight) &&
+ writeVector2(s, navigableFlags) &&
+ writeVector2(s, neighborFlags) &&
+ writeVector2(s, shadowHeights) &&
+ writeVector2(s, roamRadii) &&
+ consolidated.write(s)
+ ;
+
+ return Ok;
+}
+
+//------------------------------------------------------------------------------
+// Terrain graph info utility functions.
+
+
+// Get the index of the given grid location if it's in our region, else -1.
+// This doesn't check for obstruction though.
+S32 TerrainGraphInfo::posToIndex(Point2I p) const
+{
+ S32 idx = -1;
+
+ if( haveGraph )
+ {
+ p -= originGrid;
+
+ if( validArrayIndex(p.x, gridDimensions.x) )
+ if( validArrayIndex(p.y, gridDimensions.y) )
+ idx = p.y * gridDimensions.x + p.x;
+
+ AssertFatal( idx >= -1 && idx < nodeCount, "TerrainGraphInfo::posToIndex()" );
+ }
+
+ return idx;
+}
+
+// Fetch the location at the given grid location - a straight mapping
+// to world space, whether it's a valid node or not. But then return
+// if it's a valid node.
+bool TerrainGraphInfo::posToLoc(Point3F& loc, const Point2I& p)
+{
+ S32 index = posToIndex(p);
+ bool isValid = (index >= 0 && !obstructed(index));
+
+ loc.x = F32(p.x << gNavGlobs.mSquareShift);
+ loc.y = F32(p.y << gNavGlobs.mSquareShift);
+ loc.z = 0.0f; // need actual position at some point.
+ loc += originWorld;
+
+ return isValid;
+}
+
+
+
+// Map the given world coordinate into our graph grid.
+// Sort of a worldToGrid() routine.
+S32 TerrainGraphInfo::locToIndex(Point3F loc) const
+{
+ loc += gNavGlobs.mHalfSquare;
+ loc -= originWorld;
+ loc *= gNavGlobs.mInverseWidth;
+ return posToIndex( Point2I( mFloor(loc.x), mFloor(loc.y)) );
+}
+
+// Get the grid location. Returning a pointer since we may
+// handle bad indices at some point with a NULL return value.
+Point3F * TerrainGraphInfo::indexToLoc(Point3F & loc, S32 index)
+{
+ Point2I & pos = indexToPos(index);
+
+ loc.x = F32(pos.x << gNavGlobs.mSquareShift);
+ loc.y = F32(pos.y << gNavGlobs.mSquareShift);
+ loc.z = 0.0f;
+
+ loc += originWorld;
+
+ return & loc;
+}
+
+bool TerrainGraphInfo::obstructed(const Point3F& loc) const
+{
+ S32 index = locToIndex(loc);
+ return (index < 0) || obstructed(index);
+}
+
+Point2I & TerrainGraphInfo::indexToPos(S32 index) const
+{
+ static Point2I sPoint;
+ sPoint.x = originGrid.x + index % gridDimensions.x;
+ sPoint.y = originGrid.y + index / gridDimensions.x;
+ return sPoint;
+}
+
+
+S32 TerrainGraphInfo::locToIndexAndSphere(SphereF & sphereOut, const Point3F & loc)
+{
+ if( roamRadii.size() )
+ {
+ S32 idx = locToIndex(loc);
+ if( validArrayIndex(idx, roamRadii.size()) && !obstructed(idx) )
+ {
+ indexToLoc(sphereOut.center, idx);
+ sphereOut.radius = fixedToFloat(roamRadii[ idx ]);
+ if( sphereOut.isContained(loc) )
+ return idx;
+ }
+ }
+ return -1;
+}
+
+// This function assumes that the grid and navigable flags have been set up.
+//
+void TerrainGraphInfo::computeRoamRadii()
+{
+ Vector spiral;
+ Vector dists;
+
+ S32 radius = getMin( getMax(gridDimensions.x, gridDimensions.y), 20);
+ S32 scount = getGridSpiral(spiral, dists, radius);
+ S32 n, s;
+
+ // DMMNOTPRESENT
+ bool deadlyLiquids = false;
+// bool deadlyLiquids = (TerrainRender::mLiquidType >= 4);
+
+ roamRadii.setSize(nodeCount);
+
+ for (n = 0; n < nodeCount; n++)
+ {
+ F32 roamDist = 0.0;
+ F32 extraAvoid = 0.0;
+
+ if (!obstructed(n))
+ {
+ Point2I basePos = indexToPos(n);
+ U8 saveType = squareType(n);
+
+ for (s = 0; s < scount; s++)
+ {
+ Point2I roamPos = basePos + spiral[s];
+ S32 roamInd = posToIndex( roamPos );
+
+ if (!validArrayIndex(roamInd, nodeCount) || obstructed(roamInd))
+ break;
+
+ if (steep(roamInd))
+ {
+ extraAvoid = 0.5;
+ break;
+ }
+
+ if (saveType != squareType(roamInd))
+ {
+ // If we run into deadly liquids, then cut our roam distance more-
+ if (deadlyLiquids && submerged(roamInd))
+ extraAvoid = 2.0;
+ break;
+ }
+ }
+
+ if( s == scount ) // no hit - can probably just use largest dist
+ roamDist = dists[s - 1];
+ else
+ roamDist = (dists[s] - 1.4);
+ }
+
+ F32 roamGridDist = getMax(roamDist - extraAvoid, 0.0f);
+
+ roamRadii[n] = floatToFixed(roamGridDist * gNavGlobs.mSquareWidth);
+ }
+}
+
+// The following are used to find where to 'inbounds' when out of bounds. That is,
+// a location on graph border to (find a closest node to) go to.
+void TerrainGraphInfo::setSideSegs()
+{
+ Point2I corners[4] = {
+ Point2I(originGrid.x + 0, originGrid.y + 0 ),
+ Point2I(originGrid.x + gridDimensions.x - 1, originGrid.y + 0 ),
+ Point2I(originGrid.x + gridDimensions.x - 1, originGrid.y + gridDimensions.y - 1 ),
+ Point2I(originGrid.x + 0, originGrid.y + gridDimensions.y - 1 )
+ };
+
+ bool Ok[4];
+ S32 numOk, p1, p2;
+ Point3F loc1, loc2;
+
+ for( numOk = p1 = 0; p1 < 4; p1++ )
+ {
+ p2 = (p1 + 1) % 4;
+ Ok[p1] = posToLoc(loc1, corners[p1] );
+ Ok[p2] = posToLoc(loc2, corners[p2] );
+ numOk += Ok[p1];
+ boundarySegs[p1] = LineSegment(loc1, loc2);
+ }
+
+ // We need to have warnings in the system if the boundaries don't have nodes.
+ if( numOk < 4 )
+ Con::printf( "Graph Warning! Only %d corners of grid are navigable!", numOk );
+}
+
+void TerrainGraphInfo::doFinalDataSetup()
+{
+ // Set up grid area.
+ gridArea.point = originGrid;
+ gridArea.extent = gridDimensions;
+
+ setSideSegs();
+
+ // Offsets for finding neighboring squares when working with indices.
+ for (S32 dir = 0; dir < 8; dir++ ) {
+ Point2I off = gridOffs[dir];
+ indOffs[dir] = off.y * gridDimensions.x + off.x;
+ }
+}
+
+Point3F TerrainGraphInfo::whereToInbound(const Point3F & loc)
+{
+ // Assuming that passed parameter has already filtered with inGraphArea().
+ AssertFatal(!inGraphArea(loc), "whereToInbound() only meant for out of bound locs");
+ AssertFatal(haveGraph, "whereToInbound() called without a graph set up");
+
+ S32 solution = -1;
+ F32 bestDist = 1e9;
+
+ for( S32 i = 0; i < 4; i++ )
+ {
+ F32 d = boundarySegs[i].distance(loc);
+
+ if( d < bestDist )
+ bestDist = d, solution = i;
+ }
+
+ return boundarySegs[solution].solution();
+}
+
+//
+// See if you can go straight across terrain between the given points. Return
+// what percentage of the way the system believes can be achieved.
+//
+// ==> Need to handle out of bounds checks properly. Basically clip to mission
+// area and then enter this loop. If not intersecting, we probably return
+// 100%, or maybe the check for outside should happen earlier and
+// the bot would already know they are free to roam.
+//
+F32 TerrainGraphInfo::checkOpenTerrain(const Point3F & from, Point3F & to)
+{
+ SphereF sphere;
+ S32 prevSphere = locToIndexAndSphere(sphere, from);
+
+ if( prevSphere < 0 ) {
+ to = from;
+ return 0.0f;
+ }
+
+ LineStepper linearPath(from, to);
+
+ while(true)
+ {
+ // Find outbound intersection on sphere. (Note WE tell linearPath when to step)
+ F32 dist = linearPath.getOutboundIntersection(sphere);
+
+ // make it all the way?
+ if( dist > linearPath.remainingDist() )
+ return 1.0;
+
+ // no intersection. only can happen theoretically due to rounding dullness.
+ if( dist < 0.0f )
+ break;
+
+ // See if we're in a new circle (after advancing the stepper).
+ S32 nextSphere = locToIndexAndSphere(sphere, linearPath.advanceToSolution());
+
+ // Keep going while we're inside a sphere (and it's not the same one!)
+ if( nextSphere >= 0 && nextSphere != prevSphere )
+ prevSphere = nextSphere;
+ else
+ break;
+ }
+
+ to = linearPath.getSolution();
+ return linearPath.distSoFar() / linearPath.totalDist();
+}
+
+//-------------------------------------------------------------------------------------
+
+// Return if the given grid position is on the side of the area, and if so we return a
+// bitset of all the valid neighbors it _could_ have. See consolidateData() for use.
+U32 TerrainGraphInfo::onSideOfArea(S32 index)
+{
+ Point2I p = indexToPos(index);
+ U32 retVal = 0xff;
+
+ // is it (well) inside?
+ if(p.x>originGrid.x && p.xoriginGrid.y && p.y